using UnityEngine;
using System;
using System.Collections.Generic;

namespace mgaia.Utils
{
    /// <summary>
    /// Behaviour utilizado para ancorar um elemento nos cantos, mudando o posicionamento para cada resolução
    /// </summary>
    /// <owner>Victor</owner>
    [ExecuteInEditMode()]
    public class AnchoredPositioningBehaviour : MonoBehaviour
    {
        public ExternalXAnchor ReferenceAnchorForX;
        public ExternalYAnchor ReferenceAnchorForY;
        public InternalXAnchor InternalAnchorForX = InternalXAnchor.Center;
        public InternalYAnchor InternalAnchorForY = InternalYAnchor.Middle;
        public Vector2 Offset;
        public Camera Camera;

        private Camera mCamera;

        /// <summary>
        /// Retorna a câmera que está desenhando esse elemento
        /// </summary>
        private Camera CurrentCamera
        {
            get
            {
                return mCamera;
            }
        }

        /// <summary>
        /// Procura pela câmera está desenhando esse elemento
        /// </summary>
        public void RefreshCamera()
        {
            if (Camera != null)
            {
                mCamera = Camera;
            }
            else
            {
                Transform currentTransform = transform;
                while (currentTransform != null)
                {
                    Camera camera = currentTransform.GetComponent<Camera>();
                    if (camera != null)
                    {
                        mCamera = camera;

                        return;
                    }
                    else
                    {
                        currentTransform = currentTransform.parent;
                    }
                }

                mCamera = Camera.main;
            }
        }

        private void Awake()
        {
            Commit ();
        }

        private void OnValidate()
        {
            Commit ();
        }

		/// <summary>
		/// Posiciona o elemento segundo as configurações
		/// </summary>
		public void Commit()
		{
            RefreshCamera();

            Vector2 newPosition = GetNewPosition();

            transform.localPosition = new Vector3(newPosition.x, newPosition.y, transform.localPosition.z);
		}

        /// <summary>
        /// Calcula a posição em que o elemento tem que ficar
        /// </summary>
        private Vector2 GetNewPosition()
        {
            Vector3 cameraSize = CurrentCamera.GetOrtographicDrawingSize();

            float xPosition = transform.localPosition.x;
            float yPosition = transform.localPosition.y;

            Vector3 objectSize = gameObject.GetSize(true, true);

            if (ReferenceAnchorForX != ExternalXAnchor.Free)
            {
                xPosition = GetPositionForCoordinateForX(cameraSize.x, ReferenceAnchorForX);
                CompensateInternalAnchorForX(ref xPosition, objectSize.x, InternalAnchorForX);
                CompensateOffset(Coordinate2D.X, ref xPosition, Offset.x);
            }

            if (ReferenceAnchorForY != ExternalYAnchor.Free)
            {
                yPosition = GetPositionForCoordinateForY(cameraSize.y, ReferenceAnchorForY);
                CompensateInternalAnchorForY(ref yPosition, objectSize.y, InternalAnchorForY);
                CompensateOffset(Coordinate2D.Y, ref yPosition, Offset.y);
            }

            return new Vector2(xPosition, yPosition);
        }

        /// <summary>
        /// Compensa a posição X do objeto de acordo com a âncora interna dele
        /// </summary>
        protected virtual float CompensateInternalAnchorForX(ref float currentObjectCoordinate, float objectDimension, InternalXAnchor internalAnchor)
        {
            switch (internalAnchor)
            {
                case InternalXAnchor.Left:
                    currentObjectCoordinate += objectDimension / 2;
                    break;
                case InternalXAnchor.Right:
                    currentObjectCoordinate -= objectDimension / 2;
                    break;
            }

            return currentObjectCoordinate;
        }

        /// <summary>
        /// Compensa a posição Y do objeto de acordo com a âncora interna dele
        /// </summary>
        protected virtual float CompensateInternalAnchorForY(ref float currentObjectCoordinate, float objectDimension, InternalYAnchor internalAnchor)
        {
            switch (internalAnchor)
            {
                case InternalYAnchor.Top:
                    currentObjectCoordinate -= objectDimension / 2;
                    break;
                case InternalYAnchor.Bottom:
                    currentObjectCoordinate += objectDimension / 2;
                    break;
            }

            return currentObjectCoordinate;
        }

        /// <summary>
        /// Calcula a nova posição X de acordo com a ancora e a coordenada
        /// </summary>
        protected virtual float GetPositionForCoordinateForX(float cameraSize, ExternalXAnchor anchorType)
        {
            float position = default(float);

            switch (anchorType)
            {
                case ExternalXAnchor.Left:
                    position = -cameraSize / 2;
                    break;
                case ExternalXAnchor.Right:
                    position = cameraSize / 2;
                    break;
            }

            return position;
        }

        /// <summary>
        /// Calcula a nova posição X de acordo com a ancora e a coordenada
        /// </summary>
        protected virtual float GetPositionForCoordinateForY(float cameraSize, ExternalYAnchor anchorType)
        {
            float position = default(float);

            switch (anchorType)
            {
                case ExternalYAnchor.Bottom:
                    position = -cameraSize / 2;
                    break;
                case ExternalYAnchor.Top:
                    position = cameraSize / 2;
                    break;
            }

            return position;
        }

        /// <summary>
        /// Adiciona o offset
        /// </summary>
        protected virtual void CompensateOffset(Coordinate2D coordinate, ref float position, float offset)
        {
            position += offset;
        }
    }
}