minimapWidget = nil
minimapButton = nil
minimapWindow = nil
fullmapView = false
loaded = false
oldZoom = nil
oldPos = nil
virtualFloor = 7
dayTime = {
    h = 12,
    m = 0
}

function init()
    minimapWindow = g_ui.loadUI('minimap', modules.game_interface.getRightPanel())
    if not minimapWindow then
        g_logger.error('Failed to load minimap window')
        return
    end
    minimapWindow:setParent(modules.game_interface.getRightPanel())
    minimapWindow:setup()
    minimapWindow:open()

    minimapWidget = minimapWindow:recursiveGetChildById('minimap')
    if not minimapWidget then
        g_logger.error('Failed to load minimap widget')
        return
    end

    local gameRootPanel = modules.game_interface.getRootPanel()
    g_keyboard.bindKeyPress('Alt+Left', function() minimapWidget:move(1,0) end, gameRootPanel)
    g_keyboard.bindKeyPress('Alt+Right', function() minimapWidget:move(-1,0) end, gameRootPanel)
    g_keyboard.bindKeyPress('Alt+Up', function() minimapWidget:move(0,1) end, gameRootPanel)
    g_keyboard.bindKeyPress('Alt+Down', function() minimapWidget:move(0,-1) end, gameRootPanel)
    g_keyboard.bindKeyDown('Ctrl+M', toggle)
    g_keyboard.bindKeyDown('Ctrl+M', toggleFullMap)
    g_keyboard.bindKeyDown('Escape', function()
        if fullmapView then
            toggleFullMap()
        return true
        end
    end)


    
    connect(g_game, {
        onGameStart = online,
        onGameEnd = offline,
    })

    connect(g_app, {
        onExit = saveMap,
    })

    connect(LocalPlayer, {
        onPositionChange = updateCameraPosition
    })

    if g_game.isOnline() then
        online()
    end
end

function terminate()
    saveMap()

    disconnect(g_game, {
        onGameStart = online,
        onGameEnd = offline,
    })

    disconnect(g_app, {
        onExit = saveMap,
    })

    disconnect(LocalPlayer, {
        onPositionChange = updateCameraPosition
    })

    local gameRootPanel = modules.game_interface.getRootPanel()
    g_keyboard.unbindKeyPress('Alt+Left', gameRootPanel)
    g_keyboard.unbindKeyPress('Alt+Right', gameRootPanel)
    g_keyboard.unbindKeyPress('Alt+Up', gameRootPanel)
    g_keyboard.unbindKeyPress('Alt+Down', gameRootPanel)
    g_keyboard.unbindKeyDown('Ctrl+M')
    g_keyboard.unbindKeyDown('Ctrl+Shift+M')
    g_keyboard.unbindKeyDown('Escape', minimapWidget)


    minimapWindow:destroy()
    if minimapButton then
        minimapButton:destroy()
    end
end

function toggle()
    if not minimapButton then return end
    if minimapButton:isOn() then
        minimapWindow:close()
        minimapButton:setOn(false)
    else
        minimapWindow:open()
        minimapButton:setOn(true)
    end
end

function onMiniWindowClose()
    if minimapButton then
        minimapButton:setOn(false)
    end
end

function online()
    loadMap()
    if minimapWindow and not minimapWindow:isVisible() then
        minimapWindow:open()
    end
    updateCameraPosition()
    onChangeWorldTime()
end

function offline()
    saveMap()
end

function loadMap()
    if not minimapWidget then
        return
    end

    g_minimap.clean()
    loaded = false

    for _, minimapFile in ipairs({'/minimap.otmm', '/data/minimap.otmm'}) do
        if g_resources.fileExists(minimapFile) then
            loaded = g_minimap.loadOtmm(minimapFile)
            if loaded then
                break
            end
        end
    end

    minimapWidget:load()
end

function saveMap()
    if not minimapWidget then
        return
    end

    g_minimap.saveOtmm('/minimap.otmm')
    minimapWidget:save()
end

local function refreshVirtualFloors()
    local layersPanel = minimapWindow:recursiveGetChildById('layersPanel')
    if not layersPanel then
        return
    end

    local layersMark = layersPanel:getChildById('layersMark')
    local automapLayers = layersPanel:getChildById('automapLayers')

    if not layersMark or not automapLayers then
        return
    end

    layersMark:setMarginTop(((virtualFloor + 1) * 4) - 3)

    automapLayers:setImageClip((virtualFloor * 14) .. ' 0 14 67')
end


function updateCameraPosition()
    local player = g_game.getLocalPlayer()
    if not player then return end
    local pos = player:getPosition()
    if not pos then return end

    if not minimapWidget:isDragging() then
        if not fullmapView then
            minimapWidget:setCameraPosition(player:getPosition())
        end
        minimapWidget:setCrossPosition(player:getPosition())
    end

    virtualFloor = pos.z
    refreshVirtualFloors()
end

function upLayer()
    if virtualFloor == 0 then return end
    minimapWidget:floorUp(1)
    virtualFloor = virtualFloor - 1
    refreshVirtualFloors()
end

function downLayer()
    if virtualFloor == 15 then return end
    minimapWidget:floorDown(1)
    virtualFloor = virtualFloor + 1
    refreshVirtualFloors()
end


--[[
    Might be better option in the future to send time from server directly and update this function.
]]
local dayTime = { hour = 12, minute = 0 }

function onChangeWorldTime(hour, minute)
    hour = hour or dayTime.hour or 12
    minute = minute or dayTime.minute or 0

    if hour >= 24 then
        hour = hour % 24
    end

    dayTime.hour = hour
    dayTime.minute = minute

    scheduleEvent(function()
        local nextMinute = dayTime.minute + 12
        local nextHour = dayTime.hour

        if nextMinute >= 60 then
            nextHour = nextHour + 1
            nextMinute = nextMinute - 60
        end

        onChangeWorldTime(nextHour, nextMinute)
    end, 30000)

    local rosePanel = minimapWindow:recursiveGetChildById('rosePanel')
    if not rosePanel then return end

    local ambients = rosePanel:getChildById('ambients')
    if not ambients then return end

    local mainBar = ambients:getChildById('main')
    local overflowBar = ambients:getChildById('secondary')

    local totalMinutes = (hour * 60) + minute
    local pixelOffset = math.floor((124 / (24 * 60)) * totalMinutes)
    local visibleWidth = 31
    local overflowWidth = 0

    if pixelOffset + visibleWidth >= 124 then
        overflowWidth = (pixelOffset + visibleWidth - 124) + 1
        visibleWidth = 31 - overflowWidth
    end

    mainBar:setWidth(visibleWidth)
    if visibleWidth > 0 then
        mainBar:setImageClip(pixelOffset .. ' 0 ' .. visibleWidth .. ' 31')
        mainBar:show()
    else
        mainBar:hide()
    end

    if overflowWidth > 0 then
        overflowBar:setImageClip('0 0 ' .. overflowWidth .. ' 31')
        overflowBar:show()
    else
        overflowBar:hide()
    end
end

function onMapCirclePress(dir)
    if dir == 'north' then
        minimapWidget:move(0, 1)
    elseif dir == 'north-east' then
        minimapWidget:move(-1, 1)
    elseif dir == 'east' then
        minimapWidget:move(-1, 0)
    elseif dir == 'south-east' then
        minimapWidget:move(-1, -1)
    elseif dir == 'south' then
        minimapWidget:move(0, -1)
    elseif dir == 'south-west' then
        minimapWidget:move(1, -1)
    elseif dir == 'west' then
        minimapWidget:move(1, 0)
    elseif dir == 'north-west' then
        minimapWidget:move(1, 1)
    end
end

function zoomIn()
    minimapWidget:zoomIn()
end

function zoomOut()
    minimapWidget:zoomOut()
end

local minimapOriginalParent
local minimapOriginalRect

function toggleFullMap()
  if not fullmapView then
    fullmapView = true

    minimapOriginalParent = minimapWidget:getParent()
    minimapOriginalRect = minimapWidget:getRect()

    minimapWidget:setParent(modules.game_interface.getRootPanel())
    minimapWidget:fill('parent')
    minimapWidget:setAlternativeWidgetsVisible(true)
    minimapWidget.onDoubleClick = function()
      toggleFullMap()
    end
  else
    fullmapView = false

    minimapWidget:setParent(minimapOriginalParent)
    minimapWidget:setRect(minimapOriginalRect)
    minimapWidget:setAlternativeWidgetsVisible(false)
  end

  local zoom = oldZoom or 0
  local pos = oldPos or minimapWidget:getCameraPosition()
  oldZoom = minimapWidget:getZoom()
  oldPos = minimapWidget:getCameraPosition()
  minimapWidget:setZoom(zoom)
  minimapWidget:setCameraPosition(pos)
end