using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Irony.Interpreter;
using System.Windows.Forms;


namespace ScriptRunner
{
    public class Runner
    {
        //private myBuiltInMethod.BrowserScriptInterfaces weblib = null;
        //private myBuiltInMethod.FormScriptInterfaces formlib = null;
        private myScriptEvaluator Interpreter;
        public Dictionary<string , Irony.Interpreter.BuiltInMethod> methodlist;

        //非シグナル状態でManualResetEventオブジェクトを作成
        public ManualResetEvent manualEvent_script = new System.Threading.ManualResetEvent(false);
        //public ManualResetEvent manualEvent_debugger = new System.Threading.ManualResetEvent(false);

        //スレッドに渡すパースツリー
        Irony.Parsing.ParseTree parsedScript;

        //このクラスを呼び出した親ウィンドウ
        public IWin32Window _ParentWindow;

        EventHandler<ConsoleWriteEventArgs> _PrintEventHandler;
        EventHandler<ConsoleWriteEventArgs> _ClearTextEventHandler;


        public Runner(EventHandler<ConsoleWriteEventArgs> PrintEventHandler , EventHandler<ConsoleWriteEventArgs> ClearTextEventHandler)
        {
            this._PrintEventHandler = PrintEventHandler;
            this._ClearTextEventHandler = ClearTextEventHandler;
            InitScript();
        }





        //private void InitInterfaces()
        //{
        //    formlib = new myBuiltInMethod.FormScriptInterfaces();
        //    //formlib.Add(this.textBox_info.Name , this.textBox_info);
        //    //weblib = new BrowserScriptInterfaces(this.geckoWebBrowser1);

            

        //}


        private void InitScript()
        {
            //InitInterfaces();

            Interpreter = new myScriptEvaluator(new myScriptGrammar());

            //デバッグ用に結びつけておく
            manualEvent_script = new System.Threading.ManualResetEvent(false);
            Interpreter.App.manualEvent_script = this.manualEvent_script;
            //Interpreter.App.manualEvent_debugger = this.manualEvent_debugger;


            //Running in Grammar Explorer　グラマー定義から引用
            Interpreter.Globals.Add("null" , Interpreter.Runtime.NoneValue);
            Interpreter.Globals.Add("true" , true);
            Interpreter.Globals.Add("false" , false);
         



            //プリントイベント拾う
            Interpreter.App.ConsoleWrite += onPrint;
            Interpreter.App.ClearText += onClearText;

            //組み込み関数をDLLから読み込む
            loadDLL();
            
        }

        private void loadDLL()
        {
            //組み込み関数をDLLから読み込む
            var dllpath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location).Replace(@"\", @"\\") + @"\\myBuiltInMethod.dll";
            var className = "myBuiltInMethod.ScriptBuiltInMethod";
            var loaddllstr = string.Format(@"loadDLL(""{0}"",""{1}"")", dllpath, className);


            //読み込んだメソッドを辞書にしておき、インテリセンスで使用する。
            methodlist = new Dictionary<string, Irony.Interpreter.BuiltInMethod>();


            //************************
            //base
            //************************
            //DLL読み込みすると、返値として指定したクラスのインスタンスがobjectとして返却される。
            //今回は、ScriptBuiltInMethodのインスタンス
            var ret = Interpreter.App.Evaluate(loaddllstr) as object[];// as myBuiltInMethod.ScriptBuiltInMethod;
            var baseMethodslist = (Dictionary<string, Irony.Interpreter.BuiltInMethod>)ret[1];
            
            //baseメソッドを 
            //インテリセンス用辞書に登録
            foreach(var item in baseMethodslist)
                methodlist.Add(item.Key, item.Value);

            //************************
            //Browser
            //************************
            var tr = new ExportMethodBase(typeof(myBuiltInMethod.BrowserScriptInterfaces));//, formlib
            var Browsermethodlist = tr.CheckAttrAndExport();



            //Action extmethod =
            //    () =>
            //    {
            //        weblib.BrowserWait(false);
            //        System.Diagnostics.Debug.WriteLine("exmethod");
            //    };
            //var tr = new ExportMethod2(typeof(myBuiltInMethod.BrowserScriptInterfaces), typeof(BrowserBusyCheckAttribute), extmethod);

            //ブラウザメソッドを 
            //1. 環境に登録
            //2. インテリセンス用辞書に登録
            foreach(var item in Browsermethodlist)
            {
                Interpreter.App.Runtime.BuiltIns.AddMethod(item.Value, item.Key);
                methodlist.Add(item.Key, item.Value);
            }

            //************************
            //BasicCLR
            //************************
            var tr2 = new ExportMethodBase(typeof(myBuiltInMethod.BasicMethodInterfaces));//, formlib
            var BasicCLRmethodlist = tr2.CheckAttrAndExport();

            //ブラウザメソッドを 
            //1. 環境に登録
            //2. インテリセンス用辞書に登録
            foreach(var item in BasicCLRmethodlist)
            {
                Interpreter.App.Runtime.BuiltIns.AddMethod(item.Value, item.Key);
                methodlist.Add(item.Key, item.Value);
            }
        }


        private void loadDLL2()
        {
            //組み込み関数をDLLから読み込む
            var dllpath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location).Replace(@"\", @"\\") + @"\\myBuiltInMethod.dll";
            var className = "myBuiltInMethod.ScriptBuiltInMethod";
            var loaddllstr = string.Format(@"loadDLL(""{0}"",""{1}"")", dllpath, className);


            //読み込んだメソッドを辞書にしておき、インテリセンスで使用する。
            methodlist = new Dictionary<string, Irony.Interpreter.BuiltInMethod>();


            //************************
            //base
            //************************
            //DLL読み込みすると、返値として指定したクラスのインスタンスがobjectとして返却される。
            //今回は、ScriptBuiltInMethodのインスタンス
            var ret = Interpreter.App.Evaluate(loaddllstr) as object[];// as myBuiltInMethod.ScriptBuiltInMethod;
            var baseMethodslist = (Dictionary<string, Irony.Interpreter.BuiltInMethod>)ret[1];

            //baseメソッドを 
            //インテリセンス用辞書に登録
            foreach(var item in baseMethodslist)
                methodlist.Add(item.Key, item.Value);

            //************************
            //Browser
            //************************
            var brins = new myBuiltInMethod.BrowserScriptInterfaces();
            Action extmethod =
                () =>
                {
                    brins.BrowserWait(false);
                    System.Diagnostics.Debug.WriteLine("exmethod");
                };

            var tr = new ExportMethod2(typeof(myBuiltInMethod.BrowserScriptInterfaces), typeof(myBuiltInMethod.BrowserBusyCheckAttribute), extmethod);
            var Browsermethodlist = tr.CheckAttrAndExport();
            //ブラウザメソッドを 
            //1. 環境に登録
            //2. インテリセンス用辞書に登録
            foreach(var item in Browsermethodlist)
            {
                Interpreter.App.Runtime.BuiltIns.AddMethod(item.Value, item.Key);
                methodlist.Add(item.Key, item.Value);
            }

            //************************
            //BasicCLR
            //************************
            var tr2 = new ExportMethodBase(typeof(myBuiltInMethod.BasicMethodInterfaces));//, formlib
            var BasicCLRmethodlist = tr2.CheckAttrAndExport();

            //ブラウザメソッドを 
            //1. 環境に登録
            //2. インテリセンス用辞書に登録
            foreach(var item in BasicCLRmethodlist)
            {
                Interpreter.App.Runtime.BuiltIns.AddMethod(item.Value, item.Key);
                methodlist.Add(item.Key, item.Value);
            }
        }

        private bool runScriptInthread = false;
        public void Run(string script)
        {
            Interpreter.App.OutputBuffer.Clear();

            //テキストを渡すEvaluateではなく、parsetreenodeを渡すを使う。
            parsedScript = Interpreter.Parser.Parse(script);
            if (parsedScript.HasErrors())
            {
                throw new ScriptException("Syntax errors found.");
            }

            if(runScriptInthread)
            {

                //インタプリタをスレッド起動
                System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(runImpl));
                //フォーム（厳密にはCOM）を使っていたら、これを指定する必要がある
                t.SetApartmentState(ApartmentState.STA);
                t.Start();

                //インタプリタに実行開始させる
                manualEvent_script.Set();
                while(t.IsAlive)
                {
                    Thread.Sleep(100);
                    Application.DoEvents();
                }
            }
            else
            {
                runImpl();
            }

        }

            private void runImpl()
            {
                try
                {
                    Interpreter.App.Evaluate(parsedScript);
                }
                catch (ScriptException ex)
                {
                    MessageBox.Show(ex.Location.ToUiString() + ex.Message);
                }
            }


        public void Reset()
        {
            //if(methodlist != null)
            //    methodlist.Clear();

            //開放されるか確認・・・・
            manualEvent_script.Close();
            manualEvent_script = null;

            Interpreter.App.DataMap = null;
            

            Interpreter = null;
            GC.Collect();

            InitScript();

        }
        
        public void Resume()
        {
            manualEvent_script.Set();
            
        }


        public void debug()
        {

            var global_symbols = this.Interpreter.App.Evaluate("GetGlobalSymbols()") as List<MyScriptEvaluatorRuntime.SymbolInfo> ;
            var local_symbols = this.Interpreter.App.Evaluate("GetlocalSymbols()") as List<MyScriptEvaluatorRuntime.SymbolInfo>;
            foreach (var item in local_symbols)
            {
                System.Diagnostics.Debug.WriteLine(item.symbol);
                System.Diagnostics.Debug.WriteLine(item.value.ToString());
            }
            

        }
        public void  watchVariables(ListView listview)
        {
            var global_symbols = this.Interpreter.App.Evaluate("getglobalsymbols()") as List<MyScriptEvaluatorRuntime.SymbolInfo>;
            var local_symbols = this.Interpreter.App.Evaluate("getlocalsymbols()") as List<MyScriptEvaluatorRuntime.SymbolInfo>;
            
            listview.Items.Clear();
            
            //Interpreter.App.manualEvent_script.
            foreach (var item in local_symbols)
            {
                if (item.level == 0) continue;

                if(item.value == null) continue;

                var valstr = item.value as string; 
                if (valstr == null)
                    valstr = "null";
                
                listview.Items.Add(new ListViewItem(new string[] { item.symbol , valstr , item.value.GetType().ToString() }));
            }

            foreach (var item in global_symbols)
            {
                if(item.value == null) continue;
                var valstr = item.value as string; //.ToString();
                if (valstr == null)
                    valstr = "null";

                listview.Items.Add(new ListViewItem(new string[] { item.symbol , valstr , item.value.GetType().ToString() }));
            }

            
        }
        //private ListViewItem makelistitem(MyScriptEvaluatorRuntime.SymbolInfo symbol)
        //{
        //    var item = new ListViewItem(new string[] { symbol.symbol , symbol.value as string});
        //}



        private void onPrint(object sender , ConsoleWriteEventArgs e)
        {
            if (_PrintEventHandler != null)
                _PrintEventHandler(sender , e);
        }

        private void onClearText(object sender, ConsoleWriteEventArgs e)
        {
            if(_ClearTextEventHandler != null)
                _ClearTextEventHandler(sender, e);
        }


        //private void onaddOwnedForm(object sender, AddOwnedFormEventArgs e)
        //{
        //    if(_AddOwnedFormEventHandler != null)
        //        _AddOwnedFormEventHandler(sender, e);
        //}

    }
}
