using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Runtime.CompilerServices;
using System.Reflection;
using Microsoft.CSharp;
using System.Security;
using System.Security.Permissions;
using System.IO;

namespace EVEDemo
{
    public class CodeCompiler
    {
        /// <summary>
        /// 
        /// Compiles the given source and executes a static 
        /// function of a class taking a list of parameters
        /// </summary>
        public static object Execute(string source,
                                     string className,
                                     string functionName,
                                     object[] parameters)
        {
            CompilerResults cr = null;
            return Execute(CompileCode(source, ref cr),
                           className,
                           functionName,
                           parameters);
        }

        /// <summary>
        /// 
        /// Compiles the given source and executes a static 
        /// function of a class taking no parameters
        /// </summary>
        public static object Execute(string source,
                                     string className,
                                     string functionName)
        {
            CompilerResults cr = null;
            return Execute(CompileCode(source, ref cr),
                           className,
                           functionName,
                           null);
        }

        /// <summary>
        /// 
        /// Execute a member function of a class object that takes no parameters
        /// </summary>
        public static object Execute(Assembly a,
                                     string className,
                                     string functionName,
                                     ref object callingObject)
        {
            return Execute(a, className, functionName, null, ref callingObject);
        }

        /// <summary>
        /// 
        /// Execute a member function of a class object
        /// </summary>
        public static object Execute(Assembly a,
                                     string className,
                                     string functionName,
                                     object[] parameters,
                                     ref object callingObject)
        {
            object o = null;

            if (functionName.Length > 0)
            {
                Type t = a.GetType(className);
                MethodInfo info = t.GetMethod(functionName);

                if (parameters == null) parameters = new object[0];
                if (info != null)
                {
                    if (info.IsStatic)
                    {
                        o = info.Invoke(null, parameters);
                    }
                    else
                    {
                        if (callingObject == null)
                        {
                            callingObject = t.Assembly.CreateInstance(t.FullName);
                        }
                        o = info.Invoke(callingObject, parameters);
                    }
                }
            }
            else
            {
                Type t = a.GetType(className);

                callingObject = t.Assembly.CreateInstance(t.FullName);
            }

            return o;
        }

        /// <summary>
        /// 
        /// Execute a static class function that takes parameters
        /// </summary>
        public static object Execute(Assembly assembly,
                                     string className,
                                     string functionName,
                                     object[] parameters)
        {
            object o = null;
            return Execute(assembly, className, functionName, parameters, ref o);
        }

        /// <summary>
        /// 
        /// Execute a static class function that takes no parameters
        /// </summary>
        public static object Execute(Assembly assembly,
                                     string className,
                                     string functionName)
        {
            object o = null;
            return Execute(assembly, className, functionName, null , ref o);
        }

        /// <summary>
        /// 
        /// Compile a C# source string into an Assembly
        /// </summary>
        public static Assembly CompileCode(String source, ref CompilerResults cr)
        {
            CodeDomProvider provider = new CSharpCodeProvider();

            CompilerParameters cp = new CompilerParameters();

            // Generate a class library.
            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;

            // Generate debug information.
            cp.IncludeDebugInformation = false;

            // Add an assembly reference.


            foreach (AssemblyName assemblyName in
                            Assembly.GetEntryAssembly().GetReferencedAssemblies())
            {

                if (assemblyName.Name == "Microsoft.Xna.Framework.Game")
                {
                    string path = Assembly.GetAssembly(typeof(Microsoft.Xna.Framework.Game)).Location;

                    cp.ReferencedAssemblies.Add(path);
                    
                }
                else if (assemblyName.Name == "Microsoft.Xna.Framework")
                {
                    string path = Assembly.GetAssembly(typeof(Microsoft.Xna.Framework.BoundingBox)).Location;
                    cp.ReferencedAssemblies.Add(path);
                }
                else
                {
                    cp.ReferencedAssemblies.Add(assemblyName.Name + ".dll");
                }

                string game = Assembly.GetAssembly(typeof(EVEExposer)).Location;
                cp.ReferencedAssemblies.Add(game);
            }


            // Set the level at which the compiler 
            // should start displaying warnings.
            cp.WarningLevel = 2;

            // Set whether to treat all warnings as errors.
            cp.TreatWarningsAsErrors = false;


            // Set compiler argument to optimize output.
            //cp.CompilerOptions = "/optimize";


            // Invoke compilation.
            cr = provider.CompileAssemblyFromSource(
                                        cp, new string[] { source });

            if (cr.Errors.Count > 0)
            {
                // Display compilation errors.
                /*StringBuilder sb = new StringBuilder();

                sb.AppendFormat("Errors building {0} into {1}\n\n",
                                    source, cr.PathToAssembly);

                foreach (CompilerError ce in cr.Errors)
                {
                    sb.Append(ce.ToString());
                    sb.Append("\n");

                }
                throw new Exception(sb.ToString());
                //return null;*/
                throw new Exception("Compilation Errors!!");
            }
            else
            {
                return cr.CompiledAssembly;
            }
        }
    }
}
