using UnityEngine;

using RTSEngine.BuildingExtension;
using RTSEngine.Entities;
using RTSEngine.Logging;
using RTSEngine.Mobile.Utilities;
using RTSEngine.Selection;
using RTSEngine.Utilities;

namespace RTSEngine.Mobile.BuildingExtension
{
    public class AdvancedLocalMobileFactionPlacementHandler : AdvancedBuildingPlacementHandlerBase
    {
        #region Attributes
        // is the player actively dragging and dropping a placement instance for movement
        private bool isMoving;

        [SerializeField, Tooltip("Enable to allow the player to rotate buildings while placing them.")]
        private bool canRotate = true;
        private bool isRotating;
        private bool isRotationPositive;
        [SerializeField, Tooltip("How fast would the building rotate?")]
        private float rotationSpeed = 1f;

        private RaycastHitter selectionRaycast;
        private RaycastHitter placementRaycast;

        [SerializeField, Tooltip("Allow double tapping on a building placement instance to place it?")]
        private bool doubleTapOnBuildingCompletePlacement = true;
        private TouchMultiTap completePlacementDoubleTap;

        [SerializeField, Tooltip("Allow double tapping outside of a building placement instance to place it?")]
        private bool doubleTapOutsideBuildingStopPlacement = true;
        private TouchMultiTap stopPlacementDoubleTap;

        // double tap detection to start segmnetation placement
        private TouchMultiTap segmentationDoubleTap;
        #endregion

        #region Initializing/Terminating
        protected override void OnInit()
        {
            isMoving = false;
            isRotating = false;

            selectionRaycast = new RaycastHitter(selector.EntitySelectionLayerMask);
            placementRaycast = new RaycastHitter(placerMgr.PlacableLayerMask);

            stopPlacementDoubleTap = new TouchMultiTap(targetTapCount: 2);
            completePlacementDoubleTap = new TouchMultiTap(targetTapCount: 2);

            segmentationDoubleTap = new TouchMultiTap(targetTapCount: 2, tapCapturePhase: TouchPhase.Began);
        }
        #endregion

        #region Update
        protected override void OnInactiveUpdate()
        {
        }

        protected override void OnActiveUpdate()
        {
            if(!SegmentationEnabled
                && doubleTapOutsideBuildingStopPlacement
                && stopPlacementDoubleTap.Update(OnFirstInstanceTap, conditionFulfilled: false))
            {
                Stop();
                return;
            }

            MoveBuilding();

            RotateBuilding();

            // Activate drag and drop for current building placement?
            if (SegmentationEnabled && !SegmentationActive
                && current.firstInstance.PlacerComponent.SegmentData.enabled
                && segmentationDoubleTap.Update(OnFirstInstanceTap))
            {
                EnableSegmentation();
                mainCameraController.PanningHandler.IsActive = false;
            }

            if(!SegmentationEnabled
                && doubleTapOnBuildingCompletePlacement
                && completePlacementDoubleTap.Update(OnFirstInstanceTap))
            {
                CompleteCurrentPlacement();
            }
        }
        #endregion

        #region Handling Movement
        private void EnableMovement()
        {
            isMoving = true;
            mainCameraController.PanningHandler.IsActive = false;
        }

        private void DisableMovement()
        {
            isMoving = false;
            mainCameraController.PanningHandler.IsActive = true;
        }

        private void MoveBuilding()
        {
            if (Input.touchCount == 1)
            {
                Touch touch = Input.GetTouch(0);

                switch (touch.phase)
                {
                    case TouchPhase.Began:
                        if (OnFirstInstanceTap(touch.position))
                        {
                            EnableMovement();
                        }
                        break;

                    case TouchPhase.Moved:
                        if (!isMoving)
                            break;

                        if (placementRaycast.Hit(
                            mainCameraController.MainCamera.ScreenPointToRay(Input.mousePosition),
                            out RaycastHit hit))
                        {
                            Vector3 nextBuildingPos = hit.point;

                            // Make sure that the building position on the y axis stays inside the min and max height interval
                            nextBuildingPos.y += placerMgr.BuildingPositionYOffset;

                            if (SegmentationActive)
                            {
                                // Segmentation mode active
                                OnSegmentationUpdate(finalPosition: nextBuildingPos);
                            }
                            else
                            {
                                if (IsGridPlacementActive)
                                {
                                    placerMgr.GridHandler.TryGetCellPosition(nextBuildingPos, out nextBuildingPos);
                                }

                                current.firstInstance.PlacerComponent.OnPlacementUpdate(nextBuildingPos);
                            }
                        }
                        break;

                    case TouchPhase.Ended:
                        DisableMovement();

                        if (SegmentationActive)
                        {
                            CompleteCurrentPlacement();
                            mainCameraController.PanningHandler.IsActive = true;
                        }
                        break;
                }
            }
            else
            {
                DisableMovement();

                if (SegmentationActive)
                    Stop();
            }
        }
        #endregion

        #region Handling Rotation
        public void EnableRotation(bool positive)
        {
            // For grid placement, only allow for 4 direction rotations
            if (IsGridPlacementActive)
            {
                current.firstInstance.transform.Rotate(new Vector3(0.0f, rotationSpeed * (positive ? 1.0f : -1.0f) * 90.0f, 0.0f));
            }
            else
            {
                isRotating = true;
                isRotationPositive = positive;
            }
        }

        public void DisableRotation()
        {
            isRotating = false;
        }

        public void RotateBuilding()
        {
            if (!canRotate
                || SegmentationActive
                || IsGridPlacementActive
                || !isRotating
                || current.mainOptions.disableRotation)
                return;

            Vector3 nextEulerAngles = current.firstInstance.transform.rotation.eulerAngles;
            nextEulerAngles.y += rotationSpeed * rotationSpeed * (isRotationPositive ? 1.0f : -1.0f);
            current.firstInstance.transform.rotation = Quaternion.Euler(nextEulerAngles);
        }
        #endregion

        #region Adding
        protected override void OnAdded(ErrorMessage errorMsg, IBuilding placementInstance, IBuildingPlacementTask task, BuildingPlacementOptions options)
        {
            if (errorMsg != ErrorMessage.none)
                return;

            placementInstance.gameObject.SetActive(false);
        }
        #endregion

        #region Starting
        protected override void OnStart(IBuilding placementInstance, BuildingPlacementOptions options = default)
        {
            placementInstance.gameObject.SetActive(true);
            Vector3 nextBuildingPos = placementInstance.transform.position;

            bool initialPositionFound = false;
            Vector3 originalHitPosition = Vector3.zero;

            // Only if this is a new instance/segment, will we set its position on start.
            if (!options.isReplacingExisting)
            {
                // Set the position of the new building (and make sure it's on the terrain)
                if (placementRaycast.Hit(
                    mainCameraController.MainCamera.ScreenPointToRay(RTSHelper.MiddleScreenPoint),
                    out RaycastHit hit))
                {
                    originalHitPosition = hit.point;

                    // radius set to Math.Infinity will probably cause a huge FPS drop if no valid positions can be found fast
                    // to be changed
                    if (mvtMgr.MvtSystem.TryGetValidPosition(hit.point, Mathf.Infinity, placementInstance.PlacerComponent.PlacableNavigationMask, out nextBuildingPos)
                        && terrainMgr.TryGetCachedHeight(nextBuildingPos, placementInstance.PlacerComponent.PlacableTerrainAreas, out float terrainHeight) == ErrorMessage.none)
                    {
                        nextBuildingPos.y = terrainHeight + placerMgr.BuildingPositionYOffset;

                        if (IsGridPlacementActive)
                        {
                            placerMgr.GridHandler.TryGetCellPosition(nextBuildingPos, out nextBuildingPos);
                        }

                        initialPositionFound = true;
                    }
                }
            }

            if (options.isReplacingExisting || initialPositionFound)
            {
                placementInstance.PlacerComponent.OnPlacementUpdate(nextBuildingPos);
                if(!options.isReplacingExisting && Vector3.Distance(originalHitPosition, nextBuildingPos) > RTSMobileHelper.MinHitInitialPositionLookAtRange)
                    mainCamCtrl.PanningHandler.LookAt(nextBuildingPos, smooth: false);
            }
            else
            {
                // Unable to find suitable starting position for building on map
                Stop();
            }
        }
        #endregion

        #region Stopping
        protected override void OnStop(AdvancedPendingPlacementData removedPlacement)
        {
        }
        #endregion

        #region Complete
        // To be called from a UI element click to attempt completing a placement
        public void CompleteCurrentPlacement()
        {
            AttemptComplete();
        }

        protected override void OnComplete(ErrorMessage errorMsg, CompletedPlacementData completedPlacement)
        {
            if (errorMsg != ErrorMessage.none)
            {
                playerMsgHandler.OnErrorMessage(new PlayerErrorMessageWrapper
                {
                    message = errorMsg,

                    source = current.firstInstance
                });

                return;
            }
        }
        #endregion

        #region Helper Functions
        private bool OnFirstInstanceTap(Vector3 touchPosition)
        {
            if (selectionRaycast.Hit(mainCameraController.ScreenPointToRay(touchPosition), out RaycastHit hit))
            {
                IEntity hitEntity = hit.transform.gameObject.GetComponent<EntitySelectionCollider>()?.Entity;

                if (hitEntity.IsValid() && hitEntity == current.firstInstance)
                {
                    return true;
                }
            }

            return false;
        }
        #endregion
    }
}
