import sequtils, algorithm
import imguin/[cimgui, glfw_opengl]
import ../../utils/[appImGui, colors]

type 
    DualListSelectionWidget*[T] = ref object of RootObj
        items*: array[2, seq[T]]
        selection: array[2, ptr ImGuiSelectionBasicStorage]
        display: proc(item: T): string
        compare: proc(x, y: T): int
        tooltip: proc(item: T): string

proc defaultDisplay[T](item: T): string = 
    return $item

proc DualListSelection*[T](items: seq[T], display: proc(item: T): string = defaultDisplay, compare: proc(x, y: T): int,  tooltip: proc(item: T): string = nil): DualListSelectionWidget[T] = 
    result = new DualListSelectionWidget[T]
    result.items[0] = items
    result.items[1] = @[]
    result.selection[0] = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
    result.selection[1] = ImGuiSelectionBasicStorage_ImGuiSelectionBasicStorage()
    result.display = display
    result.compare = compare
    result.tooltip = tooltip

proc moveAll[T](component: DualListSelectionWidget[T], src, dst: int) = 
    for m in component.items[src]: 
        component.items[dst].add(m)
    component.items[dst].sort(component.compare)
    component.items[src].setLen(0)

    ImGuiSelectionBasicStorage_Swap(component.selection[src], component.selection[dst])
    ImGuiSelectionBasicStorage_Clear(component.selection[src])

proc moveSelection[T](component: DualListSelectionWidget[T], src, dst: int) = 
    var keep: seq[T]
    for i in 0 ..< component.items[src].len(): 
        let item = component.items[src][i]
        if not component.selection[src].ImGuiSelectionBasicStorage_Contains(cast[ImGuiID](i)):
            keep.add(item)
            continue
        component.items[dst].add(item)
    component.items[dst].sort(component.compare)
    component.items[src] = keep

    ImGuiSelectionBasicStorage_Swap(component.selection[src], component.selection[dst])
    ImGuiSelectionBasicStorage_Clear(component.selection[src])

proc reset*[T](component: DualListSelectionWidget[T]) = 
    component.moveAll(1, 0)

proc draw*[T](component: DualListSelectionWidget[T]) = 

    if igBeginTable("split", 3, ImGuiTableFlags_None.int32, vec2(0.0f, 0.0f), 0.0f): 

        igTableSetupColumn("", ImGuiTableColumnFlags_WidthStretch.int32, 0.0f, 0) # Left
        igTableSetupColumn("", ImGuiTableColumnFlags_WidthFixed.int32, 0.0f, 0)   # Buttons
        igTableSetupColumn("", ImGuiTableColumnFlags_WidthStretch.int32, 0.0f, 0) # Right
        igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f)

        var containerHeight: float

        # Left selection column
        igTableSetColumnIndex(0) 
            
        var modules = component.items[0]
        var selection = component.selection[0]

        # Header
        var text = "Available"
        var textSize: ImVec2
        igCalcTextSize(addr textSize, text.cstring, nil, false, 0.0f)
        igSetCursorPosX(igGetCursorPosX() + (igGetColumnWidth(0) - textSize.x) * 0.5f)
        igTextColored(GRAY, text.cstring)
        
        # Set the size of selection box to fit all modules
        igSetNextWindowContentSize(vec2(0.0f, float(modules.len()) * igGetTextLineHeightWithSpacing()))
        igSetNextWindowSizeConstraints(vec2(0.0f, igGetTextLineHeightWithSpacing() * 10.0f), vec2(igGET_FLT_MAX(), igGET_FLT_MAX()), nil, nil) # Set minimum container size
        if igBeginChild_Str("0", vec2(0.0f, -1.0f), ImGuiChildFlags_FrameStyle.int32 or ImGuiChildFlags_ResizeY.int32, ImGuiWindowFlags_None.int32):
            containerHeight = igGetWindowHeight() 

            var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_None.int32, selection[].Size, int32(modules.len())) 
            ImGuiSelectionBasicStorage_ApplyRequests(selection, multiSelectIO)
            
            for row in 0 ..< modules.len().int32: 
                var isSelected = ImGuiSelectionBasicStorage_Contains(selection, cast[ImGuiID](row))
                igSetNextItemSelectionUserData(row)
                discard igSelectable_Bool(component.display(modules[row]).cstring, isSelected, ImGuiSelectableFlags_AllowDoubleClick.int32, vec2(0.0f, 0.0f))
                
                if not component.tooltip.isNil():
                    setTooltip(component.tooltip(modules[row]))

                # Move on Enter and double-click
                if igIsItemFocused(): 
                    if igIsKeyPressed_Bool(ImGuiKey_Enter, false) or igIsKeyPressed_Bool(ImGuiKey_KeypadEnter, false):
                        component.moveSelection(0, 1)
                    if igIsMouseDoubleClicked_Nil(ImGui_MouseButton_Left.int32):
                        component.moveSelection(0, 1)

            multiSelectIO = igEndMultiSelect()
            ImGuiSelectionBasicStorage_ApplyRequests(selection, multiSelectIO)

        igEndChild()

        # Buttons column 
        igTableSetColumnIndex(1)
        igNewLine() 

        let buttonSize = vec2(igGetFrameHeight(), igGetFrameHeight())
        if igButton(">>", buttonSize): 
            component.moveAll(0, 1)
        if igButton(">", buttonSize): 
            component.moveSelection(0, 1)
        if igButton("<", buttonSize): 
            component.moveSelection(1, 0)
        if igButton("<<", buttonSize): 
            component.moveAll(1, 0)

        # Right selection column
        igTableSetColumnIndex(2) 
            
        modules = component.items[1]
        selection = component.selection[1]

        # Header
        text = "Selected"
        igCalcTextSize(addr textSize, text.cstring, nil, false, 0.0f)
        igSetCursorPosX(igGetCursorPosX() + (igGetColumnWidth(2) - textSize.x) * 0.5f)
        igTextColored(GRAY, text.cstring)
        
        # Set the size of selection box to fit all modules
        igSetNextWindowContentSize(vec2(0.0f, float(modules.len()) * igGetTextLineHeightWithSpacing()))
        if igBeginChild_Str("1", vec2(0.0f, containerHeight), ImGuiChildFlags_FrameStyle.int32, ImGuiWindowFlags_None.int32): 

            var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_None.int32, selection[].Size, int32(modules.len())) 
            ImGuiSelectionBasicStorage_ApplyRequests(selection, multiSelectIO)

            for row in 0 ..< modules.len().int32:         
                var isSelected = ImGuiSelectionBasicStorage_Contains(selection, cast[ImGuiID](row))
                igSetNextItemSelectionUserData(row)
                discard igSelectable_Bool(component.display(modules[row]).cstring, isSelected, ImGuiSelectableFlags_AllowDoubleClick.int32, vec2(0.0f, 0.0f))

                if not component.tooltip.isNil():
                    setTooltip(component.tooltip(modules[row]))

                # Move on Enter and double-click
                if igIsItemFocused(): 
                    if igIsKeyPressed_Bool(ImGuiKey_Enter, false) or igIsKeyPressed_Bool(ImGuiKey_KeypadEnter, false):
                        component.moveSelection(1, 0)
                    if igIsMouseDoubleClicked_Nil(ImGui_MouseButton_Left.int32):
                        component.moveSelection(1, 0)

            multiSelectIO = igEndMultiSelect()
            ImGuiSelectionBasicStorage_ApplyRequests(selection, multiSelectIO)

        igEndChild()

        igEndTable()