using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using CoverageFramework;
using CoverageFramework.AstGenerator.C;
using CoverageFramework.AstGenerator.Python2;
using CoverageFramework.Etc;
using CoverageFramework.Embedder;
using CoverageFramework.Operator.Decorator.C;
using CoverageFramework.Operator.Generator.C;
using CoverageFramework.Utility;
using CoverageFramework.Operator.Selector.C;
using CoverageFramework.Operator.Selector;
using CoverageFramework.CodeGenerator.C;
using CoverageFramework.AstGenerator.Java;
using CoverageFramework.Operator.Decorator.Java;
using CoverageFramework.Operator.Selector.Java;
using CoverageFramework.Operator.Generator.Java;
using System.Runtime.Serialization.Formatters.Binary;
using CoverageFramework.CodeGenerator.Java;
using CoverageFramework.Model;
using CoverageFramework.Operator.Decorator.Python;
using CoverageFramework.Operator.Generator.Python;
using CoverageFramework.Operator.Selector.Python;
using CoverageFramework.CodeGenerator.Python;
using Paraiba.Text;
using AstGenerator=CoverageFramework.AstGenerator.Java.AstGenerator;

namespace CoverageTool
{
	public partial class MainForm : Form
	{
		//static readonly ScriptRuntime runtime = ScriptRuntime.CreateFromConfiguration();

		public MainForm()
		{
			InitializeComponent();
		}

		private void MainForm_DragDrop(object sender, DragEventArgs e)
		{

		}

		private void MainForm_DragEnter(object sender, DragEventArgs e)
		{

		}

		private void btnGo_Click(object sender, EventArgs e)
		{
			//var engine = runtime.GetEngine("IronPython");
			//var script = engine.CreateScriptSourceFromString(textBox1.Text);
			//script.Execute(runtime.CreateScope());
		}

		private static void CreateDirectory(string path)
		{
			var dirInfo = new DirectoryInfo(Path.GetDirectoryName(path));
			var notExistDirInfos = new Stack<DirectoryInfo>();
			// 存在しないディレクトリを記憶する
			while (dirInfo != null && !dirInfo.Exists)
			{
				notExistDirInfos.Push(dirInfo);
			    dirInfo = dirInfo.Parent;
			}
			// 親のディレクトリから順に作成する
			foreach (var notExistDirInfo in notExistDirInfos)
			{
				notExistDirInfo.Create();
			}
		}

		private void GenerateC(IEnumerable<CodePath> codePathes, string outDirPath)
		{
			var xmlGenerator = new CoverageFramework.AstGenerator.C.AstGenerator();
			var astCleaner = new ASTCleaner();
			var reformer = new BlockEmbedder();
			var lackingBlockSelector = new LackingBlockStatementSelectorForC();
			var blockGen = new BlockDecoratorForC();

			var stmtCovGen = new StatementCoverageGeneratorForC();
			var condCovGen = new BranchCoverageDecoratorForC();

			var ifSelector = XElementSelectorUtil.Chain(
				new IfSelectorForC(),
				new ParenthesisSelectorForC());
			var condSelector = new AtomicBooleanTermSelectorForC();
			var stmtSelector = new AtomicStatementSelector();
			var switchSelector = new SwitchSelectorForC();
			var switchCaseSelector = new SwitchCaseSelectorForC();

			var stmtCov = new StatementCoverageEmbedder();
			var branchCov = new DecisionCoverageEmbedder();
			var condCov = new ConditionCoverageEmbedder();
			var switchCov = new SwitchCoverageEmbedder();

			var importGen = new ImportGeneratorForC();
			var importEmbeder = new ImportEmbeder();

			var codeGenerator = new SourceCodeGeneratorForC();

			// カバレッジ情報
			var covInfos = new Dictionary<string, CoverageInfomation>();

			foreach (var codePath in codePathes)
			{
				var fileInfo = new FileInfo(codePath.FullPath);
				var fname = Path.ChangeExtension(fileInfo.Name, null);

				var xdoc = xmlGenerator.GenerateXml(fileInfo.FullName);
				// 整形
				xdoc = astCleaner.Clean(xdoc);
				reformer.Reform(xdoc, lackingBlockSelector, blockGen);
				new XMLNodeNumberer().Number(xdoc.Root);

				// 埋め込み
				var tagger = new Tagger(codePath.AdditionalPath);
				stmtCov.Embed(xdoc.Root, stmtSelector, stmtCovGen, tagger, covInfos);
				condCov.Embed(xdoc.Root, ifSelector, condSelector, condCovGen, tagger, covInfos);
				branchCov.Embed(xdoc.Root, ifSelector, condCovGen, tagger, covInfos);
				switchCov.Embed(xdoc.Root, switchSelector, switchCaseSelector, stmtCovGen, tagger, covInfos);
				importEmbeder.Embed(xdoc.Root, importGen);

				// コード生成
				var codeString = codeGenerator.Generate(xdoc);
				var path = Path.Combine(outDirPath, codePath.AdditionalPath);
				CreateDirectory(path);
				using (var writer = new StreamWriter(path, false, XEncoding.SJIS))
				{
					writer.Write(codeString);
				}

				try
				{
					File.Copy("codes/covman.c", Path.Combine(Path.GetDirectoryName(path), "covman.c"), true);
				} catch (Exception)
				{
				}
				try
				{
					File.Copy("codes/covman.h", Path.Combine(Path.GetDirectoryName(path), "covman.h"), true);
				} catch (Exception)
				{
				}
			}

			using (var fs = new FileStream(Path.Combine(outDirPath, "coverageinfo"), FileMode.Create))
			{
				var formatter = new BinaryFormatter();
				formatter.Serialize(fs, covInfos);
			}
		}

		private void GenerateJava(IEnumerable<CodePath> codePathes, string outDirPath)
		{
			var xmlGenerator = new AstGenerator();
			var astCleaner = new ASTCleaner();
			var reformer = new BlockEmbedder();
			var lackingBlockSelector = new LackingBlockStatementSelectorForJava();
			var blockGen = new BlockDecoratorForJava();

			var stmtCovGen = new StatementCoverageGeneratorForJava();
			var condCovGen = new BranchCoverageDecoratorForJava();

			var ifSelector = XElementSelectorUtil.Chain(
				new IfSelectorForJava(),
				new ParenthesisSelectorForJava()); /*new IfConditionExpressionSelectorForJava()*/
			var condSelector = new AtomicBooleanTermSelectorForJava();
			var stmtSelector = new AtomicStatementSelector();
			var switchSelector = new SwitchSelectorForJava();
			var switchCaseSelector = new SwitchCaseSelectorForJava();

			var stmtCov = new StatementCoverageEmbedder();
			var branchCov = new DecisionCoverageEmbedder();
			var condCov = new ConditionCoverageEmbedder();
			var switchCov = new SwitchCoverageEmbedder();

			var importGen = new ImportGeneratorForJava();
			var importEmbeder = new ImportEmbedderForJava();

			var codeGenerator = new SourceCodeGeneratorForJava();

			// カバレッジ情報
			var covInfos = new Dictionary<string, CoverageInfomation>();

			var codePathList = codePathes.ToArray();
			progressBar1.Maximum = codePathList.Length;
			progressBar1.Value = 0;

			foreach (var codePath in codePathes)
			{
				var fileInfo = new FileInfo(codePath.FullPath);
				var fname = Path.ChangeExtension(fileInfo.Name, null);

				var xdoc = xmlGenerator.GenerateXml(fileInfo.FullName);
				// 整形
				xdoc = astCleaner.Clean(xdoc);
				reformer.Reform(xdoc, lackingBlockSelector, blockGen);
				new XMLNodeNumberer().Number(xdoc.Root);

				// 埋め込み
				var tagger = new Tagger(codePath.AdditionalPath);
				stmtCov.Embed(xdoc.Root, stmtSelector, stmtCovGen, tagger, covInfos);
				condCov.Embed(xdoc.Root, ifSelector, condSelector, condCovGen, tagger, covInfos);
				branchCov.Embed(xdoc.Root, ifSelector, condCovGen, tagger, covInfos);
				switchCov.Embed(xdoc.Root, switchSelector, switchCaseSelector, stmtCovGen, tagger, covInfos);
				importEmbeder.Embed(xdoc.Root, importGen);

				// コード生成
				var codeString = codeGenerator.Generate(xdoc);
				var path = Path.Combine(outDirPath, codePath.AdditionalPath);
				CreateDirectory(path);
				using (var writer = new StreamWriter(path, false, XEncoding.SJIS))
				{
					writer.Write(codeString);
				}

				progressBar1.Value++;
			}

			using (var fs = new FileStream(Path.Combine(outDirPath, "coverageinfo"), FileMode.Create))
			{
				var formatter = new BinaryFormatter();
				formatter.Serialize(fs, covInfos);
			}
		}

		private void GeneratePython(IEnumerable<CodePath> codePathes, string outDirPath)
		{
			var xmlGenerator = new CoverageFramework.AstGenerator.Python2.AstGenerator();
			var astCleaner = new ASTCleaner();

			var stmtCovGen = new StatementCoverageGeneratorForPython();
			var condCovGen = new BranchCoverageDecoratorForPython();

			var ifSelector = new IfConditionExpressionSelectorForPython();
			var condSelector = new AtomicBooleanTermSelectorForPython();
			var stmtSelector = new AtomicStatementSelectorForPython();

			var stmtCov = new StatementCoverageEmbedder();
			var branchCov = new DecisionCoverageEmbedder();
			var condCov = new ConditionCoverageEmbedder();

			var codeGenerator = new SourceCodeGeneratorForPython();

			var importGen = new ImportGeneratorForPython();
			var importEmbeder = new ImportEmbeder();

			// カバレッジ情報
			var covInfos = new Dictionary<string, CoverageInfomation>();

            foreach (var codePath in codePathes)
            {
                var fileInfo = new FileInfo(codePath.FullPath);
                var fname = Path.ChangeExtension(fileInfo.Name, null);

                var xdoc = xmlGenerator.GenerateXml(fileInfo.FullName);
                // 整形
                xdoc = astCleaner.Clean(xdoc);
                new XMLNodeNumberer().Number(xdoc.Root);

                // 埋め込み
				var tagger = new Tagger(codePath.AdditionalPath);
				stmtCov.Embed(xdoc.Root, stmtSelector, stmtCovGen, tagger, covInfos);
				condCov.Embed(xdoc.Root, ifSelector, condSelector, condCovGen, tagger, covInfos);
				branchCov.Embed(xdoc.Root, ifSelector, condCovGen, tagger, covInfos);
                importEmbeder.Embed(xdoc.Root, importGen);

                // コード生成
                var codeString = codeGenerator.Generate(xdoc);
                var path = Path.Combine(outDirPath, codePath.AdditionalPath);
                CreateDirectory(path);
                using (var writer = new StreamWriter(path, false, XEncoding.SJIS))
                {
                    writer.Write(codeString);
                }

                try
                {
					File.Copy("codes/covman.py", Path.Combine(Path.GetDirectoryName(path), "covman.py"));
                }
                catch (Exception)
                {
                }
            }

		    using (var fs = new FileStream(Path.Combine(outDirPath, "coverageinfo"), FileMode.Create))
			{
				var formatter = new BinaryFormatter();
				formatter.Serialize(fs, covInfos);
			}
		}

		private void textBox1_TextChanged(object sender, EventArgs e)
		{

		}

		private void textBox1_DragEnter(object sender, DragEventArgs e)
		{
			e.Effect = DragDropEffects.All;
		}

        private void textBox1_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                var filePathes = ((string[])e.Data.GetData(DataFormats.FileDrop));
                    

                switch (cmbLanguage.Text)
                {
                case "C":
					GenerateC(filePathes.SelectMany(path => EnumerateFiles(path, "*.c")), txtOutput.Text);
                    break;
                case "Java":
					GenerateJava(filePathes.SelectMany(path => EnumerateFiles(path, "*.java")), txtOutput.Text);
                    break;
                case "Python":
					GeneratePython(filePathes.SelectMany(path => EnumerateFiles(path, "*.py")), txtOutput.Text);
                    break;
                }
            	textBox1.Text = "埋め込み完了";
            }
        }

	    private IEnumerable<CodePath> EnumerateFiles(string path, string pattern)
		{
			var fullPath = Path.GetFullPath(path);

            if (Directory.Exists(path))
            {
            	fullPath = Directory.GetParent(fullPath).FullName;
				if (!fullPath.EndsWith("\\")) fullPath += "\\";
				var subPathes = Directory.GetFiles(fullPath, pattern, SearchOption.AllDirectories);
                foreach (var subPath in subPathes)
                {
                    var fullSubPath = Path.GetFullPath(subPath);
                    yield return new CodePath {
                        FullPath = fullSubPath,
                        AdditionalPath = fullSubPath.Substring(fullPath.Length)
                    };
                }
            }
            else if (File.Exists(path))
            {
                yield return new CodePath {
                    FullPath = fullPath,
                    AdditionalPath = Path.GetFileName(path)
                };
            }
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			//txtOutput.Text = @"C:\work\cobertura\src\main\java";
			txtOutput.Text = Directory.GetCurrentDirectory();
			cmbLanguage.SelectedIndex = 0;
		}
	}
}
