using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace CGL.Graphics.XFileLib.Text
{
    static class XFileTextReader
    {
        public static XFile FromTxtFile(string xfile, FloatMode floatMode)
        {
            //改行排除
            xfile = xfile.Replace("\r", "");
            xfile = xfile.Replace("\n", "");
            //ヘッダ排除
            xfile = xfile.Substring(16);
            //{}のツリーを作成
            dynamic xtree = BuildTree(ref xfile);

            //テンプレート読み込み
            Dictionary<string, XTemplateInfo> templateInfo;
            Dictionary<string, string> UUIDTotmpName;
            xtree = ReadTemplates(xtree, out templateInfo, out UUIDTotmpName);
            //データの読み込み
            XFile result = new XFile();
            Regex data_regex = new Regex(@"(?<type>[a-zA-Z_0-9]+)(?<id>(\s+?[a-zA-Z_0-9]+?)?)\s*?", RegexOptions.IgnoreCase);
            for (int i = 0; i < xtree.Count; )
            {
                MatchCollection mc = data_regex.Matches(xtree[i]);
                if (mc.Count == 0)
                {
                    ++i; continue;//ここに来るはず無いんだけどね……
                }
                xtree.RemoveAt(i);
                Match match = mc[0];
                string typename = match.Groups["type"].Value;
                string id = match.Groups["id"].Value;
                if (string.IsNullOrEmpty(id))
                    id = typename;
                dynamic data = xtree[i];
                xtree.RemoveAt(i);
                result.FileData.Add(id, BuildData(typename, floatMode, ref data, templateInfo, UUIDTotmpName));
                
            }
            return result;
        }
        static dynamic BuildTree(ref string source)
        {
            List<dynamic> result = new List<dynamic>();
            while (true)
            {
                int pos1 = source.IndexOf('{');
                int pos2 = source.IndexOf('}');
                
                if (pos1 != -1 && pos1<pos2)
                {
                    string data = source.Substring(0, pos1);
                    if (!string.IsNullOrWhiteSpace(data))
                        result.Add(data);
                    source = source.Substring(pos1 + 1).Trim();
                    result.Add(BuildTree(ref source));
                }
                else
                {
                    if (pos2 != -1)
                    {
                        string data = source.Substring(0, pos2);
                        if (!string.IsNullOrWhiteSpace(data))
                            result.Add(data);
                        source = source.Substring(pos2 + 1);
                        return result;
                    }
                    else
                    {
                        result.Add(source);
                        source = "";
                        return result;
                    }
                }
            }
        }
        static dynamic ReadTemplates(dynamic xtree, out Dictionary<string, XTemplateInfo> templateInfo, out Dictionary<string, string> UUIDTotmpName)
        {
            //テンプレートを抜き出し
            MatchCollection mc;
            Regex template_regex = new Regex(@"template\s+?(?<template_name>[a-zA-Z_0-9]+)\s*?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
            Regex uuid_regex = new Regex(@"\<(?<uuid>.+?)\>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            Regex field_regex = new Regex(@"(?<array>(array\s+?)?)(?<type>[a-zA-Z_0-9]+?)\s+?(?<field>[a-zA-Z_0-9]+?)(?<dimsize>(\[[a-zA-Z_0-9\[\]]+?\])?)\;", RegexOptions.Compiled | RegexOptions.IgnoreCase);

            //テンプレートの解析
            templateInfo = new Dictionary<string, XTemplateInfo>();
            UUIDTotmpName = new Dictionary<string, string>();
            for (int i = 0; i < xtree.Count; ++i)
            {
                if (xtree[i] is string)
                {
                    mc = template_regex.Matches(xtree[i]);
                    if (mc.Count > 0)
                    {
                        Match match = mc[0];
                        string temp_data = xtree[i + 1][0];
                        xtree.RemoveAt(i);
                        xtree.RemoveAt(i);
                        --i;
                        //テンプレート名の抜き出し
                        string tmpName = match.Groups["template_name"].Value;
                        //テンプレートの中身解析
                        //uuid抜き出し
                        string uuid = "";
                        Match m;
                        if ((m = uuid_regex.Match(temp_data)) != null)
                            uuid = m.Groups["uuid"].Value;
                        temp_data = uuid_regex.Replace(temp_data, "");
                        //テンプレートオブジェクト生成
                        XTemplateInfo template = new XTemplateInfo(uuid);
                        //フィールド抜き出し
                        MatchCollection fmc = field_regex.Matches(temp_data);
                        temp_data = field_regex.Replace(temp_data, "");
                        foreach (Match fmatch in fmc)
                        {
                            XTypeInfo typeinfo = new XTypeInfo();
                            string array = fmatch.Groups["array"].Value;
                            typeinfo.TypeName = fmatch.Groups["type"].Value;
                            typeinfo.FieldName = fmatch.Groups["field"].Value;
                            string dimensions = fmatch.Groups["dimsize"].Value;

                            if (!string.IsNullOrEmpty(array))
                            {
                                dimensions = dimensions.Trim();
                                dimensions = dimensions.Substring(1, dimensions.Length - 2);
                                typeinfo.Dimensions = new List<string>(dimensions.Split(new string[1] { "][" }, StringSplitOptions.RemoveEmptyEntries));
                            }
                            else
                                typeinfo.Dimensions = null;
                            template.Fields.Add(typeinfo.FieldName, typeinfo);
                        }
                        templateInfo.Add(tmpName, template);
                        UUIDTotmpName.Add(template.UUID, tmpName);
                    }
                }
            }
            
            return xtree;
        }
        static Dictionary<string, dynamic> BuildData(string typename,FloatMode floatMode, ref dynamic data, Dictionary<string, XTemplateInfo> templates, Dictionary<string, string> UUIDInfo)
        {
            Dictionary<string, dynamic> result = new Dictionary<string, dynamic>();
            //型情報取得
            XTemplateInfo temp = templates[typename];
            //データの処理
            for (int i = 0; i < data.Count; )
            {
                string strData = (string)data[i];
                data.RemoveAt(i);
                //フィールド情報ごとに処理
                foreach (var field in temp.Fields)
                {
                    dynamic value = ReadData(field.Value, floatMode, ref strData, templates, UUIDInfo, result);
                    result.Add(field.Value.FieldName, value);
                }
                strData = strData.Trim();
                //開いたテンプレートの分を読み込み
                Regex openRegex = new Regex(@"^\s*?(?<typename>[a-zA-Z_]+)\s*?", RegexOptions.IgnoreCase);
                while (true)
                {
                    MatchCollection mc = openRegex.Matches(strData);
                    if (mc.Count > 0)
                    {
                        if (data.Count == 0)
                        {
                            data.Add(strData);
                            return result;
                        }
                        else
                        {
                            Match match = mc[0];
                            string innertype = match.Groups["typename"].Value.Trim();
                            dynamic data2 = data[i];
                            data.RemoveAt(i);
                            result.Add(innertype, BuildData(innertype, floatMode, ref data2, templates, UUIDInfo));
                            if (data.Count > 0)
                            {
                                strData = data[i];
                                data.RemoveAt(i);
                                continue;
                            }
                            
                        }
                    }
                    else if (strData.Length > 0)
                    {
                        data.Add(strData);
                        return result;
                    }
                    break;
                }
            }
            return result;
        }
        static bool IsPrimitive(string typename)
        {
            switch (typename.ToUpper())
            {
                case "WORD":
                case "DWORD":
                case "FLOAT":
                case "DOUBLE":
                case "CHAR":
                case "UCHAR":
                case "BYTE":
                case "STRING":
                    return true;
                case "CSTRING":
                case "UNICODE":
                    throw new FileLoadException(typename + "型をXファイル形式で使うことは出来ません");
                default:
                    return false;
            }
        }
        static dynamic ReadData(XTypeInfo field, FloatMode floatMode, ref string data, Dictionary<string, XTemplateInfo> templates, Dictionary<string, string> UUIDInfo,Dictionary<string,dynamic> othreFields)
        {
            dynamic returnvalue;
            if (field.Dimensions != null && field.Dimensions.Count > 0)
            {//配列
                XTypeInfo newfield = (XTypeInfo)field.Clone();
                int dimension;
                if (!int.TryParse(newfield.Dimensions[0], out dimension))
                {
                    //他のフィールド値が知っている？
                    dimension = (int)othreFields[newfield.Dimensions[0]];
                }
                newfield.Dimensions.RemoveAt(0);
                //配列データ読み込み
                List<dynamic> array = new List<dynamic>();
                for (int i = 0; i < dimension; ++i)
                {
                    if (i != 0)
                    {//配列の各値にある,をチェックして除く
                        if (data.Length>0 && data[0] == ',')
                            data = data.Substring(1);
                    }
                    array.Add(ReadData(newfield,floatMode, ref data, templates, UUIDInfo, othreFields));                    
                }
                //配列の最後にある;をチェックして除く
                if (data.Length > 0 && data[0] == ';')
                    data = data.Substring(1);
                returnvalue = array.ToArray();
            }
            else
            {
                if (IsPrimitive(field.TypeName))
                {
                    returnvalue = ReadPrimitiveData(field.TypeName, ref data, floatMode);
                }
                else
                {
                    dynamic temp = new List<string>();
                    temp.Add(data);
                    returnvalue = BuildData(field.TypeName, floatMode, ref temp, templates, UUIDInfo);
                    if (temp.Count > 0)
                    {
                        data = temp[0];
                        //フィールドの最後にある;をチェックして除く
                        if (data.Length > 0 && data[0] == ';')
                            data = data.Substring(1);
                    }
                    
                }
            }
            return returnvalue;
        }
        static dynamic ReadPrimitiveData(string typename, ref string data, FloatMode floatMode)
        {
            /*int pos1 = data.IndexOf(';');
            int pos2 = data.IndexOf(',');
            int pos;
            if (pos1 == -1 && pos2 == -1)
                pos = -1;
            else if(pos1==-1)
            string strValue = "";
            if (pos == -1)
            {
                strValue = data;
                data = "";
            }
            else
            {
                strValue = data.Substring(0, pos);
                data = data.Substring(pos + 1);
            }*/
            Regex reg_num = new Regex(@"^\s*?(?<number>[0-9\.\-]+)(\;)?", RegexOptions.IgnoreCase);
            Regex reg_str = new Regex(@"^\s*?(?<string>.*?)(\;|,)", RegexOptions.IgnoreCase);
            string strValue;
            if (typename.ToUpper() == "STRING")
            {
                strValue = reg_str.Match(data).Groups["string"].Value;
                data = reg_str.Replace(data, "");
                return strValue;
            }
            else
            {
                strValue = reg_num.Match(data).Groups["number"].Value;
                data = reg_num.Replace(data, "");
            }
            switch (typename.ToUpper())
            {
                case "WORD":
                    
                    return Convert.ToInt16(strValue);
                case "DWORD":
                    return Convert.ToInt32(strValue);
                case "FLOAT":
                    if (floatMode == FloatMode.Single)
                        return Convert.ToSingle(strValue);
                    else
                        return Convert.ToDouble(strValue);
                case "DOUBLE":
                    return Convert.ToDouble(strValue);
                case "CHAR":
                case "UCHAR":
                case "BYTE":
                    return Convert.ToByte(strValue);
                default:
                    throw new Exception("……ここに来るはず無いんだけど");
            }
        }
        
    }
}
