﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEditor;
using UnityEngine;

public class TermsListEditor
{

    public event System.Action OnRepaintNeeded;

    private SerializedObject termsSourceObject;

    private SerializedProperty termsSerializedProperty;

    private LocalizationSource termsSource;

    private int selectedTerm = -1;

    private Vector2 bodyScrollPos;

    private string tempTermName;

    private const float c_HeaderLineHeight = 22;

    private const float c_AddButtonLineHeight = 22;

    private const string c_WindowControlName = "TermsListWindow";

    private const string c_TermNameControl = "TermNameInput";

    private float defaultTermHeight
    {
        get
        {
            return EditorGUIUtility.singleLineHeight * 1.3f;
        }
    }

    private float selectedTermHeight
    {
        get
        {
            float result = 0;

            //name text field
            result += defaultTermHeight;

            //description text area
            result += 75f;

            //languages text field
            result += (EditorGUIUtility.singleLineHeight * 2f) * termsSource.LanguagesCount;

            //general padding
            result += 12f;

            return result;
        }
    }

    private float termsScrollHeight
    {
        get
        {
            float result = 0;

            result = defaultTermHeight * termsSource.Terms.Count;

            if (selectedTerm >= 0)
            {
                result -= defaultTermHeight;
                result += selectedTermHeight;
            }

            return result;
        }
    }

    public TermsListEditor(SerializedObject termSerializedObject, SerializedProperty termsProperty, LocalizationSource targetObject)
    {
        termsSourceObject = termSerializedObject;
        termsSerializedProperty = termsProperty;
        termsSource = targetObject;
    }

    public void DoTermsList(Rect rect)
    {
        Rect headerRect = rect;
        headerRect.height = c_HeaderLineHeight;

        Rect addButtonRect = rect;
        addButtonRect.height = c_AddButtonLineHeight;
        addButtonRect.y = rect.y + rect.height - c_AddButtonLineHeight;

        rect.height = rect.height - c_HeaderLineHeight - c_AddButtonLineHeight;
        rect.y = rect.y + c_HeaderLineHeight;

        DrawHeader(headerRect);
        DrawBody(rect);
        DrawAddButton(addButtonRect);
    }

    private void DrawHeader(Rect rect)
    {
        EditorGUI.DrawRect(rect, Color.black * 0.3f);

        rect.y += 2;
        rect.x += 4;
        EditorGUI.LabelField(rect, "Terms list");
    }

    private void DrawBody(Rect rect)
    {
        var scrollRect = rect;

        scrollRect.height = termsScrollHeight;
        scrollRect.width -= 20;

        GUI.SetNextControlName(c_WindowControlName);
        EditorGUI.DrawRect(rect, Color.black * 0.1f);

        var termDrawRect = scrollRect;
        termDrawRect.height = defaultTermHeight;

        bodyScrollPos = GUI.BeginScrollView(rect, bodyScrollPos, scrollRect, false, true);

        for (int i = 0; i < termsSerializedProperty.arraySize; i++)
        {
            termDrawRect.height = selectedTerm == i ? selectedTermHeight : defaultTermHeight;

            DrawSplitter(termDrawRect);
            DrawTerm(termDrawRect, i);
            termDrawRect.y += termDrawRect.height;
        }

        GUI.EndScrollView();
    }

    private void DrawTerm(Rect rect, int termIndex)
    {
        var headerBackgroundRect = rect;

        headerBackgroundRect.width += 5;
        headerBackgroundRect.height = defaultTermHeight;

        EditorGUI.DrawRect(headerBackgroundRect, selectedTerm == termIndex ? Color.blue * 0.2f : Color.white * 0.1f);
        var termProperty = termsSerializedProperty.GetArrayElementAtIndex(termIndex);

        var nameRect = rect;
        var removeRect = rect;

        nameRect.y += 3;
        nameRect.x += 2;
        nameRect.width -= 25f;
        GUI.Label(nameRect, termProperty.FindPropertyRelative("m_Name").stringValue);

        removeRect.y += 2.5f;
        removeRect.x = removeRect.width - 4;
        removeRect.height = EditorGUIUtility.singleLineHeight;
        removeRect.width = 18f;

        var currentEvent = Event.current;

        if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0 && headerBackgroundRect.Contains(currentEvent.mousePosition) && !removeRect.Contains(currentEvent.mousePosition))
        {
            if (selectedTerm != termIndex)
                selectedTerm = termIndex;
            else
                selectedTerm = -1;

            if (OnRepaintNeeded != null)
                OnRepaintNeeded(); // repaint editor to draw newly selected term

            EditorGUI.FocusTextInControl(""); // reset text selection
        }

        #region arrow navigation
        if (currentEvent.keyCode == KeyCode.UpArrow && currentEvent.type == EventType.KeyUp && selectedTerm > 0)
        {
            selectedTerm--;
            currentEvent.Use();
        }

        if (currentEvent.keyCode == KeyCode.DownArrow && currentEvent.type == EventType.KeyUp && selectedTerm < termsSerializedProperty.arraySize - 1)
        {
            selectedTerm++;
            currentEvent.Use();
        }
        #endregion

        if (GUI.Button(removeRect, new GUIContent("R", "Remove")) && EditorUtility.DisplayDialog("Warning!", "Are you shure you want to delete this term?", "Yes", "No"))
        {
            termsSerializedProperty.DeleteArrayElementAtIndex(termIndex);
            selectedTerm = -1;

            termsSourceObject.ApplyModifiedProperties();
            return;
        }

        if (selectedTerm == termIndex)
        {
            var nameSpliiterRect = rect;
            var descriptionLabelRect = rect;
            var bodyBackgroundRect = rect;

            bodyBackgroundRect.width += 5;
            bodyBackgroundRect.height -= defaultTermHeight;
            bodyBackgroundRect.y += defaultTermHeight;
            EditorGUI.DrawRect(bodyBackgroundRect, Color.black * 0.15f);

            nameSpliiterRect.y += defaultTermHeight;
            DrawSplitter(nameSpliiterRect);

            descriptionLabelRect.x += 2;
            descriptionLabelRect.y += defaultTermHeight + 3f;
            EditorGUI.LabelField(descriptionLabelRect, "Description:");

            var descriptionProperty = termsSerializedProperty.GetArrayElementAtIndex(termIndex).FindPropertyRelative("m_Description");

            descriptionLabelRect.y += 18f;
            descriptionLabelRect.width = rect.width;
            descriptionLabelRect.height = 50f;
            descriptionProperty.stringValue = EditorGUI.TextArea(descriptionLabelRect, descriptionProperty.stringValue);

            var localizedTermsBoundariesRect = descriptionLabelRect;

            localizedTermsBoundariesRect.x += 1f;
            localizedTermsBoundariesRect.y += 55f;
            localizedTermsBoundariesRect.width = rect.width - 1f;
            localizedTermsBoundariesRect.height = (EditorGUIUtility.singleLineHeight * 2) * termsSource.LanguagesCount + 8;
            EditorGUI.DrawRect(localizedTermsBoundariesRect, Color.white * 0.35f);

            var languageNameRect = localizedTermsBoundariesRect;
            var languageTextAreaRect = localizedTermsBoundariesRect;

            languageNameRect.y += 4;
            languageNameRect.x += 4;
            languageNameRect.height = EditorGUIUtility.singleLineHeight;
            languageNameRect.width = 60f;

            languageTextAreaRect.y += 4;
            languageTextAreaRect.x += 66;
            languageTextAreaRect.width -= 70;
            languageTextAreaRect.height = EditorGUIUtility.singleLineHeight * 2f;

            var termStringsProperty = termsSerializedProperty.GetArrayElementAtIndex(termIndex).FindPropertyRelative("m_LocalizedStrings");

            for (int i = 0; i < termsSource.LanguagesCount; i++)
            {
                var termLocalizedStringProperty = termStringsProperty.GetArrayElementAtIndex(i);

                EditorGUI.LabelField(languageNameRect, termLocalizedStringProperty.FindPropertyRelative("m_LangCode").FindPropertyRelative("m_LanguageEnglishName").stringValue);
                termLocalizedStringProperty.FindPropertyRelative("m_Text").stringValue = EditorGUI.TextArea(languageTextAreaRect, termLocalizedStringProperty.FindPropertyRelative("m_Text").stringValue);

                languageNameRect.y += EditorGUIUtility.singleLineHeight * 2f;
                languageTextAreaRect.y += EditorGUIUtility.singleLineHeight * 2f;
            }
        }
    }

    private void DrawSplitter(Rect rect)
    {
        // Splitter rect should be full-width
        rect.xMin = 0f;
        rect.width += 4f;
        rect.height = 1;

        if (Event.current.type != EventType.Repaint)
            return;

        EditorGUI.DrawRect(rect, !EditorGUIUtility.isProSkin
            ? new Color(0.6f, 0.6f, 0.6f, 1.333f)
            : new Color(0.12f, 0.12f, 0.12f, 1.333f));
    }

    private void DrawAddButton(Rect rect)
    {
        EditorGUI.DrawRect(rect, Color.black * 0.3f);

        var addButtonTextRect = rect;
        var addButtonRect = rect;

        addButtonTextRect.x += 2;
        addButtonTextRect.y += 2;
        addButtonTextRect.width -= 4;
        addButtonTextRect.height -= 4;
        addButtonTextRect.width -= 35;

        addButtonRect.width = 35;
        addButtonRect.x += addButtonTextRect.width;
        addButtonRect.height -= 4;
        addButtonRect.y += 2;

        GUI.SetNextControlName(c_TermNameControl);
        tempTermName = EditorGUI.TextField(addButtonTextRect, tempTermName);

        if (GUI.Button(addButtonRect, "Add", EditorStyles.miniButtonRight))
            TryAddTerm(tempTermName);

        if (!string.IsNullOrEmpty(tempTermName) && Event.current.type == EventType.KeyUp && Event.current.keyCode == KeyCode.Return && GUI.GetNameOfFocusedControl() == c_TermNameControl)
        {
            GUI.FocusControl(c_TermNameControl);
            TryAddTerm(tempTermName);
        }
    }

    private void TryAddTerm(string termName)
    {
        if (string.IsNullOrEmpty(termName))
        {
            EditorUtility.DisplayDialog("Error", "Can't add term with empty name", "ok");
            GUI.FocusControl(c_WindowControlName);
            return;
        }

        if (termsSource.ContainsTerm(termName))
        {
            EditorUtility.DisplayDialog("Error", "Term with current name already exists", "ok");
            GUI.FocusControl(c_WindowControlName);
            return;
        }

        tempTermName = string.Empty;

        termsSerializedProperty.arraySize++;
        var termProperty = termsSerializedProperty.GetArrayElementAtIndex(termsSerializedProperty.arraySize - 1);

        termProperty.FindPropertyRelative("m_Name").stringValue = termName;
        termProperty.FindPropertyRelative("m_Description").stringValue = "";

        termProperty.FindPropertyRelative("m_LocalizedStrings").arraySize = 0;
        termProperty.FindPropertyRelative("m_Guid").stringValue = LocalizationUtils.GenerateGUID();
        termsSourceObject.ApplyModifiedProperties();

        termsSource.OnUpdateLanguages();
        EditorUtility.SetDirty(termsSource);
    }

}
