using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using Commons.Collections;
using NVelocity;
using NVelocity.App;

namespace CreatePropertyCS
{
    class Program
    {
        static List<ClassInfo> _list = new List<ClassInfo>();
        static void Main(string[] args)
        {
            if(args.Length<2)
            {
                Console.WriteLine("Error:Excepted CreatePropertyCS.exe <input file> <output directory>");
                Environment.Exit(1);
            }

            string inputFile = args[0];
            string outputDir = args[1];

            Regex regexGet = new Regex(@"^(?<filePath>.+)\t(?<prefix>.+) (?<type>[a-zA-Z0-9_]+) get(?<name>[a-zA-Z0-9_]+)\(");
            Regex regexSet = new Regex(@"^(?<filePath>.+)\t(?<prefix>.+) void set(?<name>[a-zA-Z0-9_]+)\((?<type>[a-zA-Z0-9_]+) ");
            StreamReader sr = new StreamReader(inputFile);
            while (sr.EndOfStream == false)
            {
                Match match;
                PropInfo info = new PropInfo();
                string line = sr.ReadLine();
                if (line.StartsWith("#") == true)
                {
                    line = "";
                }
                bool isGet = false;
                bool isSet = false;
                if(regexGet.IsMatch(line))
                {
                    match = regexGet.Match(line);
                    isGet = true;
                }
                else if(regexSet.IsMatch(line))
                {
                    match = regexSet.Match(line);
                    isSet = true;
                }
                else if(line=="")
                {
                    continue;
                }
                else
                {
                    Console.WriteLine("Syntax error: {0}", line);
                    return;
                }

                PropInfo info2 = Search(match.Groups["name"].Value, match.Groups["prefix"].Value, match.Groups["type"].Value, match.Groups["filePath"].Value);
                if (isGet)
                {
                    info2.IsGet = true;
                }
                if(isSet)
                {
                    info2.IsSet = true;
                }
            }
            sr.Close();


            //重複したプロパティとSetのみのPropertyを削除
            foreach (var clsInfo in _list)
            {
                DeleteOverlapName(clsInfo.List);
                DeleteSetOnly(clsInfo.List);
            }

            string dir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            ExtendedProperties props = new ExtendedProperties();
            props.AddProperty("file.resource.loader.path", new ArrayList(new string[] { ".", dir }));
            Velocity.Init(props);


            foreach (ClassInfo clsInfo in _list)
            {
                string fileName = Path.Combine(outputDir, clsInfo.Path + "_prop.cs");
                Console.WriteLine(fileName);
                StreamWriter sw  = new StreamWriter(fileName);

                //コンテキストの設定
                VelocityContext ctx = new VelocityContext();

                //クラスをセット
                ctx.Put("cls", clsInfo);

                //Shift-JISのテンプレートファイルを読み込み、コンテキストとマージ
                Velocity.MergeTemplate("clsfiletemplate.vm",
                    "UTF-8", ctx, sw);

                sw.Close();
            }
        }

        private static void DeleteSetOnly(List<PropInfo> list)
        {
            var result = new List<PropInfo>();
            foreach (var info in list)
            {
                if(GetRank(info)<2)
                {
                    
                }
                else
                {
                    result.Add(info);
                }
            }
            list.Clear();
            list.AddRange(result);
        }

        private static void DeleteOverlapName(List<PropInfo> list)
        {
            var result = new List<PropInfo>();
            foreach (var info in list)
            {
                PropInfo findResult = result.Find(delegate(PropInfo obj) { return obj.Name == info.Name; });
                if (findResult == null)
                {
                    result.Add(info);
                }
                else
                {
                    if(Compare(info, findResult)>0)
                    {
                        result.Remove(findResult);
                        result.Add(info);
                    }
                }
            }

            list.Clear();
            list.AddRange(result);
        }

        private static int Compare(PropInfo x, PropInfo y)
        {
            int xRank = GetRank(x);
            int yRank = GetRank(y);
            return xRank - yRank;
        }

        private static int GetRank(PropInfo x)
        {
            if (x.IsGet == true && x.IsSet == true) { return 3; }
            if (x.IsGet == false && x.IsSet == true) { return 1; }
            if (x.IsGet == true && x.IsSet == false) { return 2; }
            if (x.IsGet == false && x.IsSet == false) { return 0; }
            return 0;
        }

        private static PropInfo Search(string name, string prefix, string type, string filePath)
        {
            var clsPath = SearchClass(filePath);

            foreach (var propInfo in clsPath.List)
            {
                if (propInfo.Name == name &&
                    propInfo.Type == type)
                {
                    return propInfo;
                }
            }

            var info = new PropInfo();
            info.Name = name;
            info.Prefix = prefix;
            info.Type = type;
            clsPath.List.Add(info);
            return info;
        }
        private static ClassInfo SearchClass(string filePath)
        {
            foreach (var info in _list)
            {
                if(info.Path == filePath)
                {
                    return info;
                }
            }
            var clsInfo = new ClassInfo();
            clsInfo.Path = filePath;
            _list.Add(clsInfo);
            return clsInfo;
        }
    }
}
