using csscript;
using CSScriptLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Nappat.Testing
{
    public interface IScriptInterface
    {
        event EventHandler NewMessage;
    }

    class FileExecutor
    {
        #region InterpretingStatus
        public delegate void ExecutingStatusEventHandler();
        public event ExecutingStatusEventHandler ExecutingStart;
        public event ExecutingStatusEventHandler ExecutingStop;
        public void OnExecutingStart()
        {
            if (ExecutingStart != null)
                ExecutingStart();
        }
        public void OnExecutingStop()
        {
            if (ExecutingStop != null)
                ExecutingStop();
        }
        #endregion

        #region Private Fields
        DataSender dataSender;
        string interpretedCode;
        string[] logString = new string[10];
        #endregion

        #region Constructors
        public FileExecutor(DataSender dataSender)
        {
            this.dataSender = dataSender;
        }
        #endregion

        public void Execution()
        {
            interpretedCode = new StreamReader(dataSender.CsFilePaths).ReadToEnd();

            SetDriver();

            try
            {
                Assembly assembly = CSScript.LoadCode(interpretedCode);
                Object obj = assembly.CreateObject("AutomatedTesting." + TestNameMethod());
                IScriptInterface boundObject = obj.AlignToInterface<IScriptInterface>();
                Thread th = null;

                if ((bool)(obj.GetType().GetMethod("Init").Invoke(obj, null)))
                {
                    boundObject.NewMessage += (s, e) =>
                    {
                        this.logString = obj.GetType().GetField("messenger").GetValue(obj) as string[];
                        this.dataSender.LogMessages = new LogLine(logString[1], logString[2], logString[3], logString[4], logString[5]);
                        if (logString[3] == "FatalError")
                        {
                            //OnExecutingStop();
                            //throw new Exception("Fatal error was detected during script execution.");
                            th.Abort();
                        }
                    };
                    this.dataSender.TestTime = (long)(obj.GetType().GetField("testTime").GetValue(obj));
                    OnExecutingStart();
                    //obj.GetType().GetMethod("Run").Invoke(obj, null);
                    th = new Thread(() => obj.GetType().GetMethod("Run").Invoke(obj, null));
                    th.Start();
                    th.Join();
                    OnExecutingStop();
                }
                else
                    throw new Exception("File: " + TestNameMethod() + " was not initialized.");
            }
            catch (CompilerException e)
            {
                throw new Exception(e.Message);
            }
        }

        #region Private Methods
        private string TestNameMethod()
        {
            int position = interpretedCode.IndexOf("public class ");
            List<char> testNameList = new List<char>();
            for (int i = position + 12; interpretedCode[i] != '\r'; i++)
            {
                testNameList.Add(interpretedCode[i]);
            }
            return (String.Join("", testNameList)).Trim();
        }
        private void SetDriver()
        {
            //string librariesString =
            //"//css_reference " + dataSender.DriverPaths + "Libraries\\TestTypes.dll\r\n" +
            //"//css_reference " + dataSender.DriverPaths + "Libraries\\TestInterfaces.dll\r\n" +
            //"//css_reference " + dataSender.DriverPaths + "Libraries\\TestConfigurator.dll\r\n";
            //interpretedCode = interpretedCode.Insert(0, librariesString);
            string librariesString =
            "//css_reference " + System.Environment.CurrentDirectory + "\\TestTypes.dll\r\n" +
            "//css_reference " + System.Environment.CurrentDirectory + "\\TestInterfaces.dll\r\n" +
            "//css_reference " + System.Environment.CurrentDirectory + "\\TestConfigurator.dll\r\n";
            interpretedCode = interpretedCode.Insert(0, librariesString);
        }
        #endregion
    }
}
