-- Interface powered item editor.
local gui = require 'gui'
local dialog = require 'gui.dialogs'
local args={...}
function getTargetFromScreens()
    local my_trg
    if dfhack.gui.getCurFocus() == 'item' then
        my_trg=dfhack.gui.getCurViewscreen().item
    elseif dfhack.gui.getCurFocus() == 'joblist' then
        local t_screen=dfhack.gui.getCurViewscreen()
        my_trg=t_screen.jobs[t_screen.cursor_pos]
    elseif dfhack.gui.getCurFocus() == 'createquota' then
        local t_screen=dfhack.gui.getCurViewscreen()
        my_trg=t_screen.orders[t_screen.sel_idx]
    elseif dfhack.gui.getCurFocus() == 'dwarfmode/LookAround/Flow' then
        local t_look=df.global.ui_look_list.items[df.global.ui_look_cursor]
        my_trg=t_look.flow

    elseif dfhack.gui.getSelectedUnit(true) then
        my_trg=dfhack.gui.getSelectedUnit(true)
    elseif dfhack.gui.getSelectedItem(true) then
        my_trg=dfhack.gui.getSelectedItem(true)
    elseif dfhack.gui.getSelectedJob(true) then
        my_trg=dfhack.gui.getSelectedJob(true)
    else
        qerror("No valid target found")
    end
    return my_trg
end


local MODE_BROWSE=0
local MODE_EDIT=1
GmEditorUi = defclass(GmEditorUi, gui.FramedScreen)
GmEditorUi.ATTRS={
    frame_style = gui.GREY_LINE_FRAME,
    frame_title = "GameMaster's editor",
	}
function GmEditorUi:init(args)
    self.stack={}
    self.item_count=0
    self.mode=MODE_BROWSE
    self.keys={}
    self:pushTarget(args.target)
    
    return self
end
function GmEditorUi:find(test)
    local trg=self:currentTarget() 
    if trg.target and trg.target._kind and trg.target._kind=="container" then
        if test== nil then
            dialog.showInputPrompt("Test function","Input function that tests(k,v as argument):",COLOR_WHITE,"",dfhack.curry(self.find,self))
            return
        end
        local e,what=load("return function(k,v) return "..test.." end")
        if e==nil then
            dialog.showMessage("Error!","function failed to compile\n"..what,COLOR_RED)
        end
        for k,v in pairs(trg.target) do
            if e()(k,v)==true then
                self:pushTarget(v)
                return
            end
        end
    end
end
function GmEditorUi:insertNew(typename)
    local tp=typename
    if typename== nil then
        dialog.showInputPrompt("Class type","Input class type:",COLOR_WHITE,"",dfhack.curry(self.insertNew,self))
        return
    end
    local ntype=df[tp]
    if ntype== nil then
        dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_RED)
        return
    end
    
    local trg=self:currentTarget() 
    if trg.target and trg.target._kind and trg.target._kind=="container" then
        local thing=ntype:new()
        dfhack.call_with_finalizer(1,false,df.delete,thing,trg.target.insert,trg.target,'#',thing)
        
    end
end
function GmEditorUi:deleteSelected()
    local trg=self:currentTarget()
    if trg.target and trg.target._kind and trg.target._kind=="container" then
        trg.target:erase(trg.keys[trg.selected])
    end
end
function GmEditorUi:currentTarget()
    return self.stack[#self.stack]
end
function GmEditorUi:changeSelected(delta)
    local trg=self:currentTarget()
    if trg.item_count <= 1 then return end
    trg.selected = 1 + (trg.selected + delta - 1) % trg.item_count
end
function GmEditorUi:editSelected()
    local trg=self:currentTarget()
    if trg.target and trg.target._kind and trg.target._kind=="bitfield" then
        trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]]
    else
        --print(type(trg.target[trg.keys[trg.selected]]),trg.target[trg.keys[trg.selected]]._kind or "")
        local trg_type=type(trg.target[trg.keys[trg.selected]])
        if trg_type=='number' or trg_type=='string' then --ugly TODO: add metatable get selected
            self.mode=MODE_EDIT
            self.input=tostring(trg.target[trg.keys[trg.selected]])
        elseif trg_type=='boolean' then
            trg.target[trg.keys[trg.selected]]= not trg.target[trg.keys[trg.selected]]
        elseif trg_type=='userdata' then
            self:pushTarget(trg.target[trg.keys[trg.selected]])
            --local screen = mkinstance(gui.FramedScreen,GmEditorUi):init(trg.target[trg.keys[trg.selected]]) -- does not work
            --screen:show()
        else
            print("Unknow type:"..trg_type)
            print("Subtype:"..tostring(trg.target[trg.keys[trg.selected]]._kind))
        end
    end
end
function GmEditorUi:cancelEdit()
    self.mode=MODE_BROWSE
    self.input=""
end
function GmEditorUi:commitEdit()
    local trg=self:currentTarget()
    self.mode=MODE_BROWSE
    if type(trg.target[trg.keys[trg.selected]])=='number' then
        trg.target[trg.keys[trg.selected]]=tonumber(self.input)
    elseif type(trg.target[trg.keys[trg.selected]])=='string' then
        trg.target[trg.keys[trg.selected]]=self.input
    end
end
function GmEditorUi:onRenderBody( dc)
    local trg=self:currentTarget()
    dc:seek(2,1):string(tostring(trg.target), COLOR_RED)
    local offset=2
    local page_offset=0
    local current_item=1
    local t_col
    local width,height=self:getWindowSize()
    local window_height=height-offset-2
    local cursor_window=math.floor(trg.selected / window_height)
    if  cursor_window>0 then
        page_offset=cursor_window*window_height-1
    end
    for k,v in pairs(trg.target) do
        
        if current_item==trg.selected then
            t_col=COLOR_LIGHTGREEN
        else
            t_col=COLOR_GRAY
        end
        
        if current_item-page_offset > 0 then
            local y_pos=current_item-page_offset+offset
            dc:seek(2,y_pos):string(tostring(k),t_col)
            
            if self.mode==MODE_EDIT and current_item==trg.selected then
                dc:seek(20,y_pos):string(self.input..'_',COLOR_GREEN)
            else
                dc:seek(20,y_pos):string(tostring(v),t_col)
            end
            if y_pos+3>height then
                break
            end
        end
        current_item=current_item+1
        
    end
end
 function GmEditorUi:onInput(keys)
    if self.mode==MODE_BROWSE then
        if keys.LEAVESCREEN  then
            self:popTarget()
        elseif keys.CURSOR_UP then
            self:changeSelected(-1)
        elseif keys.CURSOR_DOWN then
            self:changeSelected(1)
        elseif keys.CURSOR_UP_FAST then
            self:changeSelected(-10)
        elseif keys.CURSOR_DOWN_FAST then
            self:changeSelected(10)
        elseif keys.SELECT then
            self:editSelected()
        elseif keys.CUSTOM_ALT_F then
            self:find()
        elseif keys.CUSTOM_ALT_E then
            --self:specialEditor()
        elseif keys.CUSTOM_ALT_I then --insert
            self:insertNew()
        elseif keys.CUSTOM_ALT_D then --delete
            self:deleteSelected()
        end
    elseif self.mode==MODE_EDIT then
        if keys.LEAVESCREEN  then
            self:cancelEdit()
        elseif keys.SELECT then
            self:commitEdit()
        elseif keys._STRING then
            if keys._STRING==0 then
                self.input=string.sub(self.input,1,-2)
            else
                self.input=self.input.. string.char(keys._STRING)
            end
        end
    end
end
function GmEditorUi:pushTarget(target_to_push)
    local new_tbl={}
    new_tbl.target=target_to_push
    new_tbl.keys={}
    new_tbl.selected=1
    for k,v in pairs(target_to_push) do
        table.insert(new_tbl.keys,k)
    end
    new_tbl.item_count=#new_tbl.keys
    table.insert(self.stack,new_tbl)
end
function GmEditorUi:popTarget()
    table.remove(self.stack) --removes last element
    if #self.stack==0 then
        self:dismiss()
    end
end
function show_editor(trg)
    local screen = GmEditorUi{target=trg}
    screen:show()
end
if #args~=0 then
    if args[1]=="dialog" then
        function thunk(entry)
            local t=load("return "..entry)()
            show_editor(t)
        end
        dialog.showInputPrompt("Gm Editor", "Object to edit:", COLOR_GRAY, "",thunk)
    else
        local t=load("return "..args[1])()
        show_editor(t)
    end
else
    show_editor(getTargetFromScreens())
end