HarvestRoute = {}
HarvestRoute.name = "HarvestRoute"
HarvestRoute.displayName = 'HarvestRoute for Harvestmap'
HarvestRoute.version = "1.0.3"
HarvestRoute.author = "generic"
HarvestRoute.settingsVersion = 1

HarvestRoute.defaultSettings = {
  trackerRange = 5
}

local LibAddonMenu2 = LibAddonMenu2

local Farm = Harvest.farm
local Editor = Farm.editor
local Path = Harvest.path
local MapCache = nil
local Helper = Farm.helper

local HarvestRouteButton = nil
local isActive = false
local currentZoneId = nil
local lastNodeId = nil
local pathNodeId = nil
local pathNodes = {}

local function initialize( eventCode, addOnName )

    if ( addOnName ~= HarvestRoute.name ) then
        return
    end
    local padding = 30
    local control = WINDOW_MANAGER:CreateControlFromVirtual('HarvestRouteButton', HarvestFarmGenerator, "ZO_DefaultButton")
    control:SetText(HarvestRoute.GetLocalization( "buttonstarttracker" ))
    control:SetAnchor(TOPLEFT, Harvest.farm.generator.generatorBar , BOTTOMLEFT, 0, 10)
    control:SetWidth(HarvestFarmGenerator:GetWidth() - padding)
    control:SetClickSound("Click")
    control:SetHandler("OnClicked", HarvestRoute.OnButton )
    HarvestRouteButton = control
    local lastControl = control

    local control = WINDOW_MANAGER:CreateControl(nil, HarvestFarmGenerator, CT_LABEL)
    control:SetFont("ZoFontGame")
    control:SetText(HarvestRoute.GetLocalization( "tourtrackerdescription" ))
    control:SetAnchor(TOPLEFT, lastControl, BOTTOMLEFT, 0, 20)
    control:SetWidth(HarvestFarmGenerator:GetWidth() - padding)
    HarvestRoute.savedVars = ZO_SavedVars:NewAccountWide("HarvestRoute", HarvestRoute.settingsVersion, nil, HarvestRoute.defaultSettings)
    HarvestRoute.InitializeSettingsMenu()

    EVENT_MANAGER:RegisterForEvent("HarvestRoutePlayerActivated", EVENT_PLAYER_ACTIVATED, HarvestRoute.OnPlayerLoaded )

    Harvest.callbackManager:RegisterForEvent(Harvest.events.TOUR_CHANGED, HarvestRoute.checkPath )

end

function HarvestRoute:InitializeSettingsMenu()
  local panel = {
    type = "panel",
    name = HarvestRoute.name,
    displayName = HarvestRoute.displayName,
    author = HarvestRoute.author,
    version = HarvestRoute.version,
    registerForRefresh = true,
    registerForDefaults = true,
  }
  local options = {
      {
          type = "description",
          text = HarvestRoute.GetLocalization("addonsettingstext"),
      },
      {
        type = "slider",
        name = HarvestRoute.GetLocalization("routerangemultiplier"),
        tooltip = HarvestRoute.GetLocalization("routerangemultipliertooltip"),
        min = 3,
        max = 20,
        getFunc = function () return HarvestRoute.savedVars.trackerRange end,
        setFunc = function (value) HarvestRoute.savedVars.trackerRange = value end,
        default = HarvestRoute.defaultSettings.trackerRange,
      }
    }
    LibAddonMenu2:RegisterAddonPanel(HarvestRoute.name.."OptionsMenu", panel)
    LibAddonMenu2:RegisterOptionControls(HarvestRoute.name.."OptionsMenu", options)
end



function HarvestRoute:OnButton()
    if isActive then
      EVENT_MANAGER:UnregisterForUpdate("HarvestRouteUpdatePosition")
      HarvestRouteButton:SetText(HarvestRoute.GetLocalization( "buttonstarttracker" ))
      isActive = false
    else
      EVENT_MANAGER:RegisterForUpdate("HarvestRouteUpdatePosition", 50, HarvestRoute.OnUpdate)
      HarvestRouteButton:SetText(HarvestRoute.GetLocalization( "buttonstoptracker" ))
      local mapMetaData = Harvest.mapTools:GetViewedMapMetaData()
      MapCache = Harvest.Data:GetMapCache( mapMetaData )
      isActive = true
      currentZoneId = GetUnitZoneIndex("player")
    end
end

function HarvestRoute:OnPlayerLoaded()
      local newZoneId = GetUnitZoneIndex("player")
      if currentZoneId and currentZoneId == newZoneId then
        return
      end
      lastNodeId = nil
      pathNodeId = nil
      pathNodes = {}
      currentZoneId = newZoneId
      if isActive then
        HarvestRoute:OnButton()
      end
end

-- hacky, but should do the trick: reset pathnodes when path has changed from outside
function HarvestRoute:checkPath(event, path)
  local index = Farm.path:GetIndex(pathNodeId)
  if not index  then
    pathNodeId = nil
    pathNodes = {}
  end
end

function HarvestRoute:OnUpdate (time)
    if isActive then
      local nodeId = HarvestRoute:GetClosestNode()
      if nodeId and nodeId ~= lastNodeId then
        lastNodeId = nodeId
        local x, y = MapCache:GetLocal(nodeId)
        if Farm.path then
          local index = Farm.path:GetIndex(nodeId)
          if index then
            pathNodeId = nodeId
          else
            if pathNodeId then
              Farm.path:InsertAfter(pathNodeId, nodeId)
              pathNodeId = nodeId
              Harvest.farm.display:Refresh(Farm.path, Harvest.farm.display.selectedPins, Harvest.farm.display.selectedMapCache)
              --Harvest.callbackManager:FireCallbacks(Harvest.events.TOUR_CHANGED, Farm.path)
              -- if we are farming, update the next pin so we can actually finish the loop if we want to
              if Farm.helper:IsRunning() then
                index = Farm.path:GetIndex(pathNodeId)
                Farm.helper.nextPathIndex = index
                Farm.helper:UpdateToNextTarget()
              end
            end
          end
        else --create a new path
          pathNodes[#pathNodes + 1] = nodeId
          if #pathNodes > 2 then
            path = Harvest.path:New(Harvest.mapPins.mapCache)
            path:GenerateFromNodeIds(pathNodes, 1, #pathNodes)
            Farm:SetPath(path)
            pathNodeId = nodeId
            pathNodes = {}
          end
        end
      end
    end
end

-- get the closest node to the player, using HarvestMap MapCache Divisions (and only check 4 divisions, from -50 to +50 from current position)
function HarvestRoute:GetClosestNode()
    local bestNodeId = nil
    if not MapCache then --yeah, no cache no cookies.
      return
    end
    local x, y, z = Harvest.GetPlayer3DPosition()

    local halfDivisionInMeters = zo_floor(MapCache.DivisionWidthInMeters / 2) --should be 50 most of the time, but lets keep it dynamic
    local numDivisions = MapCache.numDivisions
    local totalNumDivisions = MapCache.TotalNumDivisions

    local half_before_x = x - halfDivisionInMeters
    local half_before_y = y - halfDivisionInMeters

    local index = (zo_floor(half_before_x / MapCache.DivisionWidthInMeters) + zo_floor(half_before_y / MapCache.DivisionWidthInMeters) * numDivisions) % MapCache.TotalNumDivisions

    local bestDistance = halfDivisionInMeters * halfDivisionInMeters
    local divisions = MapCache.divisions

    -- check all pintypes, and the 4 divisions that contain the 100 x 100 square around the player
    if divisions then
      for _, pinTypeId in pairs(Harvest.PINTYPES) do
        local pinDivision = MapCache.divisions[pinTypeId]
        if pinDivision then
          for i = index, index + 1 do
            for j = 0, 1 do
              division = pinDivision[(i + j * numDivisions) % totalNumDivisions] or {}

              if division then
                -- check all node in current division, if there are closer nodes to the player
                for _, nodeId in pairs(division) do
                  if MapCache.worldX then
                    dx = MapCache.worldX[nodeId] - x
                    dy = MapCache.worldY[nodeId] - y
                    distance = dx * dx + dy * dy
                    if distance < bestDistance then
                      bestNodeId = nodeId
                      bestDistance = distance
                    end
                  end
                end
              end
            end -- for j
          end -- for i
        end -- if pinDivision
      end --for pinType
      if bestNodeId then
        local realDistance = bestDistance ^ 0.5
        if realDistance < self.savedVars.trackerRange then
          return bestNodeId
        end
      end
    end
    return nil
end

EVENT_MANAGER:RegisterForEvent("HarvestRoute", EVENT_ADD_ON_LOADED, initialize)