-- CustomCompassPins by Shinni
local version = 1.13
local onlyUpdate = false

if COMPASS_PINS then
    if COMPASS_PINS.version and COMPASS_PINS.version >= version then
        return
    end
    onlyUpdate = true
else
    COMPASS_PINS = {}
end

local PARENT = COMPASS.container
local FOV = math.pi * 0.6

--
-- Base class, can be accessed via COMPASS_PINS
--

local CompassPinManager = ZO_ControlPool:Subclass()

function COMPASS_PINS:New( ... )
    if onlyUpdate then
        self:UpdateVersion()
    else
        self:Initialize( ... )
    end

    self.control:SetHidden(false)
    self.control:SetHandler("OnUpdate", function() self:Update() end )
    self.version = version
    self.defaultFOV = FOV
    --EVENT_MANAGER:RegisterForEvent("CustomCompassPins", EVENT_ZONE_CHANGED, function()
--      self:RefreshPins() end )
    return result
end

function COMPASS_PINS:UpdateVersion()
    --local pins = self.pinManager.pins
    local data = self.pinManager.pinData
    self.pinManager = CompassPinManager:New()
    --if pins then
    --  self.pinManager.pins = pins
    --end
    if data then
        self.pinManager.pinData = data
    end
end

function COMPASS_PINS:Initialize( ... )
    self.control = WINDOW_MANAGER:CreateControlFromVirtual("CP_Control", GuiRoot, "ZO_MapPin")
    self.pinCallbacks = {}
    self.pinLayouts = {}
    self.pinManager = CompassPinManager:New()
end

-- pinType should be a string eg "skyshard"
-- pinCallbacks should be a function, it receives the pinManager as argument
-- layout should be table, currently only the key texture is used (which should return a string)
function COMPASS_PINS:AddCustomPin( pinType, pinCallback, layout )
    self.pinCallbacks[ pinType ] = pinCallback
    self.pinLayouts[ pinType ] = layout
    self.pinManager:CreatePinType( pinType )
end

-- refreshes/calls the pinCallback of the given pinType
-- refreshes all custom pins if no pinType is given
function COMPASS_PINS:RefreshPins( pinType )
    self.pinManager:RemovePins( pinType )
    if pinType then
        if not self.pinCallbacks[ pinType ] then
            return
        end
        self.pinCallbacks[ pinType ]( self.pinManager )
    else
        for tag, callback in pairs( self.pinCallbacks ) do
            callback( self.pinManager )
        end
    end

end

-- updates the pins (recalculates the position of the pins)
function COMPASS_PINS:Update()
    -- maybe add some delay, because pin update could be to expensive to be calculated every frame
    local heading = GetPlayerCameraHeading()
    if not heading then
        return
    end
    if heading > math.pi then --normalize heading to [-pi,pi]
        heading = heading - 2 * math.pi
    end

    local x, y = GetMapPlayerPosition("player")
    self.pinManager:Update( x, y, heading )
end

--
-- pin manager class, updates position etc
--

function CompassPinManager:New( ... )

    local result = ZO_ControlPool.New(self, "ZO_MapPin", PARENT, "Pin")
    result:Initialize( ... )

    return result
end

function CompassPinManager:Initialize( ... )
    --self.pins = {}
    self.pinData = {}
    self.defaultAngle = 1
end

function CompassPinManager:CreatePinType( pinType )
    --self.pins[ pinType ] = {}
end

function CompassPinManager:GetNewPin( data )
    local pin, pinKey = self:AcquireObject()
    --table.insert( self.pins[ data.pinType ], pinKey )
    self:ResetPin( pin )
    pin:SetHandler("OnMouseDown", nil)
    pin:SetHandler("OnMouseUp", nil)
    pin:SetHandler("OnMouseEnter", nil)
    pin:SetHandler("OnMouseExit", nil)

    pin.xLoc = data.xLoc
    pin.yLoc = data.yLoc
    pin.pinType = data.pinType
    pin.pinTag = data.pinTag

    local layout = COMPASS_PINS.pinLayouts[ data.pinType ]
    local texture = pin:GetNamedChild( "Background" )
    texture:SetTexture( layout.texture )

    return pin, pinKey
end
-- creates a pin of the given pinType at the given location
-- (radius is not implemented yet)
function CompassPinManager:CreatePin( pinType, pinTag, xLoc, yLoc )
    local data = {}

    data.xLoc = xLoc
    data.yLoc = yLoc
    data.pinType = pinType
    data.pinTag = pinTag

    table.insert(self.pinData, data)
end

function CompassPinManager:RemovePins( pinType )
    if not pinType then
        self:ReleaseAllObjects()
        self.pinData = {}
        --for pinType, _ in pairs( self.pins ) do
        --  self.pins[ pinType ] = {}
        --end
    else
        --if not self.pins[ pinType ] then
        --  return
        --end
        --for _, pinKey in pairs( self.pins[ pinType ] ) do
        --  self:ReleaseObject( pinKey )
        --end
        for key, data in pairs( self.pinData ) do
            if data.pinType == pinType then
                if data.pinKey then
                    self:ReleaseObject( data.pinKey )
                end
                self.pinData[key] = nil
            end
        end
        --self.pins[ pinType ] = {}
    end
end

function CompassPinManager:ResetPin( pin )
    for _, layout in pairs(COMPASS_PINS.pinLayouts) do
        if layout.additionalLayout then
            layout.additionalLayout[2]( pin )
        end
    end
end

function CompassPinManager:Update( x, y, heading )
    local value
    local pin
    local angle
    local normalizedAngle
    local xDif, yDif
    local layout
    local normalizedDistance
    for _, pinData in pairs( self.pinData ) do

        layout = COMPASS_PINS.pinLayouts[ pinData.pinType ]
        xDif = x - pinData.xLoc
        yDif = y - pinData.yLoc
        normalizedDistance = (xDif * xDif + yDif * yDif) / (layout.maxDistance * layout.maxDistance)
        if normalizedDistance < 1 then

            if pinData.pinKey then
                pin = self:GetExistingObject( pinData.pinKey )
            else
                pin, pinData.pinKey = self:GetNewPin( pinData )
            end

            if pin then
                --self:ResetPin( pin )
                pin:SetHidden( true )
                angle = -math.atan2( xDif, yDif )
                angle = (angle + heading)
                if angle > math.pi then
                    angle = angle - 2 * math.pi
                elseif angle < -math.pi then
                    angle = angle + 2 * math.pi
                end

                normalizedAngle = 2 * angle / (layout.FOV or COMPASS_PINS.defaultFOV)

                if zo_abs(normalizedAngle) > (layout.maxAngle or self.defaultAngle) then
                    pin:SetHidden( true )
                else

                    pin:ClearAnchors()
                    pin:SetAnchor( CENTER, PARENT, CENTER, 0.5 * PARENT:GetWidth() * normalizedAngle, 0)
                    pin:SetHidden( false )

                    if layout.sizeCallback then
                        layout.sizeCallback( pin, angle, normalizedAngle, normalizedDistance )
                    else
                        if zo_abs(normalizedAngle) > 0.25 then
                            pin:SetDimensions( 36 - 16 * zo_abs(normalizedAngle), 36 - 16 * zo_abs(normalizedAngle) )
                        else
                            pin:SetDimensions( 32 , 32  )
                        end
                    end

                    pin:SetAlpha(1 - normalizedDistance)

                    if layout.additionalLayout then
                        layout.additionalLayout[1]( pin, angle, normalizedAngle, normalizedDistance)
                    end

                end
            else
                d("CustomCompassPin Error:")
                d("no pin with key : " .. pinData.pinKey .. ", found!")
            end
        else
            if pinData.pinKey then
                self:ReleaseObject( pinData.pinKey )
                pinData.pinKey = nil
            else

            end
        end
    end
end

COMPASS_PINS:New()
--can't create OnUpdate handler on via CreateControl, so i'll have to create somethin else via virtual


--[[
example:

COMPASS_PINS:CreatePinType( "skyshard", function (pinManager)
        for _, skyshard in pairs( mySkyshards ) do
            pinManager:CreatePin( "skyshard", skyshard.x, skyshard.y )
        end
    end,
    { texture = "esoui/art/compass/quest_assistedareapin.dds" } )

]]--