//
// Proto2CSEditor.cs
//
// Author:
//       JasonXuDeveloper（傑） <jasonxudeveloper@gmail.com>
//
// Copyright (c) 2020 JEngine
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System.IO;
using Google.Protobuf.Reflection;
using ProtoBuf.Reflection;
using UnityEditor;
using UnityEngine;

namespace Guru
{
    internal class Proto2CSEditor : EditorWindow
    {
        private static Proto2CSEditor win;

        //[MenuItem("Tools/Protobuf/Show Generate Window")]
        //public static void ShowCSGenerateWindow()
        //{
        //    int index = Application.dataPath.LastIndexOf('/');
        //    var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos";
        //    win = GetWindow<Proto2CSEditor>("Proto2CS Generator");
        //    win.folder = EditorUtility.OpenFolderPanel("Please select proto files directory",
        //        proto_dir, "");
        //    win.minSize = new Vector2(500, 500);
        //    win.Show();
        //}

        [MenuItem("Tools/Protobuf/Generate All")]
        public static void GenerateAllProtos()
        {
            int index = Application.dataPath.LastIndexOf('/');
            var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos";
            var file_list = GetAllProtoFiles(proto_dir);
            var dest_folder = proto_dir;// $"{Application.dataPath}/../Assets/Scripts/NetworkGen";

            if (Directory.Exists(dest_folder))
            {
                Directory.Delete(dest_folder, true);

                // Just in case the system has output_folder still locked for deletion
                while (Directory.Exists(dest_folder))
                {
                    System.Threading.Thread.Sleep(10);
                }
            }

            Directory.CreateDirectory(dest_folder);

            Generate(proto_dir, file_list, dest_folder);
        }

        [MenuItem("Tools/Protobuf/View Proto Files")]
        private static void ViewDataPath()
        {
            int index = Application.dataPath.LastIndexOf('/');
            var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos";

            if (!Directory.Exists(proto_dir))
            {
                Directory.CreateDirectory(proto_dir);
            }

            EditorUtility.OpenWithDefaultApp(proto_dir);
        }

        [SerializeField] protected string[] _fileList = new string[0];
        protected string folder;
        protected SerializedObject _serializedObject;
        protected SerializedProperty _fileListProperty;


        protected void OnEnable()
        {
            //使用当前类初始化
            _serializedObject = new SerializedObject(this);
            //获取当前类中可序列话的属性
            _fileListProperty = _serializedObject.FindProperty("_fileList");
        }

        protected void OnGUI()
        {
            //绘制标题
            GUILayout.Space(10);
            GUIStyle textStyle = new GUIStyle();
            textStyle.fontSize = 24;
            textStyle.normal.textColor = Color.white;
            textStyle.alignment = TextAnchor.MiddleCenter;
            GUILayout.Label("Proto文件转CS文件", textStyle);
            textStyle.fontSize = 18;
            GUILayout.Label("Proto file to CS file", textStyle);
            GUILayout.Space(10);

            /*
			 * 路径
			 */
            GUILayout.Label("Proto file folder Proto文件路径");
            GUILayout.BeginHorizontal();
            EditorGUI.BeginDisabledGroup(true);
            folder = EditorGUILayout.TextField(folder);
            EditorGUI.EndDisabledGroup();

            GUILayout.Space(10);
            if (GUILayout.Button("Select Path 选择路径", GUILayout.ExpandWidth(false)))
            {
                int index = Application.dataPath.LastIndexOf('/');
                var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos";

                folder = EditorUtility.OpenFolderPanel("Select proto files source 请选择proto文件路径", proto_dir, "");
            }

            GUILayout.EndHorizontal();

            /*
			 * 文件
			 */
            GUILayout.Space(10);
            GUILayout.Label("Files to convert 需转换文件");
            //更新
            _serializedObject.Update();
            //开始检查是否有修改
            EditorGUI.BeginChangeCheck();
            //显示属性
            EditorGUILayout.PropertyField(_fileListProperty, true);

            //结束检查是否有修改
            if (EditorGUI.EndChangeCheck())
            {
                //提交修改
                _serializedObject.ApplyModifiedProperties();
            }

            /*
			 * 按钮
			 */
            GUILayout.Space(50);
            if (GUILayout.Button("Match all files from folder 从文件夹中匹配全部文件"))
            {
                _fileList = GetAllProtoFiles(folder);
                _serializedObject.Update();
            }

            GUILayout.Space(10);
            if (GUILayout.Button("Generate 生成"))
            {
                var dest_folder = $"{Application.dataPath}/Gen/Network";
                //Generate(folder, _fileList, dest_folder);
            }
        }

        private static string[] GetAllProtoFiles(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                Debug.LogError($"Folder path is empty!");
                return null;
            }

            var file_list = Directory.GetFiles(path, "*.proto", SearchOption.AllDirectories);
            var file_name_list = new string[file_list.Length];

            for (int i = 0; i < file_list.Length; i++)
            {
                file_name_list[i] = Path.GetFileName(file_list[i]);
            }

            return file_name_list;
        }

        private static void Generate(string inpath, string[] inprotos, string outpath)
        {
            if (!Directory.Exists(outpath))
            {
                Directory.CreateDirectory(outpath);
            }

            var set = new FileDescriptorSet();
            set.AddImportPath(inpath);
            foreach (var inproto in inprotos)
            {
                var s = inproto;
                if (!inproto.Contains(".proto"))
                {
                    s += ".proto";
                }

                set.Add(s, true);
            }

            set.Process();
            var errors = set.GetErrors();
            CSharpCodeGenerator.ClearTypeNames();
            var files = CSharpCodeGenerator.Default.Generate(set);

            foreach (var file in files)
            {
                CSharpCodeGenerator.ClearTypeNames();
                var full_file_name = file.Name;
                int index = full_file_name.LastIndexOf('.');
                var file_name = index > 0 ? full_file_name.Substring(0, index) : full_file_name;
                file_name = file_name.ToLower();
                var dest_filename = $"{NameNormalizer.AutoCapitalize(file_name)}.cs";
                //var path = Path.Combine(outpath, file.Name);
                var path = Path.Combine(outpath, dest_filename);
                File.WriteAllText(path, file.Text);

                Debug.Log($"Generated cs file for {full_file_name.Replace(".cs", ".proto")} successfully to: {path}");
            }

            EditorUtility.DisplayDialog("Complete",
                "Proto文件已转CS，详细请看控制台输出" +
                "\n" +
                "Proto files has been convert into CS files, please go to console and view details",
                "Close window");

            if (win != null)
            {
                win.Close();
            }

            AssetDatabase.Refresh();
        }

    }

}