local OPCODE=205
local window
local topButton
local trackerWindow
local state={tasks={},active=-1,points=0,cooldown=0}
local selectedTier='All'
local selectedTask=nil

local function captureKeyboard()
  if not window then return end
  window:grabKeyboard()
  window.onKeyDown = function(_, keyCode, keyboardModifiers)
    if keyboardModifiers == KeyboardNoModifier and keyCode == KeyEscape then hide() end
    return true
  end
end

local function releaseKeyboard()
  if window then window:ungrabKeyboard() end
end

local tierColors={Novice='#78d98b',Hunter='#69b9e8',Veteran='#c896ff',Master='#ff9b68'}

local function ui(id)
  return window and window:recursiveGetChildById(id)
end

local function send(action,data)
  local protocol=g_game.getProtocolGame()
  if protocol then protocol:sendExtendedOpcode(OPCODE,json.encode({action=action,data=data or {}})) end
end

local function findTask(id)
  for _,task in ipairs(state.tasks or {}) do if task.index==id then return task end end
end

local function formatNumber(value)
  local text=tostring(value or 0)
  while true do local changed;text,changed=text:gsub('^(-?%d+)(%d%d%d)', '%1,%2');if changed==0 then break end end
  return text
end

local function selectTask(task)
  selectedTask=task
  if not window or not task then return end
  ui('detailName'):setText(task.name)
  ui('detailName'):setColor(tierColors[task.tier] or '#e8d7a2')
  ui('detailTier'):setText(task.tier..' Task  |  Level '..task.level..'+')
  ui('detailGoal'):setText('Hunt '..task.required..' matching creatures, then defeat '..task.boss..' alone.')
  ui('detailReward'):setText(formatNumber(task.exp)..' EXP   |   '..formatNumber(task.gold)..' gold   |   +'..task.points..' Task Points   |   Hunt Reward Casket (random reward)')
  local progress=task.active and math.min(task.required,task.kills or 0) or 0
  ui('detailProgress'):setPercent(math.floor(progress*100/task.required))
  ui('detailProgress'):setText(progress..' / '..task.required)
  ui('accept'):setVisible(not task.active and #(state.activeTasks or {})<3)
  ui('accept'):setEnabled(not task.locked and (state.cooldown or 0)<=0)
  ui('cancel'):setVisible(task.active)
  ui('boss'):setVisible(task.active and task.ready and not task.accepted and not task.bossActive)
  if task.locked then ui('hint'):setText('Requires level '..task.level..'.')
  elseif (state.cooldown or 0)>0 then ui('hint'):setText('Next task available in '..math.ceil(state.cooldown/60)..' minutes.')
  elseif task.bossActive then ui('hint'):setText('Boss challenge active. Defeat the boss before the arena closes.')
  elseif task.accepted then ui('hint'):setText('Boss accepted. Now you can go to the boss portal in Thais, Riftport or Ankrahmun.')
  elseif task.ready then ui('hint'):setText('Hunt complete - accept your private boss challenge, then use a boss portal.')
  elseif task.active then ui('hint'):setText('Active hunt. Progress is saved automatically.')
  elseif #(state.activeTasks or {})>=3 then ui('hint'):setText('Maximum reached: you may have up to 3 active tasks.')
  else ui('hint'):setText('Select Accept Task to begin this hunt.') end
end

local function rebuild()
  if not window then return end
  ui('points'):setText('Task Points: '..(state.points or 0))
  local list=ui('taskList');list:destroyChildren()
  local first=nil
  for _,task in ipairs(state.tasks or {}) do
    if selectedTier=='All' or task.tier==selectedTier then
      first=first or task
      local row=g_ui.createWidget('TaskListRow',list)
      row.name:setText(task.name)
      row.name:setColor(tierColors[task.tier] or '#eeeeee')
      row.tier:setText(task.tier..'  -  Lv. '..task.level..'+')
      row.progress:setText(task.active and ((task.kills or 0)..' / '..task.required) or (task.required..' kills'))
      row.marker:setText(task.bossActive and 'BOSS ACTIVE' or (task.accepted and 'BOSS ACCEPTED' or (task.ready and 'BOSS READY' or (task.active and 'ACTIVE' or (task.locked and 'LOCKED' or 'AVAILABLE')))))
      row.marker:setColor(task.ready and '#ffd86b' or (task.active and '#78d98b' or (task.locked and '#8b8b8b' or '#b9c4c9')))
      local chooseTask=function()
        selectTask(task)
        return true
      end
      row.onClick=chooseTask
      row.onMouseRelease=function(widget,mousePos,mouseButton) return chooseTask() end
    end
  end
  local current=findTask(selectedTask and selectedTask.index or state.active)
  selectTask(current or first)
  if trackerWindow then
    local contents=trackerWindow:getChildById('contents')
    contents:destroyChildren()
    for _,active in ipairs(state.activeTasks or {}) do
      local row=g_ui.createWidget('TaskTrackerRow',contents)
      row.trackerName:setText(active.name)
      row.trackerName:setColor(tierColors[active.tier] or '#e8d7a2')
      row.trackerProgress:setPercent(math.floor(math.min(active.required,active.kills or 0)*100/active.required))
      row.trackerProgress:setText((active.kills or 0)..' / '..active.required)
      row.trackerBoss:setText(active.bossActive and ('BOSS ACTIVE: '..active.boss) or (active.accepted and ('GO TO BOSS PORTAL: '..active.boss) or (active.ready and ('BOSS READY: '..active.boss) or ('Boss: '..active.boss))))
    end
    trackerWindow:setHeight(math.max(72,36+#(state.activeTasks or {})*72))
    if #(state.activeTasks or {})>0 then trackerWindow:show() else trackerWindow:hide() end
  end
end

local function onOpcode(protocol,opcode,buffer)
  local ok,msg=pcall(json.decode,buffer)
  if not ok or type(msg)~='table' or msg.action~='sync' then return end
  state=msg.data or state
  rebuild()
  if msg.message and msg.message~='' then ui('notice'):setText(msg.message) end
end

local function setTier(tier)
  selectedTier=tier
  local buttons={All='all',Novice='novice',Hunter='hunter',Veteran='veteran',Master='master'}
  for name,id in pairs(buttons) do
    local button=ui(id)
    local active=name==tier
    button:setBackgroundColor(active and '#5b4524ee' or '#241f19ee')
    button:setBorderColor(active and '#dfb95d' or '#82672f')
    button:setColor(active and '#fff2bd' or '#f4dfaa')
  end
  rebuild()
end

function init()
  ProtocolGame.registerExtendedOpcode(OPCODE,onOpcode)
  connect(g_game,{onGameStart=onGameStart,onGameEnd=onGameEnd})
  window=g_ui.displayUI('tasks');window:hide()
  trackerWindow=g_ui.loadUI('tracker',modules.game_interface.getRightPanel());trackerWindow:setup();trackerWindow:hide()
  ui('all').onClick=function()setTier('All')end
  ui('novice').onClick=function()setTier('Novice')end
  ui('hunter').onClick=function()setTier('Hunter')end
  ui('veteran').onClick=function()setTier('Veteran')end
  ui('master').onClick=function()setTier('Master')end
  ui('accept').onClick=function()if selectedTask then send('start',{id=selectedTask.index})end end
  ui('cancel').onClick=function()if selectedTask then send('cancel',{id=selectedTask.index})end end
  ui('boss').onClick=function()if selectedTask then send('boss',{id=selectedTask.index})end end
  setTier('All')
  topButton=modules.client_topmenu.addRightGameButton('reperaTasksButton',tr('Hunt Tasks'),'/images/topbuttons/repera-tasks',toggle,false,7)
  if g_game.isOnline() then onGameStart() end
end

function terminate()
  disconnect(g_game,{onGameStart=onGameStart,onGameEnd=onGameEnd})
  ProtocolGame.unregisterExtendedOpcode(OPCODE)
  if topButton then topButton:destroy();topButton=nil end
  if trackerWindow then trackerWindow:destroy();trackerWindow=nil end
  releaseKeyboard()
  if window then window:destroy();window=nil end
end

function onGameStart() scheduleEvent(function()send('init')end,600) end
function onGameEnd() hide();if trackerWindow then trackerWindow:hide() end end
function show() window:show();window:raise();window:focus();captureKeyboard();send('init') end
function hide() releaseKeyboard();if window then window:hide() end end
function toggle() if window:isVisible() then hide() else show() end end
