local ICON_PER_ROW = 6
local ICON_PADDING = 40
local ICON_SIZE = 64
local ICON_OFFSET_X = 0
local ICON_OFFSET_Y = 0

ZO_ICONSELECTOR_MOVEMENT_TYPES = 
{
    MOVE_RIGHT = 1,
    MOVE_LEFT = 2,
    MOVE_UP = 3,
    MOVE_DOWN = 4,

    -- LAST allows derived classes to start their movement enumerations after the base movements 
    LAST = 5,
}

function ZO_IconSelectorPlaySound(type)
    if type == ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_RIGHT then
        PlaySound(SOUNDS.HOR_LIST_ITEM_SELECTED)
    elseif type == ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_LEFT then
        PlaySound(SOUNDS.HOR_LIST_ITEM_SELECTED)
    elseif type == ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_UP then
        PlaySound(SOUNDS.GAMEPAD_MENU_UP)
    elseif type == ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_DOWN then
        PlaySound(SOUNDS.GAMEPAD_MENU_DOWN)
    end
end

--------------------
-- Util Functions --
--------------------

local function GetGridRowCol(index, itemsPerRow)
    local row = zo_floor((index - 1) / itemsPerRow)
    local col = (index - 1) % itemsPerRow

    return row, col
end

local function GetGridIndex(row, col, itemsPerRow)
    local index = (row * itemsPerRow) + col

    return index
end

----------------------------
-- ZO_GamepadIconSelector --
----------------------------

ZO_GamepadIconSelector = ZO_Object:Subclass()

function ZO_GamepadIconSelector:New(...)
    local object = ZO_Object.New(self)
    object:Initialize(...)
    return object
end

function ZO_GamepadIconSelector:Initialize(control, settings)
    self.control = control
    self.control.iconSelectorObject = self
    
    self.iconsPerRow = ICON_PER_ROW
    self.iconPadding = ICON_PADDING
    self.iconSize = ICON_SIZE
    self.iconOffsetX = ICON_OFFSET_X
    self.iconOffsetY = ICON_OFFSET_Y    

    if(settings.iconsPerRow) then
        self.iconsPerRow = settings.iconsPerRow
    end

    if(settings.iconPadding) then
        self.iconPadding = settings.iconPadding
    end
        
    if(settings.iconSize) then
        self.iconSize = settings.iconSize
    end
        
    if(settings.iconOffsetX) then
        self.iconOffsetX = settings.iconOffsetX
    end

    if(settings.iconOffsetY) then
        self.iconOffsetY = settings.iconOffsetY
    end

    if(settings.totalIcons > self.iconsPerRow) then
        self.vertMovementController = ZO_MovementController:New(MOVEMENT_CONTROLLER_DIRECTION_VERTICAL)
    end
    self.horzMovementController = ZO_MovementController:New(MOVEMENT_CONTROLLER_DIRECTION_HORIZONTAL)

    self.onPlaySoundFunction = ZO_IconSelectorPlaySound

    self.iconHighlight = control:GetNamedChild("Highlight")
    self.iconHighlight:SetHidden(true)
    
    self.iconHighlightHint = self.iconHighlight:GetNamedChild("Hint")

    self.iconSelectorControls = {}
    local iconSelectorId = 0
    for i = 1, settings.totalIcons do
        local iconSelector = CreateControlFromVirtual(settings.uniqueName, control, "ZO_GamepadIconSelectorElementTemplate", iconSelectorId)
        iconSelector.iconIndex = i

        iconSelector:SetDimensions(self.iconSize, self.iconSize)

        local row, col = GetGridRowCol(i, self.iconsPerRow)

        iconSelector:SetAnchor(TOPLEFT, nil, TOPLEFT, col * (self.iconSize + self.iconPadding) + self.iconOffsetX, row * (self.iconSize + self.iconPadding) + self.iconOffsetY)
        settings.initFunc(iconSelector, i)
        iconSelectorId = iconSelectorId + 1
        table.insert(self.iconSelectorControls, iconSelector)
    end

    local defaultHighlightIndex = 1
    if(settings.defaultHighlightIndex) then
        defaultHighlightIndex = settings.defaultHighlightIndex
    end
    self:HighlightIconControl(defaultHighlightIndex)
end

function ZO_GamepadIconSelector:ForAllIconControls(func, ...)
    for i = 1, #self.iconSelectorControls do
        func(self.iconSelectorControls[i], i, ...)
    end
end

------------------------
-- Selector Highlight --
------------------------

function ZO_GamepadIconSelector:ModifyHighlightIndex(rowDiff, colDiff)
    local oldHighlightIndex = self.highlightIndex
    local itemsPerRow = self.iconsPerRow
    local totalItems = #self.iconSelectorControls

    local minCol = 0
    local minRow = 0

    local row, col = GetGridRowCol(self.highlightIndex, itemsPerRow)
    local maxRow, maxCol = GetGridRowCol(totalItems, itemsPerRow)
    
    col = col + colDiff

    if(row ~= maxRow) then
        if(col < minCol) then
            col = itemsPerRow - 1
        elseif(col > itemsPerRow - 1) then
            col = minCol
        end
    else
        if(col < minCol) then
            col = maxCol
        elseif(col > maxCol) then
            col = minCol
        end
    end

    row = row + rowDiff

    if(row < minRow) then
        row = maxRow
    elseif (row > maxRow) then
        row = 0
    end

    if(row == maxRow and col > maxCol) then
        if(rowDiff > 0) then
            row = minRow
        else
            row = row - 1
        end
    end
        
    local newHighlightIndex = GetGridIndex(row, col, itemsPerRow) + 1

    self:HighlightIconControl(newHighlightIndex)

    return newHighlightIndex ~= oldHighlightIndex
end

function ZO_GamepadIconSelector:HighlightIconControl(index)
    local control = self.iconSelectorControls[index]
    self.highlightIndex = index
    self.iconHighlight:ClearAnchors()
    self.iconHighlight:SetAnchor(CENTER, control, CENTER, 0, 0)
    self.iconHighlight:SetHidden(false)

    if(control.highlightHint) then
        self.iconHighlightHint:SetText(control.highlightHint)
        self.iconHighlightHint:SetHidden(false)
    else
        self.iconHighlightHint:SetHidden(true)
    end
end

function ZO_GamepadIconSelector:GetHighlightIndex()
    return self.highlightIndex
end

function ZO_GamepadIconSelector_RefreshIconSelectionIndicator(control, index, selectedIconIndex)
    local selectedIndicator = control:GetNamedChild("SelectedIndicator")
    selectedIndicator:SetHidden(index ~= selectedIconIndex)
end

-------------------------
-- Movement Controller --
-------------------------

function ZO_GamepadIconSelector:Activate()
    self:SetDirectionalInputEnabled(true)
end

function ZO_GamepadIconSelector:Deactivate()
    self:SetDirectionalInputEnabled(false)
end

function ZO_GamepadIconSelector:SetDirectionalInputEnabled(enabled)
    if(self.directionInputEnabled ~= enabled) then
        self.directionInputEnabled = enabled
        self.iconHighlight:SetHidden(not enabled)

        if enabled then
            DIRECTIONAL_INPUT:Activate(self, self.control)
        else
            DIRECTIONAL_INPUT:Deactivate(self)
        end
    end
end

function ZO_GamepadIconSelector:UpdateDirectionalInput()
    if(self.horzMovementController) then
        local result = self.horzMovementController:CheckMovement()
        if result == MOVEMENT_CONTROLLER_MOVE_NEXT then
            self:MoveRight()
        elseif result == MOVEMENT_CONTROLLER_MOVE_PREVIOUS then
            self:MoveLeft()
        end
    end

    if(self.vertMovementController) then
        local result = self.vertMovementController:CheckMovement()
        if result == MOVEMENT_CONTROLLER_MOVE_NEXT then
            self:MoveDown()
        elseif result == MOVEMENT_CONTROLLER_MOVE_PREVIOUS then
            self:MoveUp()
        end
    end
end

function ZO_GamepadIconSelector:MoveUp()
    if self:ModifyHighlightIndex(-1, 0) then
        self.onPlaySoundFunction(ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_UP)
    end
end

function ZO_GamepadIconSelector:MoveDown()
    if self:ModifyHighlightIndex(1, 0) then
        self.onPlaySoundFunction(ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_DOWN)
    end
end

function ZO_GamepadIconSelector:MoveLeft()
    if self:ModifyHighlightIndex(0, -1) then
        self.onPlaySoundFunction(ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_LEFT)
    end
end

function ZO_GamepadIconSelector:MoveRight()
    if self:ModifyHighlightIndex(0, 1) then
        self.onPlaySoundFunction(ZO_ICONSELECTOR_MOVEMENT_TYPES.MOVE_RIGHT)
    end
end

function ZO_GamepadIconSelector:SetPlaySoundFunction(fn)
    self.onPlaySoundFunction = fn
end
