﻿

using System.Threading;

namespace IronPythonLeakTest
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using IronPython.Hosting;
    using IronPython.Runtime;
    using IronPython.Runtime.Exceptions;
    using IronPython.Runtime.Types;
    using Microsoft.Scripting;
    using Microsoft.Scripting.Hosting;
    using System.Windows.Forms;

    public static class ScriptExecutor
    {
        public static bool import_os = true;

        public static bool import_warnings = true;

        public static bool set_trace = true;

        private const string LIB_PATH = @"C:\Program Files (x86)\IronPython 2.7.1\Lib";

        private static readonly Dictionary<string, object> DEFAULT_EXECUTION_OPTIONS =
            new Dictionary<string, object> { { "LightweightScopes", true } };

        private static int TraceCount;

        private static void Main()
        {
            long afterFirst = 0;
            long beforeStart = 0;
            try
            {
                beforeStart = GC.GetTotalMemory(true);

                Execute(0);

                afterFirst = GC.GetTotalMemory(true);

                for (int i = 1; i < 1000; i += 1)
                {
                    Execute(i);
                }
            }
            finally
            {
                for (int i = 0; i < 5; i += 1)
                {
                    Application.DoEvents();
                    GC.Collect();
                    Thread.Sleep(1000);
                    GC.WaitForPendingFinalizers();
                }

                long afterLast = GC.GetTotalMemory(true);

                Console.WriteLine("Script Finished");
                Console.WriteLine("First run: {0}Mb, 1000 runs: {1}Mb", (afterFirst - beforeStart) / (1024 * 1024), (afterLast - beforeStart) / (1024 * 1024));
                Console.ReadLine();
            }
        }

        private static void Execute(int i)
        {
            DateTime start = DateTime.Now;

            string src = "from __future__ import print_function \n";
            
            if (import_os) src += "import os \n";

            src += string.Format("print('hallo: ', {0}) \n", i);
            
            ScriptEngine engine = Python.CreateEngine(DEFAULT_EXECUTION_OPTIONS);
            
            ScriptRuntime runtime = engine.Runtime;
            ScriptScope mainScope = engine.CreateScope();

            runtime.LoadAssembly(typeof(IronPython.Modules.PythonNT).Assembly);


            ScriptSource scriptSource = engine.CreateScriptSourceFromString(src, "TestScript", SourceCodeKind.AutoDetect);

            scriptSource.Compile(new global::IronPythonLeakTest.ErrorListenerImpl());
            ScriptSource source = scriptSource;

            // Set the search pathes.
            engine.SetSearchPaths(new[] { LIB_PATH });

            if (set_trace)
            {
                engine.SetTrace(TraceBackHandler);
            }

            if (import_warnings)
            {
                var warningModule = engine.ImportModule("warnings");
                warningModule.SetVariable("showwarning", new ShowWarning(ShowWarning));
            }

            source.Execute(mainScope);

            Console.WriteLine("{0}: {1} - {2}", i, ScriptExecutor.TraceCount, (DateTime.Now - start).Milliseconds);
        }


        private static void ShowWarning(CodeContext context, object message, PythonType category, string filename, int lineno, object file, string line)
        {
            Console.WriteLine(message ?? "");
        }

        private static TracebackDelegate TraceBackHandler(TraceBackFrame stackFrame, string stEvent, object payload)
        {

            TraceCount += 1;

            return TraceBackHandler;
        }
    }

    public delegate void ShowWarning(
        CodeContext context, object message, PythonType category, string filename, int lineno,
        [DefaultParameterValue(null)] object file, [DefaultParameterValue(null)] string line);

    class ErrorListenerImpl : ErrorListener
    {
        public override void ErrorReported(ScriptSource source, string stMessage, Microsoft.Scripting.SourceSpan span, int errorCode, Microsoft.Scripting.Severity severity)
        {
            Console.WriteLine(stMessage);
        }
    }
}


