//////////////////////////////////////////////////////////////////////////
//Created by Neodrop. neodrop@unity3d.ru
//////////////////////////////////////////////////////////////////////////

using System;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// Базовый класс окон Антареса
/// </summary>
public class AntaresEdWindow : EditorWindow
{
    public delegate void SectorDelegate(params object[] obj);

    public static AntaresEdWindow thisScript;

    enum AntEnumEdit
    {
        Language = 1,
        Exit = 2
    }

    private AntEnumEdit enumEdit;

    private int editSelectedIndex = 0;
    protected static bool initDone = false;
    /// <summary>
    /// Скин Редактора, для отображение GUI одинаково во всех вкладках
    /// </summary>
    public GUISkin genericEditorSkin = EditorGUIUtility.GetBuiltinSkin(1);
    public int totalSharpLinesCount = 0, totalJSlinesCount = 0, totalLinesCount = 0;
    public static Hashtable textElements0 = new Hashtable();
    public static Texture folderIcon;
    public System.Collections.Hashtable TextElements0
    {
        get { return textElements0; }
        set { textElements0 = value; }
    }

    private static string[] editOptionsEng = new string[3], editOptionsRus = new string[3];
    public static void AntInit()
    {
        if (initDone) return;

        folderIcon = AssetDatabase.GetCachedIcon("Assets/Editor");

        editOptionsEng[0] = "";
        editOptionsEng[1] = "Language";
        editOptionsEng[2] = "Exit";

        editOptionsRus[0] = "";
        editOptionsRus[1] = "Язык";
        editOptionsRus[2] = "Выход";
        editOptionsRus = CheckText(editOptionsRus);

        ////////////////////////////////////////////////////////////////////////// Тут добавляем столько слайдеров, сколько потребуется.
        scrolls.Add("1-1", new Vector2());// Коллекция Vector2 для скроллов окон. Просто чтобы не плодить тучу переменных. Синтаксис : вкладка-колонка (1-1)
        scrolls.Add("1-2", new Vector2());
        scrolls.Add("AssetTabExportLeft", new Vector2());
        scrolls.Add("AssetTabExportRight", new Vector2());
        scrolls.Add("AssetTextures", new Vector2());
        scrolls.Add("6-1", new Vector2());
        scrolls.Add("6-2", new Vector2());

        //////////////////////////////////////////////////////////////////////////Заполнение хэштаблей названий элементов гуя.
        #region
        textElements0.Add("Antares Project Browser", "Проект Антарес");
        textElements0.Add("Assets", "Ассеты");
        textElements0.Add("All Objects", "Все Объекты");
        textElements0.Add("search", "искать");
        textElements0.Add("chosen", "выбран");
        textElements0.Add("Close", "Закрыть");
        textElements0.Add("Total C# lines count : ", "Строк в C# скриптах : ");
        textElements0.Add("Total JavaScript lines count : ", "Строк в JavaScript скриптах : ");
        textElements0.Add("Total scripts lines count : ", "Всего строк в скриптах : ");
        textElements0.Add("Total Objects Found : ", "Найдено объектов : ");
        textElements0.Add("Total scripts count : ", "Всего скриптов : ");
        textElements0.Add("Total Textures in Project : ", "Всего Текстур в Проекте : ");
        textElements0.Add("Textures|Materials", "Текстуры|Материалы");
        textElements0.Add("Textures", "Текстуры");
        textElements0.Add("Select All", "Выбрать всё");
        textElements0.Add("Scripts", "Скрипты");
        textElements0.Add("Refresh", "Обновить");
        textElements0.Add("Reserved", "Резерв");
        textElements0.Add("Ignore Antares Scripts", "Без скриптов Антареса");
        textElements0.Add("Lines Count : ", "Строк в скрипте :");
        textElements0.Add("", "");
        textElements0.Add("Language", "Язык");
        textElements0.Add("Models", "Модели");
        textElements0.Add("Materials", "Материалы");
        textElements0.Add("Exit", "Выход");
        textElements0.Add("Events", "События");
        textElements0.Add("Edit", "Изменить");
        textElements0.Add("Export Assets", "Экспорт");
        textElements0.Add("Export To Bundle", "Экспорт в .unity3d");
        textElements0.Add("Prefabs", "Префабы");
        textElements0.Add("Objects Selected : ", "Выбрано объектов : ");
        textElements0.Add("Objects", "объектов");
        textElements0.Add("Find Assets", "Найти Ассеты");
        textElements0.Add("Donate", "Помочь Проекту");
        #endregion

        if (EditorPrefs.HasKey("Antares : Selected Language"))
            languageSelected = EditorPrefs.GetInt("Antares : Selected Language");

        initDone = true;
    }
    /// <summary>
    /// Коллекция Vector2 для скроллов окон. Просто чтобы не плодить тучу переменных. Синтаксис : вкладка-колонка (1-1)
    /// </summary>
    public static Hashtable scrolls = new Hashtable();
    private int tabSelected = 0;

    public bool showLanguage = false;
    protected string[] languages = new string[2] { "English", "Русский" };
    /// <summary>
    /// 0 - Eng
    /// 1 - Rus
    /// </summary>
    protected static int languageSelected = 0;

    public static string[] CheckText(params string[] arr)
    {
        int count = arr.Length;
        for (int a = 0; a < count; a++)
        {
            string str = arr[a];
            if (str.Length == 0) continue;
            string str1 = "";

            char aa;
            int length = str.Length;
            for (int i = 0; i < length; i++)
            {
                int res = Convert.ToInt32(str[i]);
                if (res > 191 && res < 256) res = res + 848;
                aa = Convert.ToChar(res);
                str1 += aa.ToString();
            }
            arr[a] = str1;
        }
        return arr;
    }

    public static string Lang(string key)
    {
        if (languageSelected == 0) return key;
        return (string)textElements0[key];
    }

    public static Color colorGUI = GUI.color, colorSelected = Color.Lerp(Color.cyan, Color.white, .75f);
    public int DrawUpSideBar()
    {
        AntInit();

        #region // Вкладки команд
        GUILayout.Space(5);
        GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(32));
        GUILayout.Space(-28);
        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(-32);
        // Пока не удалось русифицировать.
        //string[] lang_options;
        //switch (langugeSelected)
        //{
        //    case 0:
        //        lang_options = editOptionsEng;
        //        break;
        //    default:
        //        lang_options = editOptionsRus;
        //        break;
        //}
        editSelectedIndex = EditorGUILayout.Popup(Lang("Edit"), editSelectedIndex, editOptionsEng, GUILayout.Width(128), GUILayout.Height(32));

        if (GUI.changed)
        {
            switch (editSelectedIndex)
            {
                case (int)AntEnumEdit.Exit:
                    Close();
                    break;
                case (int)AntEnumEdit.Language:
                    editSelectedIndex = 0; // Сбрасываю. Иначе будет висеть выбранное на кнопке. Пока это излишне.
                    showLanguage = !showLanguage;
                    break;
            }
        }
        GUILayout.Space(175);
        GUI.color = colorSelected;
        if (GUILayout.Button(Lang("Donate"), GUILayout.Width(128))) Application.OpenURL(AntaresGameObjectCustom.Routines.donationURL);
        GUI.color = colorGUI;
        EditorGUILayout.EndHorizontal();
        if (showLanguage)
        {
            GUILayout.Space(-10);
            GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(32));
            GUILayout.Space(-29);
            GUILayout.BeginHorizontal(GUILayout.Width(128));
            GUILayout.Space(10);
            bool eng = true, ru = false;
            if (languageSelected == 1)
            {
                eng = !eng; ru = !ru;
            }

            eng = GUILayout.Toggle(eng, languages[0]);
            ru = GUILayout.Toggle(ru, languages[1]);
            if (ru && languageSelected == 0)
            {
                eng = false;
                languageSelected = 1;
                EditorPrefs.SetInt("Antares : Selected Language", 1);
            }
            else if (eng && languageSelected == 1)
            {
                ru = false;
                languageSelected = 0;
                EditorPrefs.SetInt("Antares : Selected Language", 0);
            }
            GUILayout.Space(20);
            if (GUILayout.Button(Lang("Close"))) showLanguage = false;
            GUILayout.EndHorizontal();
            GUILayout.Space(12);
        }
        #endregion
        GUILayout.Space(-10);
        GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(32));
        GUILayout.Space(-29);
        GUILayout.BeginHorizontal();
        GUILayout.Space(10);
        //tabSelected = GUILayout.SelectionGrid(tabSelected, tabsContent, 6, GUILayout.MaxWidth(1024));
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //Выводим кнопки табов
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        if (tabSelected == 0) GUI.color = colorSelected;
        if (GUILayout.Button(Lang("Scripts"), GUILayout.Width(128)))
        {
            tabSelected = 0;
        }
        GUI.color = colorGUI; if (tabSelected == 1) GUI.color = colorSelected;
        if (GUILayout.Button(Lang("Assets"), GUILayout.Width(128)))
        {
            tabSelected = 1;
        }
        //GUI.color = colorGUI; if (tabSelected == 2) GUI.color = colorSelected;
        //if (GUILayout.Button(Lang("Textures|Materials")))
        //{
        //    tabSelected = 2;
        //}
        //GUI.color = colorGUI; if (tabSelected == 3) GUI.color = colorSelected;
        //if (GUILayout.Button(Lang("Prefabs")))
        //{
        //    tabSelected = 3;
        //}
        //GUI.color = colorGUI; if (tabSelected == 4) GUI.color = colorSelected;
        //if (GUILayout.Button(Lang("Models")))
        //{
        //    tabSelected = 4;
        //}
        //GUI.color = colorGUI; if (tabSelected == 5) GUI.color = colorSelected;
        //if (GUILayout.Button(Lang("Reserved"), GUILayout.Width(128)))
        //{
        //    tabSelected = 5;
        //}
        GUI.color = colorGUI;
        GUI.enabled = false;
        if (tabSelected == 6) GUI.color = colorSelected;
        if (GUILayout.Button(Lang("Events"), GUILayout.Width(128)))
        {
            tabSelected = 6;
        }
        GUI.color = colorGUI;
        GUI.enabled = true;
        GUILayout.EndHorizontal();

        return tabSelected;
    }

    /// <summary>
    /// Ищет объекты по маске файла
    /// </summary>
    /// <param name="fileMask">К примеру "*.cs"</param>
    /// <returns>Возвращает ArrayList найденных файлов</returns>
    public ArrayList Search(string fileMask)
    {
        ArrayList arr = new ArrayList();
        int path = Application.dataPath.Length + 1;
        FileInfo[] fileInfo;
        DirectoryInfo dir = new DirectoryInfo(Application.dataPath);

        System.Type type = null;
        switch (fileMask)
        {
            case "*.cs":
                type = typeof(MonoScript);
                totalSharpLinesCount = 0;
                break;
            case "*.js":
                type = typeof(MonoScript);
                totalJSlinesCount = 0;
                break;
        }

        int count;

        fileInfo = dir.GetFiles(fileMask, SearchOption.AllDirectories);
        count = fileInfo.Length;

        for (int i = 0; i < count; i++)
        {
            string filePath = fileInfo[i].ToString();
            arr.Add(AssetDatabase.LoadAssetAtPath(filePath.Remove(0, path), type));
            ProcessStatistic(fileMask, filePath);
        }
        return arr;
    }

    private List<AntaresEdMainWindow.ExportedOptions> FindMaterialsByTexture(AntaresEdMainWindow.ExportedOptions texture)
    {
        List<AntaresEdMainWindow.ExportedOptions> materials = new List<AntaresEdMainWindow.ExportedOptions>();
        List<DirectoryInfo> infos = (List<DirectoryInfo>)AntaresEdMainWindow.assetsVar.materialsInProject["Keys"];

        //"_MainTex" is the main diffuse texture. This can also be accessed via mainTexture property.
        //"_BumpMap" is the normal map.
        //"_LightMap" is the lightmap texture.
        //"_Cube" is the reflection cubemap.

        Texture stex = texture.obj as Texture;

        foreach (DirectoryInfo dif in infos)
        {
            List<AntaresEdMainWindow.ExportedOptions> objects = (List<AntaresEdMainWindow.ExportedOptions>)AntaresEdMainWindow.assetsVar.materialsInProject[dif];
            int cou = objects.Count;
            for (int i = 0; i < cou; i++)
            {
                AntaresEdMainWindow.ExportedOptions matObj = objects[i];
                Material mat = matObj.obj as Material;
                Texture tex = null;
                if(mat.HasProperty("_MainTex"))
                {
                    tex = mat.GetTexture("_MainTex");
                    if ( !matObj.allTabConteined && tex == stex && !materials.Contains(matObj)) materials.Add(matObj);
                }
                if (mat.HasProperty("_BumpMap"))
                {
                    tex = mat.GetTexture("_BumpMap");
                    if (!matObj.allTabConteined && tex == stex && !materials.Contains(matObj)) materials.Add(matObj);
                }
                if (mat.HasProperty("_LightMap"))
                {
                    tex = mat.GetTexture("_LightMap");
                    if (!matObj.allTabConteined && tex == stex && !materials.Contains(matObj)) materials.Add(matObj);
                }
                if (mat.HasProperty("_Cube"))
                {
                    tex = mat.GetTexture("_Cube");
                    if (!matObj.allTabConteined && tex == stex && !materials.Contains(matObj)) materials.Add(matObj);
                }
            }
        }
        if (materials.Count == 0) return null;
        else return materials;
    }

    private static Texture cSTex = null, jSTex = null;
    public Hashtable FindObjects(List<string> fileExtansion, ref int objectsCount)
    {
        int path = Application.dataPath.Length + 1;
        Hashtable foundObjects = new Hashtable();
        FileInfo[] fileInfo;
        DirectoryInfo dir = new DirectoryInfo(Application.dataPath);
        DirectoryInfo[] directories = dir.GetDirectories("*.*", SearchOption.AllDirectories);
        ArrayList sorted = new ArrayList();
        System.Type texType = typeof(Texture2D);

        List<AntaresEdMainWindow.ExportedOptions> allFiles = new List<AntaresEdMainWindow.ExportedOptions>();

        objectsCount = 0;
        foreach (DirectoryInfo dif in directories)
        {
            fileInfo = dif.GetFiles("*.*", SearchOption.TopDirectoryOnly);
            int count = fileInfo.Length;
            List<AntaresEdMainWindow.ExportedOptions> files = new List<AntaresEdMainWindow.ExportedOptions>();
            
            for (int i = 0; i < count; i++)
            {
                string fileExt = fileInfo[i].Extension.ToLower();

                if (!fileExtansion.Contains("*" + fileExt))
                {
                    continue;
                }

                AntaresEdMainWindow.ExportedOptions f = new AntaresEdMainWindow.ExportedOptions();

                f.path = fileInfo[i].ToString().Remove(0, path);
                f.obj = AssetDatabase.LoadMainAssetAtPath("Assets/" + f.path);

                if (f.obj != null)
                {
                    objectsCount++;
                    f.type = f.obj.GetType();
                    switch(fileExt)
                    {
                        case ".cs":
                            if (cSTex != null) f.icon.image = cSTex;
                            else f.icon.image = cSTex = AssetDatabase.GetCachedIcon("Assets/" + f.path);
                            break;
                        case ".js":
                            if (jSTex != null) f.icon.image = jSTex;
                            else f.icon.image = jSTex = AssetDatabase.GetCachedIcon("Assets/" + f.path);
                            break;
                        default :
                            f.icon.image = AssetDatabase.GetCachedIcon("Assets/" + f.path);
                            break;
                    }
                    if (f.type == texType) f.textureMaterals = FindMaterialsByTexture(f);
                    //f.icon = EditorGUIUtility.ObjectContent(f.obj, f.type);
                    files.Add(f);
                    //AntaresEdMainWindow.ExportedOptions fa = new AntaresEdMainWindow.ExportedOptions();
                    //fa.icon = f.icon; fa.obj = f.obj; fa.path = f.path; fa.textureMaterals = f.textureMaterals;
                    //fa.type = f.type;
                    //fa.allTabConteined = true;
                    allFiles.Add(f);
                }
            }
            if (files.Count > 0)
            {
                files.Sort(SortFilesByName);
                foundObjects.Add(dif, files);
                sorted.Add(dif);
            }
        }
        
        foundObjects.Add(dir, allFiles);
        sorted.Add(dir);

        sorted.Sort(new UOComparerDirectoryInfo());
        Hashtable ret = new Hashtable();
        List<bool> choosen = new List<bool>(), selectAll = new List<bool>();
        List<DirectoryInfo> myKeys = new List<DirectoryInfo>();
        foreach (DirectoryInfo dif in sorted)
        {
            choosen.Add(false);
            selectAll.Add(false);
            myKeys.Add(dif);
            ret.Add(dif,foundObjects[dif]);
        }
        ret.Add("Toggles", choosen);
        ret.Add("Select All", selectAll);
        ret.Add("Keys", myKeys);

        return ret;
    }

    /// ///////////////////////////////////////////////////////////////////////////////////////////// SAVE TO BUNDLE ////////////////////////////////////
    private static string lastSavedFolder = "";
    public static void SaveToBundles(Hashtable toExport, bool collectDependencies)
    {
        string srcFolder = lastSavedFolder;
        if (lastSavedFolder == "") srcFolder = Application.dataPath;
        string label = "";
        switch (Application.systemLanguage)
        {
            case SystemLanguage.English:
                label = "Save bundles to directory";
                break;
            case SystemLanguage.Russian:
                label = "Укажите папку для сохранения ресурсов";
                break;
        }
        string dstFolder = lastSavedFolder = EditorUtility.SaveFolderPanel(label, srcFolder, "");
        
        if (dstFolder == "") return;

        int count = 0;

        BuildAssetBundleOptions options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets;
        if (!collectDependencies) options = BuildAssetBundleOptions.CompleteAssets;

        List<DirectoryInfo> infos = (List<DirectoryInfo>)toExport["Keys"];
        List<AntaresEdMainWindow.ExportedOptions> processed = new List<AntaresEdMainWindow.ExportedOptions>();
        foreach (DirectoryInfo dif in infos)
        {
            List<AntaresEdMainWindow.ExportedOptions> objects = (List<AntaresEdMainWindow.ExportedOptions>)toExport[dif];
            int cou = objects.Count;
            for (int i = 0; i < cou; i++)
            {
                AntaresEdMainWindow.ExportedOptions sobj = objects[i];
                if (sobj.markedToSave && !processed.Contains(sobj))
                {
                    processed.Add(sobj);
                    if (!BuildPipeline.BuildAssetBundle(sobj.obj, null, dstFolder + Path.DirectorySeparatorChar + sobj.obj.name + ".unity3d", options))
                    {
                        Debug.Log("Error during Exporting : " + sobj.obj.name);
                    }
                    else count++;
                }
            }
        }
        Debug.Log("Total Objects Exported : " + count);
    }

    private static int SortFilesByName(AntaresEdMainWindow.ExportedOptions a, AntaresEdMainWindow.ExportedOptions b)
    {
        if (a == null || b == null) return 0;
        return string.Compare(a.obj.name, b.obj.name);//a.priority.CompareTo(b.priority);
    }

    /// <summary>
    /// Ищет объекты по маске файла
    /// </summary>
    /// <param name="fileMask">К примеру "*.cs"</param>
    /// <param name="ignoreAntaresScripts">Не искать файлы в папке Антареса</param>
    /// <returns></returns>
    public ArrayList Search(string fileMask, bool ignoreAntaresScripts)
    {
        ArrayList arr = new ArrayList();
        int path = Application.dataPath.Length - 6;
        FileInfo[] fileInfo;
        DirectoryInfo dir = new DirectoryInfo(Application.dataPath);

        System.Type type = typeof(MonoScript);
        switch (fileMask)
        {
            case "*.cs":
                totalSharpLinesCount = 0;
                break;
            case "*.js":
                totalJSlinesCount = 0;
                break;
        }

        int count;

        fileInfo = dir.GetFiles(fileMask, SearchOption.AllDirectories);

        count = fileInfo.Length;
        for (int i = 0; i < count; i++)
        {
            string filePath = fileInfo[i].ToString();
            if (!ignoreAntaresScripts)
            {
                arr.Add(AssetDatabase.LoadAssetAtPath(filePath.Remove(0, path), type));
                ProcessStatistic(fileMask, filePath);
            }
            else
                if (!fileInfo[i].ToString().Contains("Antares"))
                {
                    arr.Add(AssetDatabase.LoadAssetAtPath(filePath.Remove(0, path), type));
                    ProcessStatistic(fileMask, filePath);
                }
        }
        arr.Sort(new UOComparer());
        return arr;
    }

    /// <summary>
    /// Ищет C# и JavaScript скрипты
    /// </summary>
    /// <param name="findAllMonoScripts">Просто напоминание. Значение не существенно</param>
    /// <param name="ignoreAntaresScripts">При поиске, не учитывать скрипты из папки Антареса</param>
    /// <returns>Возвращает ArrayList найденных файлов</returns>
    public ArrayList Search(bool findAllMonoScripts, bool ignoreAntaresScripts)
    {
        totalLinesCount = 0;
        ArrayList arr = Search("*.cs", ignoreAntaresScripts);
        ArrayList arrJ = Search("*.js", ignoreAntaresScripts);
        int count = arrJ.Count;
        for (int i = 0; i < count; i++) arr.Add(arrJ[i]);
        return arr;
    }

    public static int ProcessFile(string filename)
    {
        System.IO.StreamReader reader = System.IO.File.OpenText(filename);
        int iLineCount = 0;
        while (reader.Peek() >= 0)
        {
            reader.ReadLine();
            ++iLineCount;
        }
        reader.Close();
        return iLineCount;
    }

    void ProcessStatistic(string fileMask, string filePath)
    {
        int iLinesCount = ProcessFile(filePath);
        switch (fileMask)
        {
            case "*.cs":
                totalSharpLinesCount += iLinesCount;
                break;
            case "*.js":
                totalJSlinesCount += iLinesCount;
                break;
            default:
                return;
        }
        totalLinesCount += iLinesCount;
    }

    /// <summary>
    /// DrawField
    /// </summary>
    /// <param name="deleg">Delegate Method</param>
    /// <param name="skin">GUSkin</param>
    /// <param name="scroll">Vector2</param>
    /// <returns></returns>
    public static Vector2 DrawField(SectorDelegate deleg, GUISkin skin, Vector2 scroll, params GUILayoutOption[] option)
    {
        GUILayout.Space(7);
        scroll = EditorGUILayout.BeginScrollView(scroll, false, false,
            skin.horizontalScrollbar, skin.verticalScrollbar, skin.box, option);

        deleg();

        EditorGUILayout.EndScrollView();
        return scroll;
    }

    public class UOComparer : IComparer
    {
        int IComparer.Compare(object a, object b)
        {
            UnityEngine.Object A = (UnityEngine.Object)a;
            UnityEngine.Object B = (UnityEngine.Object)b;
            return string.Compare(A.name, B.name);
        }
    }

    public class UOComparerDirectoryInfo : IComparer
    {
        int IComparer.Compare(object a, object b)
        {
            DirectoryInfo A = (DirectoryInfo)a;
            DirectoryInfo B = (DirectoryInfo)b;
            return string.Compare(A.FullName, B.FullName);
        }
    }

    public class UOComparerTextures : IComparer
    {
        int IComparer.Compare(object a, object b)
        {
            Texture A = (Texture)a;
            Texture B = (Texture)b;
            return string.Compare(A.name, B.name);
        }
    }

    public void DrawSeparator()
    {
        GUI.color = colorSelected;
        GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(3));
        GUI.color = colorGUI;
    }
}