﻿using UnityEngine;
using System.Collections;
using UnityEditor;
using GAF.Assets;
using GAF.Objects;
using System.Collections.Generic;
using GAF.Core;

namespace GAFEditor.Objects
{
    public class GAFCustomSelectionWindow : EditorWindow
    {
		private enum CapturedSide
		{	
			  None
			, TopLeft
			, Top
			, TopRight
			, Right
			, BottomRight
			, Bottom
			, BottomLeft
			, Left
		}

        private Vector2			m_ScrollPosition;
        private Vector2			m_WindowSize;
        private Texture2D		m_Background;
        private GUIStyle		m_SelectionRectStyle;
		private GUIStyle		m_BorderStyle;
		private float			m_TexturePositionX = 0f;
		private float			m_TexturePositionY = 0f;

        private Vector2			m_StartMousePosition;
        private Vector2			m_CurrentMousePosition;
        private Rect			m_SelectionRect;
        private bool			m_RectDrawn;
		private CapturedSide	m_CapturedSide;

		private Rect m_TextureRect;
        private Rect m_TopLeftRect;
        private Rect m_TopRect;
        private Rect m_TopRightRect;
        private Rect m_RightRect;
        private Rect m_BottomRightRect;
        private Rect m_BottomRect;
        private Rect m_BottomLeftRect;
        private Rect m_LeftRect;

        private int m_Left      = 0;
        private int m_Top       = 0;
        private int m_Right     = 0;
        private int m_Bottom    = 0;

        private static List<GAFObject>	m_Objects			= null;
		private static SerializedObject m_SerializedObject	= null;

        private bool		m_IsDragging			= false;
		private bool		m_OutOfTextureBounds	= false;
        private float		m_ResizeRectSize		= 6f;
        private Texture2D	m_Atlas					= null;
		private Color		m_BorderColor			= new Color(1, 1, 1, 1);

        private Rect selectionInfoRect
        {
            get
            {
                return new Rect(this.position.width - 280f, this.position.height - 130f, 280f, 130f);
            }
        }

		private bool verticalResize
		{
 			get
			{
				return (m_CapturedSide == CapturedSide.Bottom || m_CapturedSide == CapturedSide.Top);
			}
		}

		private bool horizontalResize
		{
			get
			{
				return (m_CapturedSide == CapturedSide.Left || m_CapturedSide == CapturedSide.Right);
			}
		}
        
        internal static GAFCustomSelectionWindow GetWindow(List<GAFObject> _Objects, SerializedObject _SerializedObject)
        {
            m_Objects			= _Objects;
			m_SerializedObject	= _SerializedObject;
            return EditorWindow.GetWindow<GAFCustomSelectionWindow>(false, "", true);
        }

        private void OnEnable()
        {
            minSize = new Vector2(400, 300);

            float windowPositionX = (float)Screen.currentResolution.width / 2 - minSize.x / 2;
            float windowPositionY = (float)Screen.currentResolution.height / 2 - minSize.y / 2;

            position = new Rect(windowPositionX, windowPositionY, minSize.x, minSize.y);

			var properties				= m_SerializedObject.FindProperty("m_Data");
			var atlasElementId			= properties.FindPropertyRelative("m_AtlasElementID").intValue;
			var material				= properties.FindPropertyRelative("m_Material").objectReferenceValue as Material;
			var clip					= properties.FindPropertyRelative("m_Clip").objectReferenceValue as GAFBaseMovieClip;
			var atlasData				= clip.asset.getAtlases(clip.timelineID).Find(atlas => atlas.scale == clip.settings.scale);
			var atlasElementData		= atlasData.getElement((uint)atlasElementId);
			var texturesData			= atlasData.getAtlas(atlasElementData.atlasID);
			var hasCustomTexture		= material != null;
			var atlasTexture			= hasCustomTexture ? material.mainTexture as Texture2D :
											clip.resource.getTexture(System.IO.Path.GetFileNameWithoutExtension(texturesData.getFileName(clip.settings.csf)));

			m_Atlas = atlasTexture;

            m_SelectionRectStyle    = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle("selectionRect");
            m_WindowSize            = new Vector2(position.width, position.height);

            m_Background = new Texture2D((int)m_WindowSize.x, (int)m_WindowSize.y, TextureFormat.ARGB32, false);
            for (int i = 0; i < m_Background.width; i++)
            {
                for (int j = 0; j < m_Background.height; j++)
                {
                    m_Background.SetPixel(i, j, new Color());
                }
            }
            m_Background.Apply();

			m_BorderStyle = new GUIStyle(GUI.skin.box);
			m_BorderStyle.normal.background = EditorGUIUtility.whiteTexture;
			m_BorderStyle.border = new RectOffset(2, 2, 2, 2);

			m_TextureRect = new Rect(m_TexturePositionX, m_TexturePositionY, m_Atlas.width, m_Atlas.height);

			Debug.Log("Rect drawn: " + m_RectDrawn);
        }

        private void OnGUI()
        {
			EditorGUI.DrawTextureTransparent(new Rect(0, 0, position.width, position.height), m_Background);

			m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
			{
				GUI.DrawTexture(m_TextureRect, m_Atlas, ScaleMode.ScaleToFit, true);
			}
			EditorGUILayout.EndScrollView();

			if (!m_RectDrawn)
			{
				drawSelectionRect(m_TextureRect);
			}
			else
			{
				drawSelectedRect(m_TextureRect);
			}

			if (Event.current.type == EventType.Layout)
			{
				//drawInfoPanel();
			}

			if (Event.current.type == EventType.Repaint)
			{
				m_SelectionRectStyle.Draw(m_SelectionRect, "", false, false, false, false);
				setResizeRects();
				//drawInfoPanel();
			}
			if (Event.current.keyCode == KeyCode.Escape)
			{
				reset(true);
			}

			var color = Handles.color;
			Handles.color = m_BorderColor;
			Handles.DrawLine(Vector3.one, new Vector3(m_Atlas.width, 0));
			Handles.DrawLine(new Vector3(m_Atlas.width, 0), new Vector3(m_Atlas.width, m_Atlas.height));
			Handles.DrawLine(new Vector3(m_Atlas.width, m_Atlas.height), new Vector3(0, m_Atlas.height));
			Handles.DrawLine(new Vector3(0, m_Atlas.height), Vector3.one);
			Handles.color = color;

			Repaint();

			drawInfoPanel();
		}

		private void drawInfoPanel()
		{
			GUILayout.BeginArea(this.selectionInfoRect);
			{
				GUILayout.BeginVertical(new GUIContent("Settings"), GUI.skin.window, new GUILayoutOption[0]);
				{
					GUI.enabled = m_RectDrawn;

					m_SelectionRect = EditorGUILayout.RectField("Selected region: ", m_SelectionRect);

					EditorGUIUtility.wideMode = true;
					GUILayout.Space(5f);

					GUILayout.BeginHorizontal();
					{
						GUILayout.FlexibleSpace();
						if (GUILayout.Button("Commit", GUILayout.Width(100f)))
						{
							foreach (var obj in m_Objects)
							{
								obj.serializedProperties.atlasTextureRect = m_SelectionRect;
								obj.serializedProperties.clip.reload();
							}

							reset(true);
							System.GC.Collect();
							Close();
						}

						GUILayout.Space(5f);
						if (GUILayout.Button("Cancel", GUILayout.Width(100f)))
						{
							reset(true);
						}

						GUILayout.FlexibleSpace();
					}
					GUILayout.EndHorizontal();

					GUI.enabled = true;

					GUILayout.Space(6f);
					m_BorderColor = EditorGUILayout.ColorField("Border color: ", m_BorderColor);
				}
				GUILayout.EndVertical();
			}
			GUILayout.EndArea();
		}

		private void drawSelectionRect(Rect _TextureRect)
		{
			var currentEvent = Event.current;

			m_OutOfTextureBounds = !_TextureRect.Contains(currentEvent.mousePosition);

			switch (currentEvent.type)
			{
				case EventType.MouseDown:
					if (currentEvent.button == 0)
					{
						if (!m_IsDragging)
						{
							m_IsDragging = true;

							m_Left = 0;
							m_Top = 0;
							m_Right = 0;
							m_Bottom = 0;

							m_StartMousePosition = currentEvent.mousePosition;
							m_CurrentMousePosition = currentEvent.mousePosition;
							m_SelectionRect = new Rect();
						}
					}
					break;

				case EventType.MouseDrag:
					drag(currentEvent);
					break;

				case EventType.MouseUp:
					if (m_IsDragging)
					{
						if (m_SelectionRect.width == 0f || m_SelectionRect.height == 0f)
						{
							reset(true);
							break;
						}

						m_RectDrawn = true;
					}
					reset(false);
					break;

				default:
					break;
			}
        }

		private void drawSelectedRect(Rect _TextureRect)
		{
			var currentEvent = Event.current;

			switch (currentEvent.type)
			{
				case EventType.MouseDown:
					if (currentEvent.button == 0)
					{
						if (!m_IsDragging)
						{
							if (!checkRect(currentEvent.mousePosition))
							{
								return;
							}

							m_IsDragging = true;
						}
					}
					break;

				case EventType.MouseDrag:
					resizeDrawnRect(currentEvent);
					break;

				case EventType.MouseUp:
					if (m_IsDragging)
						reset(false);
					break;

				default:
					break;
			}
		}

		private void reset(bool _IsHard)
		{
			m_IsDragging			= false;
			m_OutOfTextureBounds	= false;
			m_CapturedSide			= CapturedSide.None;

			if (m_CurrentMousePosition.y < m_StartMousePosition.y)
			{
				var startPosition		= m_StartMousePosition;
				m_StartMousePosition	= new Vector2(m_StartMousePosition.x, m_CurrentMousePosition.y);
				m_CurrentMousePosition	= new Vector2(m_CurrentMousePosition.x, startPosition.y);
			}
			if (m_CurrentMousePosition.x < m_StartMousePosition.x)
			{
				var startPosition		= m_StartMousePosition;
				m_StartMousePosition	= new Vector2(m_CurrentMousePosition.x, m_StartMousePosition.y);
				m_CurrentMousePosition	= startPosition;
			}

			if (_IsHard)
			{
				m_RectDrawn = false;
				m_SelectionRect = new Rect();
			}
		}

        private void drag(Event _CurrentEvent)
        {
			if (m_IsDragging && !m_OutOfTextureBounds)
			{
				m_CurrentMousePosition = _CurrentEvent.mousePosition;

				if (m_StartMousePosition.x > m_CurrentMousePosition.x)
				{
					m_Left = (int)m_CurrentMousePosition.x;
					m_Right = (int)m_StartMousePosition.x;
				}
				else
				{
					m_Left = (int)m_StartMousePosition.x;
					m_Right = (int)m_CurrentMousePosition.x;
				}

				if (m_StartMousePosition.y > m_CurrentMousePosition.y)
				{
					m_Top = (int)m_CurrentMousePosition.y;
					m_Bottom = (int)m_StartMousePosition.y;
				}
				else
				{
					m_Top = (int)m_StartMousePosition.y;
					m_Bottom = (int)m_CurrentMousePosition.y;
				}

				m_SelectionRect = new Rect(m_Left, m_Top, m_Right - m_Left, m_Bottom - m_Top);

				if (_CurrentEvent.mousePosition.x < 0f ||
					_CurrentEvent.mousePosition.y < 0f ||
					_CurrentEvent.mousePosition.x > position.width ||
					_CurrentEvent.mousePosition.y > position.height)
				{
					reset(false);
					m_RectDrawn = true;
				}

				Repaint();

				_CurrentEvent.Use();
			}
        }

		private void resizeDrawnRect(Event _CurrentEvent)
		{
			if (m_IsDragging)
			{
				var startPosition = m_StartMousePosition;

				switch (m_CapturedSide)
				{
					case CapturedSide.TopLeft:
						m_StartMousePosition = _CurrentEvent.mousePosition;
						break;
					case CapturedSide.Top:
						m_StartMousePosition = new Vector2(startPosition.x, _CurrentEvent.mousePosition.y);
						break;
					case CapturedSide.TopRight:
						m_StartMousePosition	= new Vector2(startPosition.x, _CurrentEvent.mousePosition.y);
						m_CurrentMousePosition	= new Vector2(_CurrentEvent.mousePosition.x, m_CurrentMousePosition.y);
						break;
					case CapturedSide.Right:
						m_CurrentMousePosition = new Vector2(_CurrentEvent.mousePosition.x, m_CurrentMousePosition.y);
						break;
					case CapturedSide.BottomRight:
						m_CurrentMousePosition = _CurrentEvent.mousePosition;
						break;
					case CapturedSide.Bottom:
						m_CurrentMousePosition = new Vector2(m_CurrentMousePosition.x, _CurrentEvent.mousePosition.y);
						break;
					case CapturedSide.BottomLeft:
						m_StartMousePosition	= new Vector2(_CurrentEvent.mousePosition.x, startPosition.y);
						m_CurrentMousePosition	= new Vector2(m_CurrentMousePosition.x, _CurrentEvent.mousePosition.y);
						break;
					case CapturedSide.Left:
						m_StartMousePosition = new Vector2(_CurrentEvent.mousePosition.x, startPosition.y);
						break;
					default:
						break;
				}

				checkRestrictions();

				if (!verticalResize)
				{
					if (m_StartMousePosition.x > m_CurrentMousePosition.x)
					{
						m_Left	= (int)m_CurrentMousePosition.x;
						m_Right = (int)m_StartMousePosition.x;
					}

					else
					{
						m_Left	= (int)m_StartMousePosition.x;
						m_Right = (int)m_CurrentMousePosition.x;
					}
				}

				if (!horizontalResize)
				{
					if (m_StartMousePosition.y > m_CurrentMousePosition.y)
					{
						m_Top = (int)m_CurrentMousePosition.y;
						m_Bottom = (int)m_StartMousePosition.y;
					}

					else
					{
						m_Top = (int)m_StartMousePosition.y;
						m_Bottom = (int)m_CurrentMousePosition.y;
					}
				}

				m_SelectionRect = new Rect(m_Left, m_Top, m_Right - m_Left, m_Bottom - m_Top);

				if (_CurrentEvent.mousePosition.x < 0f ||
					_CurrentEvent.mousePosition.y < 0f ||
					_CurrentEvent.mousePosition.x > position.width ||
					_CurrentEvent.mousePosition.y > position.height)
				{
					reset(false);
				}

				Repaint();

				_CurrentEvent.Use();
			}
		}

        private void setResizeRects()
        {
			if (m_SelectionRect != default(Rect))
			{ 
				m_TopLeftRect     = new Rect(m_SelectionRect.x - m_ResizeRectSize / 2f, 
										m_SelectionRect.y - m_ResizeRectSize / 2f, 
										m_ResizeRectSize, 
										m_ResizeRectSize);
				m_TopRect         = new Rect((m_SelectionRect.x + m_SelectionRect.width / 2f) - m_ResizeRectSize / 2f, 
										m_SelectionRect.y - m_ResizeRectSize / 2f, 
										m_ResizeRectSize, 
										m_ResizeRectSize);
				m_TopRightRect    = new Rect(m_SelectionRect.x + m_SelectionRect.width - m_ResizeRectSize / 2f, 
										m_SelectionRect.y - m_ResizeRectSize / 2f, 
										m_ResizeRectSize, 
										m_ResizeRectSize);
				m_RightRect       = new Rect(m_SelectionRect.x + m_SelectionRect.width - m_ResizeRectSize / 2f, 
										(m_SelectionRect.y + m_SelectionRect.height / 2f) - m_ResizeRectSize / 2f, 
										 m_ResizeRectSize, 
										 m_ResizeRectSize);
				m_BottomRightRect = new Rect(m_SelectionRect.x + m_SelectionRect.width - m_ResizeRectSize / 2f,
										m_SelectionRect.y + m_SelectionRect.height - m_ResizeRectSize / 2f, 
										m_ResizeRectSize, 
										m_ResizeRectSize);
				m_BottomRect      = new Rect((m_SelectionRect.x + m_SelectionRect.width / 2f) - m_ResizeRectSize / 2f, 
										(m_SelectionRect.y + m_SelectionRect.height) - m_ResizeRectSize / 2f, 
										 m_ResizeRectSize, 
										 m_ResizeRectSize);
				m_BottomLeftRect  = new Rect(m_SelectionRect.x - m_ResizeRectSize / 2f, 
										(m_SelectionRect.y + m_SelectionRect.height) - m_ResizeRectSize / 2f, 
										 m_ResizeRectSize, 
										 m_ResizeRectSize);
				m_LeftRect        = new Rect(m_SelectionRect.x - m_ResizeRectSize / 2f, 
										(m_SelectionRect.y + m_SelectionRect.height / 2f) - m_ResizeRectSize / 2f, 
										 m_ResizeRectSize, 
										 m_ResizeRectSize);

				EditorGUI.DrawRect(m_TopLeftRect,       Color.white);
				EditorGUI.DrawRect(m_TopRect,           Color.white);
				EditorGUI.DrawRect(m_TopRightRect,      Color.white);
				EditorGUI.DrawRect(m_RightRect,         Color.white);
				EditorGUI.DrawRect(m_BottomRightRect,   Color.white);
				EditorGUI.DrawRect(m_BottomRect,        Color.white);
				EditorGUI.DrawRect(m_BottomLeftRect,    Color.white);
				EditorGUI.DrawRect(m_LeftRect,          Color.white);

				EditorGUIUtility.AddCursorRect(m_TopLeftRect,     MouseCursor.ResizeUpLeft);
				EditorGUIUtility.AddCursorRect(m_TopRect,         MouseCursor.ResizeVertical);
				EditorGUIUtility.AddCursorRect(m_TopRightRect,    MouseCursor.ResizeUpRight);
				EditorGUIUtility.AddCursorRect(m_RightRect,       MouseCursor.ResizeHorizontal);
				EditorGUIUtility.AddCursorRect(m_BottomRightRect, MouseCursor.ResizeUpLeft);
				EditorGUIUtility.AddCursorRect(m_BottomRect,      MouseCursor.ResizeVertical);
				EditorGUIUtility.AddCursorRect(m_BottomLeftRect,  MouseCursor.ResizeUpRight);
				EditorGUIUtility.AddCursorRect(m_LeftRect,        MouseCursor.ResizeHorizontal);
			}
        }

        private bool checkRect(Vector2 _CurrentMousePosition)
        {
			m_CapturedSide = CapturedSide.None;

			if (m_TopLeftRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.TopLeft;
			}

			if (m_TopRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.Top;
			}

			if (m_TopRightRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.TopRight;
			}

			if (m_RightRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.Right;
			}

			if (m_BottomRightRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.BottomRight;
			}

			if (m_BottomRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.Bottom;
			}

			if (m_BottomLeftRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.BottomLeft;
			}

			if (m_LeftRect.Contains(_CurrentMousePosition))
			{
				m_CapturedSide = CapturedSide.Left;
			}

			return	m_TopLeftRect.Contains(_CurrentMousePosition)		||
					m_TopRightRect.Contains(_CurrentMousePosition)		||
					m_BottomRightRect.Contains(_CurrentMousePosition)	||
					m_BottomLeftRect.Contains(_CurrentMousePosition)	||
					horizontalResize || verticalResize;
        }

		private void checkRestrictions()
		{
			if (m_StartMousePosition.x < m_TextureRect.x)
			{
				m_StartMousePosition = new Vector2(m_TextureRect.x, m_StartMousePosition.y);
			}
			if (m_StartMousePosition.y < m_TextureRect.y)
			{
				m_StartMousePosition = new Vector2(m_StartMousePosition.x, m_TextureRect.y);
			}
			if (m_CurrentMousePosition.x > (m_TextureRect.x + m_TextureRect.width))
			{
				m_CurrentMousePosition = new Vector2((m_TextureRect.x + m_TextureRect.width), m_CurrentMousePosition.y);
			}
			if (m_CurrentMousePosition.y > (m_TextureRect.y + m_TextureRect.height))
			{
				m_CurrentMousePosition = new Vector2(m_CurrentMousePosition.x, (m_TextureRect.y + m_TextureRect.height));
			}
		}
    }
}
