﻿using Microsoft.CodeAnalysis.CSharp.Scripting;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Scripting;
// using System.Runtime;
// using Microsoft.CodeAnalysis.Scripting.Hosting;
// using System.Threading;

namespace CsharpScripting
{
    class TestEvaluationWithRoslynScriptingApi
    {
        public async Task<int> Test(int loopNumber)
        {
            // add ConfigureAwait(false) ?!?!?

            // is async a virus ?   https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
            
            // see this http://stackoverflow.com/a/36355946/5288052 and this http://stackoverflow.com/a/9343733/5288052

            // well written code   <take some code from here>
            // https://github.com/neuecc/EtwStream/blob/master/EtwStream.Service/ScriptingEvaluator.cs

            // base documentation
            // see 'Waiting for one or more tasks to complete' from   https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples

            // read this for async constructor
            // http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

            // about async await
            // base documentation   https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx
            // 
            // other
            // http://stackoverflow.com/questions/11836325/await-operator-can-only-be-used-within-an-async-method
            // http://blog.stephencleary.com/2012/02/async-and-await.html
            // https://msdn.microsoft.com/en-us/library/mt674882.aspx
            // https://msdn.microsoft.com/en-us/library/hh873173.aspx
            // http://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c
            // http://stackoverflow.com/questions/13002507/how-can-i-call-async-go-method-in-for-example-main

            // code from Roslyn
            // source root   https://github.com/dotnet/roslyn/tree/a7319e2bc8cac34c34527031e6204d383d29d4ab/src/Scripting/CSharp
            // CSharpScript.cs   https://github.com/dotnet/roslyn/blob/master/src/Scripting/CSharp/CSharpScript.cs
            // ScriptOptions.cs   https://github.com/dotnet/roslyn/blob/master/src/Scripting/Core/ScriptOptions.cs

            // from Roslyn tests: examples of Default.AddReferences(HostAssembly)
            // http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests/InteractiveSessionTests.cs,58195aef40c4bd62
            // http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests/InteractiveSessionTests.cs,58195aef40c4bd62,references
            //
            // other examples from Roslyn tests
            // http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests/ScriptTests.cs
            // 
            // examples about the old scripting engine (see "Interacting with the Host Application")
            // https://blogs.msdn.microsoft.com/csharpfaq/2011/12/02/introduction-to-the-roslyn-scripting-api/
            //
            // other
            // http://stackoverflow.com/questions/20097326/how-do-i-add-references-using-latest-roslyn-api-c-script-execution
            // "GetType().Assembly" from here https://gist.github.com/amazedsaint/3828951


            /*
            string script = @"using CsharpScripting;
                testMethodUsedInScript = new TestMethodUsedInScript();
                testMethodUsedInScript.Min(1, 2);";
            */

            /*
            string script = @"using CsharpScripting;
                TestMethodUsedInScript testMethodUsedInScript = new TestMethodUsedInScript();
                TestMethodUsedInScript2 testMethodUsedInScript2 = new TestMethodUsedInScript2();
                int a = 1;
                int b = 2;
                int c = 3;
                int d = testMethodUsedInScript.Min(a, b);
                d = d * 10;
                d = testMethodUsedInScript2.Max(d, c);
                return d;";
            */

            string script = @"
                TestMethodUsedInScript testMethodUsedInScript = new TestMethodUsedInScript();
                TestMethodUsedInScript2 testMethodUsedInScript2 = new TestMethodUsedInScript2();
                int a = 1;
                int b = 2;
                int c = 3;
                int d = testMethodUsedInScript.Min(a, b);
                d = d * 10;
                d = testMethodUsedInScript2.Max(d, c);
                return d;";

            // int result = await CSharpScript.EvaluateAsync<int>("1 + 2").ConfigureAwait(false);   <<< 'ConfigureAwait(false)' is used when calling an Async from a non async
            // int result = await CSharpScript.EvaluateAsync<int>(script, ScriptOptions.Default.WithReferences(typeof(TestEvaluationWithRoslynScriptingApi).Assembly));
            // int result = await CSharpScript.EvaluateAsync<int>(script, ScriptOptions.Default.WithReferences(this.GetType().Assembly));

            var options = ScriptOptions.Default
                .AddReferences(new[]
                {
                    this.GetType().Assembly
                })
                .WithImports(
                    "System",
                    "CsharpScripting");

            int result = 0;

            var state = await CSharpScript.RunAsync<int>(script, options).ConfigureAwait(false);

            for (int i = 0; i < loopNumber; i++)
            {
                state = await state.ContinueWithAsync<int>(script).ConfigureAwait(false);
                result = result + state.ReturnValue;
            }

            return result;
        }
    }
}
