--
-- Created by IntelliJ IDEA.
-- User: RJ
-- Date: 13/08/2021
-- Time: 10:25
-- To change this template use File | Settings | File Templates.
--

--***********************************************************
--**                    THE INDIE STONE                    **
--***********************************************************

if isServer() then return end

require "ISBuildingObject"

ISPlace3DItemCursor = ISBuildingObject:derive("ISPlace3DItemCursor");

local FONT_HGT_MEDIUM = getTextManager():getFontHeight(UIFont.Medium)

function ISPlace3DItemCursor:create(x, y, z, north, sprite)
    local drop = self.itemSq == nil; -- nil when the item is in a vehicle
    if self.itemSq and luautils.walkAdj(self.chr, self.itemSq, true) then
        drop = true;
    end
    if not drop then return; end
    for i,v in ipairs(self.items) do
--        if not v:getWorldItem() or not AdjacentFreeTileFinder.isTileOrAdjacent(self.selectedSqDrop, v:getWorldItem():getSquare()) then
            ISWorldObjectContextMenu.transferIfNeeded(self.chr, v)
--        end
    end
    if self.chr:getJoypadBind() == -1 then
        self.placeAll = isShiftKeyDown()
    end
    if self.placeAll then
        if luautils.walkAdjAltTest(self.chr, self.selectedSqDrop, self.itemSq, true) then
            for i,v in ipairs(self.items) do
                if self.chr:isEquipped(v) then
                    ISTimedActionQueue.add(ISUnequipAction:new(self.chr, v, 1));
                end
                ISTimedActionQueue.add(ISDropWorldItemAction:new(self.chr, v, self.selectedSqDrop, self.render3DItemXOffset, self.render3DItemYOffset, self.render3DItemZOffset, self.render3DItemRot, #self.items > 1));
            end
        end
    else
        local item = table.remove(self.items, 1)
        if luautils.walkAdjAltTest(self.chr, self.selectedSqDrop, self.itemSq, true) then
            if self.chr:isEquipped(item) then
                ISTimedActionQueue.add(ISUnequipAction:new(self.chr, item, 1));
            end
            ISTimedActionQueue.add(ISDropWorldItemAction:new(self.chr, item, self.selectedSqDrop, self.render3DItemXOffset, self.render3DItemYOffset, self.render3DItemZOffset, self.render3DItemRot, false));
        end
        if #self.items > 0 then
            getCell():setDrag(self, self.chr:getPlayerNum())
        end
    end
    self.keepOnSquare = false
end

function ISPlace3DItemCursor:walkTo(x, y, z)
    return true
end

function ISPlace3DItemCursor:isValid(square)
    if not self.previousSq then
        self.previousSq = square;
    end
    if self.previousSq ~= square then -- reset the selected high when changing sq
        self.previousSq = square;
        self.surfaceSelected = 1;
    end
    if self.chr:getCharacterActions():isEmpty() then
        self.chr:faceLocation(square:getX(), square:getY())
    end
--    print("render X/Y", self.render3DItemXOffset, self.render3DItemYOffset, self.render3DItemRot)
    if not square:isCouldSee(self.chr:getPlayerNum()) then
        return false
    end
    if square:isWallTo(self.chr:getCurrentSquare()) or square:isWindowTo(self.chr:getCurrentSquare()) then
        return false
    end
    local surface = self:getSurface(square)
    if (surface == 0) and (square:isSolid() or square:isSolidTrans() or not square:TreatAsSolidFloor()) then
        return false
    end
    return true;
end

function ISPlace3DItemCursor:render(x, y, z, square)
    if not self.RENDER_SPRITE_FLOOR then
        self.RENDER_SPRITE_FLOOR = IsoSprite.new()
        self.RENDER_SPRITE_FLOOR:LoadFramesNoDirPageSimple('media/ui/FloorTileCursor.png')
    end
    if not square or not self:isValid(square) then
        self.RENDER_SPRITE_FLOOR:RenderGhostTileColor(x, y, z, 1.0, 0.0, 0.0, 0.2)
        self.chr:setIgnoreMovement(false) -- for joypad Y button
        return
    end
    self.RENDER_SPRITE_FLOOR:RenderGhostTileColor(x, y, z, 1.0, 1.0, 1.0, 0.2)

    self:checkRotateKey()
    self:checkSelectSurfaceKey()
    self:checkRotateJoypad()
    self:checkPositionJoypad()

    if self.surfaceKeyTimer then
        self.surfaceKeyTimer = self.surfaceKeyTimer - 1;
        if self.surfaceKeyTimer == 0 then
            self.surfaceKeyTimer = nil;
        end
    end

    local worldX = x + 0.5
    local worldY = y + 0.5
    if self.chr:getPlayerNum() == 0 and (self.chr:getJoypadBind() == -1 or wasMouseActiveMoreRecentlyThanJoypad()) then
        worldX = screenToIsoX(self.player, getMouseX(), getMouseY(), math.floor(self.chr:getZ()))
        worldY = screenToIsoY(self.player, getMouseX(), getMouseY(), math.floor(self.chr:getZ()))
        if self.isLeftDown then
            if not self.keepOnSquare then
                self.keepOnSquare = true
                self.keepOnSquareX = math.floor(worldX)
                self.keepOnSquareY = math.floor(worldY)
            end
        end
        if self.keepOnSquare then
            worldX = PZMath.clampFloat(worldX, self.keepOnSquareX + 0.05, self.keepOnSquareX + 0.95)
            worldY = PZMath.clampFloat(worldY, self.keepOnSquareY + 0.05, self.keepOnSquareY + 0.95)
        end
    end
    if self.chr:getJoypadBind() ~= -1 and not (self.chr:getPlayerNum() == 0 and wasMouseActiveMoreRecentlyThanJoypad()) then
        worldX = x + self.joypadPositionX
        worldY = y + self.joypadPositionY
    end
    local sq = getSquare(worldX, worldY, self.chr:getZ());
    if not sq then
        return;
    end
    self.render3DItemXOffset = worldX - sq:getX();
    self.render3DItemYOffset = worldY - sq:getY();
    self.render3DItemZOffset = self:getSurface(sq);
    if square:HasStairs() then
        self.render3DItemZOffset = square:getApparentZ(self.render3DItemXOffset, self.render3DItemYOffset)
    end
    self.selectedSqDrop = sq;
    if self.placeAll then
        for i,v in ipairs(self.items) do
            -- ensure you're not too far
            local sq = nil;
            if v:getWorldItem() then
                sq = v:getWorldItem():getSquare();
            end
            local container = item:getOutermostContainer()
            if container then
                if container:getParent() then
                    sq = container:getParent():getSquare();
                end
                if container:getContainingItem() and container:getContainingItem():getWorldItem() then
                    sq = container:getContainingItem():getWorldItem():getSquare()
                end
            end
            self.itemSq = sq;
            if container and container:getVehiclePart() then
                self.itemSq = nil
            end
            Render3DItem(v, sq, worldX, worldY, self.selectedSqDrop:getZ() + self.render3DItemZOffset, self:clamp(self.render3DItemRot));
        end
    else
        local item = self.items[1]
        local sq = nil;
        if item:getWorldItem() then
            sq = item:getWorldItem():getSquare();
        end
        local container = item:getOutermostContainer()
        if container then
            if container:getParent() then
                sq = container:getParent():getSquare();
            end
            if container:getContainingItem() and container:getContainingItem():getWorldItem() then
                sq = container:getContainingItem():getWorldItem():getSquare()
            end
        end
        self.itemSq = sq;
        if container and container:getVehiclePart() then
            self.itemSq = nil
        end
        Render3DItem(item, sq, worldX, worldY, self.selectedSqDrop:getZ() + self.render3DItemZOffset, self:clamp(self.render3DItemRot));
    end

--    ISPlace3DItemCursor.panel:drawText("Press R/Shift-R to rotate", 0, 0, 1, 1, 1, 1, UIFont.Medium);

--    ISEquippedItem.instance:drawText(getText("IGUI_Place3DItem_Rotate", getKeyName(getCore():getKey("Rotate building")), getKeyName(42)), 0, 200, 1, 1, 1, 1, UIFont.Small);
--    if self.surfacesPossible and #self.surfacesPossible > 1 then
--        ISEquippedItem.instance:drawText(getText("IGUI_Place3DItem_Surface", getKeyName(getCore():getKey("Toggle mode"))), 0, 220, 1, 1, 1, 1, UIFont.Small);
--    end
end

function ISPlace3DItemCursor:deactivate()
    self.chr:setIgnoreMovement(false) -- for joypad Y button
    ISBuildingObject.deactivate(self)
end

-- For keyboard & mouse user.
function ISPlace3DItemCursor:drawPrompt(playerNum, ui)
    if playerNum ~= 0 then return end
    if JoypadState.players[playerNum+1] then return end
    local screenX = getPlayerScreenLeft(playerNum)
    local screenY = getPlayerScreenTop(playerNum)
    local screenW = getPlayerScreenWidth(playerNum)
    local screenH = getPlayerScreenHeight(playerNum)
    local y = screenH - (FONT_HGT_MEDIUM * 4)
    local textW = getTextManager():MeasureStringX(UIFont.Medium, getText("IGUI_Place3DItem_Rotate"))
    local hotBar = getPlayerHotbar(playerNum)
    if hotBar and hotBar:isReallyVisible() and (screenX + screenW - 30 - textW < hotBar:getRight()) then
        y = hotBar:getY() - FONT_HGT_MEDIUM * 3
    end
    ui:drawTextRight(getText("IGUI_Place3DItem_Rotate", getKeyName(getCore():getKey("Rotate building")), getKeyName(42)), screenX + screenW - 30, y, 1, 1, 1, 1, UIFont.NewMedium);
    y = y + FONT_HGT_MEDIUM;
    if self.surfacesPossible and #self.surfacesPossible > 1 then
        ui:drawTextRight(getText("IGUI_Place3DItem_Surface", getKeyName(getCore():getKey("Toggle mode"))), screenX + screenW - 30, y, 1, 1, 1, 1, UIFont.NewMedium);
    end
end

function ISPlace3DItemCursor:getSurface(square)
    -- check if we have a surface, so we can do a z offset to make items goes on this surface
    -- we list all our possible surface height, allow Shift to switch thru them
--    local surface = 0
    self.surfacesPossible = {};
    for i=1,square:getObjects():size() do
        local object = square:getObjects():get(i-1);
        if object:getSurfaceOffsetNoTable() > 0 then
            -- the surface is in pixel, set for the 1X texture, so we *2 (192 pixels is 1X texture height)
            local newSurf = (object:getSurfaceOffsetNoTable() / 96);
            local add = true;
            for x,j in ipairs(self.surfacesPossible) do
                if j == newSurf then
                    add = false;
                    break;
                end
            end
            if add then
                table.insert(self.surfacesPossible, newSurf)
            end
--            if newSurf > surface then
--                surface = newSurf;
--            end
        end
    end
--    print(self.surfacesPossible[2])
--    print("total possible surface: ")
--    for x,j in ipairs(self.surfacesPossible) do
--        print(x, j)
--    end
    return self.surfacesPossible[self.surfaceSelected] or 0;
end

function ISPlace3DItemCursor:rotateDelta()
    return 0.75 * getGameTime():getMultiplier() / 1.6
end

function ISPlace3DItemCursor:clamp(rot)
    -- Clamp to 10-degree intervals
    return round(rot / 5, 0) * 5
end

function ISPlace3DItemCursor:checkSelectSurfaceKey() -- switch between our possible surface height when pressing Shift
    if self.surfaceKeyTimer then
        return;
    end
    if self.chr:getPlayerNum() ~= 0 then return end
    if self.chr:getJoypadBind() ~= -1 then return end
    local pressed = isKeyDown(getCore():getKey("Toggle mode"))
    if(pressed) then
        self.surfaceKeyTimer = 10;
        self.surfaceSelected = self.surfaceSelected + 1;
        if self.surfaceSelected > #self.surfacesPossible then
            self.surfaceSelected = 1;
        end
    end
end

function ISPlace3DItemCursor:checkRotateKey()
    if self.chr:getPlayerNum() ~= 0 then return end
    if self.chr:getJoypadBind() ~= -1 then return end
    local pressed = isKeyDown(getCore():getKey("Rotate building"))
    local reverse = isShiftKeyDown()
    self:handleRotate(pressed, reverse)
end

function ISPlace3DItemCursor:checkRotateJoypad()
    if self.chr:getJoypadBind() == -1 then return end
    if self.joypadPositionActive then
        return
    end
    -- FIXME: the way the model rotates depends on the up-axis
    local reverse = isJoypadLBPressed(self.chr:getJoypadBind())
    local pressed = isJoypadRBPressed(self.chr:getJoypadBind()) or reverse
    self:handleRotate(pressed, reverse)
end

function ISPlace3DItemCursor:handleRotate(pressed, reverse)
    if pressed then
        self.rotateReverse = reverse
        if not self.rotatePressed then
            self.rotatePressed = true
            self.rotateStart = getTimestampMs()
            return
        elseif getTimestampMs() - self.rotateStart > 250 then
            self.rotating = true
        else
            return
        end
        local rot = self.render3DItemRot;
        if reverse then
            rot = rot - 5 * self:rotateDelta();
        else
            rot = rot + 5 * self:rotateDelta();
        end
        if rot < 0 then
            rot = 360;
        end
        if rot > 360 then
            rot = 0;
        end
        self.render3DItemRot = rot;
    else
        if self.rotatePressed then
            self.rotatePressed = false
            local rot = self.render3DItemRot;
            if not self.rotating then
                 -- Quick tap/release rotates 10 degrees
                if self.rotateReverse then
                    rot = rot - 5;
                else
                    rot = rot + 5;
                end
                if rot < 0 then
                    rot = 360;
                end
                if rot > 360 then
                    rot = 0;
                end
            end
            self.render3DItemRot = self:clamp(rot)
        end
        self.rotating = false
    end
end

function ISPlace3DItemCursor:checkPositionJoypad()
    local joypadID = self.chr:getJoypadBind()
    if joypadID == -1 then return end
    self.joypadPositionActive = isJoypadPressed(joypadID, getJoypadYButton(joypadID))
    if self.joypadPositionActive then
        self.chr:setIgnoreMovement(true)
        local axisX = getJoypadMovementAxisX(joypadID)
        local axisY = getJoypadMovementAxisY(joypadID)
        local delta = UIManager.getMillisSinceLastRender() / 33.3 * 0.025
        self.joypadPositionX = PZMath.clampFloat(self.joypadPositionX + axisX * delta, 0.05, 0.95)
        self.joypadPositionY = PZMath.clampFloat(self.joypadPositionY + axisY * delta, 0.05, 0.95)
    else
        self.chr:setIgnoreMovement(false)
    end
end

function ISPlace3DItemCursor:onJoypadPressButton(joypadIndex, joypadData, button)
    if self.joypadPositionActive then
        if button == Joypad.RBumper then
            self.surfaceSelected = self.surfaceSelected + 1
            if self.surfaceSelected > #self.surfacesPossible then
                self.surfaceSelected = 1
            end
        end
        return
    end
    if button == Joypad.XButton then
        self.placeAll = not self.placeAll
    else
        ISBuildingObject.onJoypadPressButton(self, joypadIndex, joypadData, button)
    end
end

function ISPlace3DItemCursor:onJoypadDirDown(joypadData)
    if self.joypadPositionActive then
        self.joypadPositionY = PZMath.clampFloat(self.joypadPositionY + 0.05, 0.05, 0.95)
        return
    end
    return ISBuildingObject.onJoypadDirDown(self, joypadData)
end

function ISPlace3DItemCursor:onJoypadDirUp(joypadData)
    if self.joypadPositionActive then
        self.joypadPositionY = PZMath.clampFloat(self.joypadPositionY - 0.05, 0.05, 0.95)
        return
    end
    return ISBuildingObject.onJoypadDirUp(self, joypadData)
end

function ISPlace3DItemCursor:onJoypadDirRight(joypadData)
    if self.joypadPositionActive then
        self.joypadPositionX = PZMath.clampFloat(self.joypadPositionX + 0.05, 0.05, 0.95)
        return
    end
    return ISBuildingObject.onJoypadDirRight(self, joypadData)
end

function ISPlace3DItemCursor:onJoypadDirLeft(joypadData)
    if self.joypadPositionActive then
        self.joypadPositionX = PZMath.clampFloat(self.joypadPositionX - 0.05, 0.05, 0.95)
        return
    end
    return ISBuildingObject.onJoypadDirLeft(self, joypadData)
end

function ISPlace3DItemCursor:getAPrompt()
    if self.joypadPositionActive then
        return nil
    end
    return getText("ContextMenu_PlaceItemOnGround")
end

function ISPlace3DItemCursor:getBPrompt()
    if self.joypadPositionActive then
        return nil
    end
    return ISBuildingObject.getBPrompt(self)
end

function ISPlace3DItemCursor:getXPrompt()
    if self.joypadPositionActive then
        return nil
    end
    return getText(self.placeAll and "ContextMenu_PlaceAll" or "ContextMenu_PlaceOne")
end

function ISPlace3DItemCursor:getYPrompt()
    return getText("IGUI_Place3DItem_JoypadPosition")
end

function ISPlace3DItemCursor:getLBPrompt()
    if self.joypadPositionActive then
        return nil
    end
    return ISBuildingObject.getLBPrompt(self)
end

function ISPlace3DItemCursor:getRBPrompt()
    if self.joypadPositionActive then
        if #self.surfacesPossible <= 1 then return nil end
        return getText("IGUI_Place3DItem_JoypadSurface")
    end
    return ISBuildingObject.getRBPrompt(self)
end

function ISPlace3DItemCursor:onObjectLeftMouseButtonDown(object, x, y)
    -- Disable clicks on objects, to prevent the loot window being displayed
    return true
end

function ISPlace3DItemCursor:new(character, items)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o:init()
    o.chr = character
    o.character = character
    o.items = items;
    o.player = character:getPlayerNum()
    o.skipBuildAction = true
    o.noNeedHammer = false
    o.dragNilAfterPlace = true
    o.isYButtonResetCursor = false
    o.render3DItemRot = 0;
    o.surfaceSelected = 1;
    o.placeAll = false
    o.previousSq = nil;
    o.isPlace3DCursor = true;
    o.joypadPositionActive = false;
    o.joypadPositionX = 0.5;
    o.joypadPositionY = 0.5;
    return o
end

