---@var InteractionType :InteractionType
---@var InteractionRange :number = 1.5
---@var InteractionArea :UnityEngine.Collider
---@var InteractionMode :Interaction3DType
---@var InteractionButton :boolean
---@var ButtonPosition :InteractiveButtonType
---@var AdditiveButton :ParaButton
---@var HandButton :ParaButton
---@var PromptText :string
---@var Outline :boolean = true;
---@var OutlineColor :Color
---@var PickupType :PickupType
---@var LeftLocation :UnityEngine.Transform
---@var RightLocation :UnityEngine.Transform
---@var AllowDrop :boolean = true;
---@var AllowTheft :boolean
---@var MainButtonType :MainInteractButtonType
---@var IsShowMainButton :bool = true;
---@var MainButton :ParaButton
---@var MainJoyStick :ParaJoyStick
---@var WaveJoyStick :ParaJoyStick
---@var IsShowFirstSubButton : bool = false;
---@var FirstSubButton :ParaButton
---@var IsShowSecondSubButton : bool = false;
---@var SecondSubButton :ParaButton
---@end

local leftPickupInteractionButton = nil
local leftMainInteractionButton = nil
local leftThrowInteractionButton = nil
local rightPickupInteractionButton = nil
local rightMainInteractionButton = nil
local rightThrowInteractionButton = nil
local rightAssistInteractionButton = nil
local leftMainJoystick = nil
local leftWaveJoystick = nil
local leftWaveJoyStickBG = nil
local leftWaveJoyStickFG = nil
local rightMainJoystick = nil
local rightWaveJoystick = nil
local rightWaveJoyStickBG = nil
local rightWaveJoyStickFG = nil
local currentPlayer = nil
local currentPlayerGo = nil
local currentPlayerAnimator = nil
local currentPlayerBone = nil
local rigidbody = nil
local isKinematic = false
local isInteractable = false
local isFocus = false

function Awake()
    self.gameObject:SetLayer(CS.ParaLayerDefine.PropsLayer)

    rigidbody = self.transform:GetComponent(typeof(CS.UnityEngine.Rigidbody))
    if rigidbody ~= nil then
        isKinematic = rigidbody.isKinematic
    end

    if InteractionArea == nil then
        InteractionType = CS.InteractionType.Range
    end
    if InteractionRange <= 0 then
        InteractionRange = 1.5
    end
    if InteractionType == CS.InteractionType.Range then
        InteractionArea = self.gameObject:AddComponent(typeof(CS.UnityEngine.SphereCollider))
        InteractionArea.center = CS.UnityEngine.Vector3.zero
        InteractionArea.radius = InteractionRange
    end
    if InteractionArea ~= nil then
        InteractionArea.isTrigger = true
    end

    leftPickupInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Left, CS.UIType.Pickup)
    leftMainInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Left, CS.UIType.Main)
    leftThrowInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Left, CS.UIType.Throw)
    rightPickupInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Right, CS.UIType.Pickup)
    rightMainInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Right, CS.UIType.Main)
    rightThrowInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Right, CS.UIType.Throw)
    rightAssistInteractionButton = CS.ParaUIService.GetInteractionButton(CS.HandType.Right, CS.UIType.Assist)
    leftMainJoystick = CS.ParaUIService.GetJoyStickControl(CS.HandType.Left, CS.UIType.Main)
    leftWaveJoystick = CS.ParaUIService.GetJoyStickControl(CS.HandType.Left, CS.UIType.WaveJoyStick)
    if leftWaveJoystick ~= nil then
        if leftWaveJoystick.background ~= nil then
            leftWaveJoyStickBG = leftWaveJoystick.background.sprite
        end
        if leftWaveJoystick.frontground ~= nil then
            leftWaveJoyStickFG = leftWaveJoystick.frontground.sprite
        end
    end
    rightMainJoystick = CS.ParaUIService.GetJoyStickControl(CS.HandType.Right, CS.UIType.Main)
    rightWaveJoystick = CS.ParaUIService.GetJoyStickControl(CS.HandType.Right, CS.UIType.WaveJoyStick)
    if rightWaveJoystick ~= nil then
        if rightWaveJoystick.background ~= nil then
            rightWaveJoyStickBG = rightWaveJoystick.background.sprite
        end
        if rightWaveJoystick.frontground ~= nil then
            rightWaveJoyStickFG = rightWaveJoystick.frontground.sprite
        end
    end
    
    local image = CS.ParaUIService.GetDefaultIcon(CS.IconType.Pickup)
    InteractionButton = true
    ButtonPosition = CS.InteractiveButtonType.Hand
    HandButton.Interactable = true
    HandButton.SpriteState = 
    {
        normalImage = image,
        pressedImage = image,
        disabledImage = image,
    }
end

function OnInteractionButtonClick(handType) 
    if PickupType == CS.PickupType.Any then
        local localPlayer = CS.ParaPlayerService.GetLocalPlayer()
        if localPlayer ~= nil then
            localPlayer:Pickup(self.gameObject, handType, CS.UnityEngine.Matrix4x4.identity)
        end
    elseif PickupType == CS.PickupType.Custom then
        if handType == CS.HandType.Left then
            local localPlayer = CS.ParaPlayerService.GetLocalPlayer()
            if localPlayer ~= nil then
                if LeftLocation ~= nil then
                    localPlayer:Pickup(self.gameObject, handType, LeftLocation:LocalMatrix())
                else
                    localPlayer:Pickup(self.gameObject, handType, CS.UnityEngine.Matrix4x4.identity)
                end
            end
        elseif handType == CS.HandType.Right then
            local localPlayer = CS.ParaPlayerService.GetLocalPlayer()
            if localPlayer ~= nil then
                if RightLocation ~= nil then
                    localPlayer:Pickup(self.gameObject, handType, RightLocation:LocalMatrix())
                else
                    localPlayer:Pickup(self.gameObject, handType, CS.UnityEngine.Matrix4x4.identity)
                end
            end
        end
    end
end

function ShowJoystickControl(joystick, descriptor)
    if joystick == nil or descriptor == nil then
        return
    end
    if joystick.gameObject ~= nil then
        joystick.gameObject:SetActive(true)
    end
    if joystick.background ~= nil then
        joystick.background.gameObject:SetActive(true)
        joystick.background.sprite = descriptor.backgroundImage
    end
    if joystick.frontground ~= nil then
        joystick.frontground.gameObject:SetActive(true)
        joystick.frontground.sprite = descriptor.frontgroundImage
    end
end

function HideJoystickControl(joystick)
    if joystick == nil then
        return
    end
    if joystick.gameObject ~= nil then
        joystick.gameObject:SetActive(false)
    end
    if joystick.background ~= nil then
        joystick.background.gameObject:SetActive(false)
    end
    if joystick.frontground ~= nil then
        joystick.frontground.gameObject:SetActive(false)
    end
end

function ShowInteractionButton(btn, descriptor)
    if btn == nil or descriptor == nil then
        return
    end
    btn.gameObject:SetActive(true)
    btn.interactable = descriptor.Interactable
    btn.transition = CS.UnityEngine.UI.Selectable.Transition.SpriteSwap
    btn.image.sprite = descriptor.SpriteState.normalImage
    local spriteState = btn.spriteState;
    spriteState.pressedSprite = descriptor.SpriteState.pressedImage
    spriteState.disabledSprite = descriptor.SpriteState.disabledImage
    btn.spriteState = spriteState;
    
    if descriptor.ButtonText == true then
        local txt = btn:GetComponentInChildren(typeof(CS.UnityEngine.UI.Text), true)
        if txt ~= nil then
            txt.gameObject:SetActive(true)
            txt.text = descriptor.Text
            txt.fontSize = descriptor.TextSize
            txt.color = descriptor.TextColor
        end
    end
end

function HideInteractionButton(btn)
    if btn == nil then
        return
    end
    btn.gameObject:SetActive(false)
    local txt = btn:GetComponentInChildren(typeof(CS.UnityEngine.UI.Text), true)
    if txt ~= nil then
        txt.gameObject:SetActive(false)
    end
end

function EnableInteraction()
    if Outline == true then
        CS.ParaUtility.AddOutlineEffect(self.gameObject)
    end
    if PromptText ~= "" then
        CS.ParaUtility.Show3DTips(self.gameObject, PromptText)
    end
    self:EnableInteraction(ButtonPosition)
end

function DisableInteraction()
    if Outline == true then
        CS.ParaUtility.DeleteOutlineEffect(self.gameObject)
    end
    if PromptText ~= "" then
        CS.ParaUtility.Hide3DTips(self.gameObject)
    end
    self:DisableInteraction()
end

function OnPlayerTriggerEnter(player)
    if player.isLocal == false then
        return
    end
    isInteractable = true
    if currentPlayerGo == nil then
        EnableInteraction()
    elseif currentPlayer ~= nil and AllowTheft == true then
        if currentPlayer.isLocal == false then
            EnableInteraction()
        end
    end
end

function OnPlayerTriggerExit(player)
    if player.isLocal == false then
        return
    end
    isInteractable = false
    DisableInteraction()
end

function OnPlayerLeft(player)
    if player.isLocal == false then
        return
    end
    isInteractable = false
    DisableInteraction()
end

function OnDisable()
    if isInteractable == true then
        isInteractable = false
        DisableInteraction()
    end
end

function OnFocus(handType)
    isFocus = true
    if InteractionButton == true then
        if ButtonPosition == CS.InteractiveButtonType.Hand then
            if handType == CS.HandType.Left then
                ShowInteractionButton(leftPickupInteractionButton, HandButton)
            elseif handType == CS.HandType.Right then
                ShowInteractionButton(rightPickupInteractionButton, HandButton)
            end
        elseif ButtonPosition == CS.InteractiveButtonType.Additive then
            ShowInteractionButton(rightAssistInteractionButton, AdditiveButton)
        end
    end
end

function OnLostFocus(handType)
    isFocus = false
    if InteractionButton == true then
        if ButtonPosition == CS.InteractiveButtonType.Hand then
            if handType == CS.HandType.Left then
                HideInteractionButton(leftPickupInteractionButton)
            elseif handType == CS.HandType.Right then
                HideInteractionButton(rightPickupInteractionButton)
            end
        elseif ButtonPosition == CS.InteractiveButtonType.Additive then
            HideInteractionButton(rightAssistInteractionButton)
        end
    end
end

function OnOwnershipRequest(playerId)
    return true;
end

function OnPickup(player, handType, pickUpPoint)
    if player ~= nil then
        DisablePhysical()
        currentPlayer = player
        currentPlayerGo = currentPlayer.gameObject
        currentPlayerAnimator = currentPlayerGo:GetComponentInChildren(typeof(CS.UnityEngine.Animator))
        if currentPlayerAnimator ~= nil then
            currentPlayerBone = nil
            if handType == CS.HandType.Left then
                currentPlayerBone = currentPlayerAnimator:GetBoneTransform(CS.UnityEngine.HumanBodyBones.LeftHand)
            elseif handType == CS.HandType.Right then
                currentPlayerBone = currentPlayerAnimator:GetBoneTransform(CS.UnityEngine.HumanBodyBones.RightHand)
            end
            if currentPlayerBone ~= nil then
                self.transform:SetParent(currentPlayerBone)
            end
            UpdateTransform(handType, pickUpPoint.inverse)
        end
    end
    OnPickupImp(handType)
end

function OnPlayerDrop(player, handType)
    if player.gameObject ~= nil and player == currentPlayer then
        OnDropImp(handType)
        self.transform:SetParent(nil)
        EnablePhysical()
        currentPlayer = nil
        currentPlayerGo = nil
        currentPlayerAnimator = nil
    end
    if currentPlayer == nil and isInteractable == true then
        EnableInteraction()
    end
end

function On3DClick(handType, clickPoint)
    if PickupType == CS.PickupType.Any then
        local localPlayer = CS.ParaPlayerService.GetLocalPlayer()
        if localPlayer ~= nil then
            localPlayer:Pickup(self.gameObject, handType, clickPoint)
        end
    elseif PickupType == CS.PickupType.Custom then
        if handType == CS.HandType.Left then
            local localPlayer = CS.ParaPlayerService.GetLocalPlayer()
            if localPlayer ~= nil then
                if LeftLocation ~= nil then
                    localPlayer:Pickup(self.gameObject, handType, LeftLocation:LocalMatrix())
                else
                    localPlayer:Pickup(self.gameObject, handType, CS.UnityEngine.Matrix4x4.identity)
                end
            end
        elseif handType == CS.HandType.Right then
            local localPlayer = CS.ParaPlayerService.GetLocalPlayer()
            if localPlayer ~= nil then
                if RightLocation ~= nil then
                    localPlayer:Pickup(self.gameObject, handType, RightLocation:LocalMatrix())
                else
                    localPlayer:Pickup(self.gameObject, handType, CS.UnityEngine.Matrix4x4.identity)
                end
            end
        end
    end
end

function UpdateTransform(HandType, pickupMatrix)
    local palmPosition = CS.UnityEngine.Vector3.zero
    local palmRotation = CS.UnityEngine.Quaternion.identity
    local palmWorldMatrix = CS.UnityEngine.Matrix4x4.identity
    if HandType == CS.HandType.Left then
        palmPosition, palmRotation = currentPlayerAnimator:CalcPalmTransform(true)
        palmWorldMatrix = CS.UnityEngine.Matrix4x4.TRS(palmPosition, palmRotation, CS.UnityEngine.Vector3.one);
    elseif HandType == CS.HandType.Right then
        palmPosition, palmRotation = currentPlayerAnimator:CalcPalmTransform(false)
        palmWorldMatrix = CS.UnityEngine.Matrix4x4.TRS(palmPosition, palmRotation, CS.UnityEngine.Vector3.one);
        local rotation = CS.UnityEngine.Quaternion.AngleAxis(180, CS.UnityEngine.Vector3.up);
        local rightPalmMatrix = CS.UnityEngine.Matrix4x4.Rotate(rotation)
        palmWorldMatrix = palmWorldMatrix * rightPalmMatrix
    end
    local palmMatrix = palmWorldMatrix * pickupMatrix
    self.transform.position = palmMatrix:GetPosition()
    self.transform.rotation = palmMatrix.rotation
end

function EnablePhysical()
    if rigidbody ~= nil and isKinematic ~= nil then
        rigidbody.isKinematic = isKinematic
    end
end

function DisablePhysical()
    if rigidbody ~= nil then
        rigidbody.isKinematic = true
    end
end

function OnPickupImp(hand)
    if currentPlayer ~= nil and currentPlayer.isLocal == false then
         if AllowTheft == false then
            DisableInteraction()
        end
        return
    end
    
    DisableInteraction()
    
    if MainButtonType == CS.MainInteractButtonType.Button then
        if hand == CS.HandType.Left then
            leftThrowInteractionButton.gameObject:SetActive(AllowDrop)
            if IsShowMainButton == true then
                ShowInteractionButton(leftMainInteractionButton, MainButton)
            end
        elseif hand == CS.HandType.Right then
            rightThrowInteractionButton.gameObject:SetActive(AllowDrop)
            if IsShowMainButton == true then
                ShowInteractionButton(rightMainInteractionButton, MainButton)
            end
        end
    elseif MainButtonType == CS.MainInteractButtonType.Stick then
        if hand == CS.HandType.Left then
            leftThrowInteractionButton.gameObject:SetActive(AllowDrop)
            ShowJoystickControl(leftMainJoystick, MainJoyStick)
        elseif hand == CS.HandType.Right then
            rightThrowInteractionButton.gameObject:SetActive(AllowDrop)
            ShowJoystickControl(rightMainJoystick, MainJoyStick)
        end
    end

    if WaveJoyStick ~= nil then
        if WaveJoyStick.backgroundImage ~= nil or WaveJoyStick.frontgroundImage ~= nil then
            if hand == CS.HandType.Left then
                ShowJoystickControl(leftWaveJoystick, WaveJoyStick)
            elseif hand == CS.HandType.Right then
                ShowJoystickControl(rightWaveJoystick, WaveJoyStick)
            end
        end
    end

    if IsShowFirstSubButton == true then
        local interactionButton1 = CS.ParaUIService.GetInteractionButton(hand, CS.UIType.SubFirst)
        if interactionButton1 ~= nil then
            ShowInteractionButton(interactionButton1, FirstSubButton)
        end
    end
    if IsShowSecondSubButton == true then
        local interactionButton2 = CS.ParaUIService.GetInteractionButton(hand, CS.UIType.SubSecond)
        if interactionButton2 ~= nil then
            ShowInteractionButton(interactionButton2, SecondSubButton)
        end
    end
end

function OnDropImp(hand)
    if currentPlayer ~= nil and currentPlayer.isLocal == false then
        return
    end
    if MainButtonType == CS.MainInteractButtonType.Button then
        if hand == CS.HandType.Left then
            leftThrowInteractionButton.gameObject:SetActive(false)
            HideInteractionButton(leftMainInteractionButton)
        elseif hand == CS.HandType.Right then
            rightThrowInteractionButton.gameObject:SetActive(false)
            HideInteractionButton(rightMainInteractionButton)
        end
    elseif MainButtonType == CS.MainInteractButtonType.Stick then
        if hand == CS.HandType.Left then
            leftThrowInteractionButton.gameObject:SetActive(false)
            HideJoystickControl(leftMainJoystick)
        elseif hand == CS.HandType.Right then
            rightThrowInteractionButton.gameObject:SetActive(false)
            HideJoystickControl(rightMainJoystick)
        end
    end

    if WaveJoyStick ~= nil then
        if WaveJoyStick.backgroundImage ~= nil or WaveJoyStick.frontgroundImage ~= nil then
            if hand == CS.HandType.Left then
                if leftWaveJoystick ~= nil then
                    if leftWaveJoystick.background ~= nil then
                        leftWaveJoystick.background.sprite = leftWaveJoyStickBG
                    end
                    if leftWaveJoystick.frontground ~= nil then
                        leftWaveJoystick.frontground.sprite = leftWaveJoyStickFG
                    end
                end
            elseif hand == CS.HandType.Right then
                if rightWaveJoystick ~= nil then
                    if rightWaveJoystick.background ~= nil then
                        rightWaveJoystick.background.sprite = rightWaveJoyStickBG
                    end
                    if rightWaveJoystick.frontground ~= nil then
                        rightWaveJoystick.frontground.sprite = rightWaveJoyStickFG
                    end
                end
            end
        end
    end

    local interactionButton1 = CS.ParaUIService.GetInteractionButton(hand, CS.UIType.SubFirst)
    if interactionButton1 ~= nil then
        HideInteractionButton(interactionButton1)
    end
    local interactionButton2 = CS.ParaUIService.GetInteractionButton(hand, CS.UIType.SubSecond)
    if interactionButton2 ~= nil then
        HideInteractionButton(interactionButton2)
    end
end

function OnUIPointerClick(uiType, handType)
    if uiType == CS.UIType.Pickup then
        if currentPlayer == nil then
            OnInteractionButtonClick(handType)
        else
            if currentPlayer.isLocal == false then
                OnInteractionButtonClick(handType)
            end
        end
    elseif uiType == CS.UIType.Throw then
        if currentPlayer ~= nil then
            currentPlayer:Drop(self.gameObject)
        end
    end
    CallAdditveButtonActions(uiType);
end


function CallAdditveButtonActions(uiType)
    local ButtonActions = nil;
    if uiType == CS.UIType.Main then
        ButtonActions = MainButton.ButtonActions;
    elseif uiType == CS.UIType.SubFirst then
        ButtonActions = FirstSubButton.ButtonActions;
    elseif uiType == CS.UIType.SubSecond then  
        ButtonActions = SecondSubButton.ButtonActions;
    end
    if ButtonActions and ButtonActions.Length > 0 then
        for i=0,ButtonActions.Length - 1 do
            local item = ButtonActions[i];
            if item.Data then
                local pfn = item.Data.script[item.methodName];
                if pfn then
                    pfn();
                end
            end 
        end
    end
end

