/*
 * Copyright (c) Contributors, http://opensimulator.org/
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSimulator Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using OpenSim.ScriptEngine.Shared;

namespace OpenSim.ScriptEngine.Components.DotNetEngine.Scheduler
{
    public class BaseClassFactory
    {        


        public static void MakeBaseClass(ScriptStructure script)
        {
            string asmName = "ScriptAssemblies";
            string ModuleID = asmName;
            string ClassID = "Script";
            string moveToDir = "ScriptEngines";
            string asmFileName = ModuleID + "_" + ClassID + ".dll";
            if (!Directory.Exists(moveToDir))
                Directory.CreateDirectory(moveToDir);

            ILGenerator ilgen;
            AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName(asmName), AssemblyBuilderAccess.RunAndSave);

            // The module builder
            ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(ModuleID, asmFileName);

            // The class builder
            TypeBuilder classBuilder = modBuilder.DefineType(ClassID, TypeAttributes.Class | TypeAttributes.Public);

            // The default constructor
            //ConstructorBuilder ctorBuilder = classBuilder.DefineDefaultConstructor(MethodAttributes.Public);


            Type[] paramsTypeArray = new Type[] {typeof (System.ParamArrayAttribute)};
            Type[] executeFunctionTypeArray = new Type[] {typeof (string), typeof (System.ParamArrayAttribute)};
            foreach (IScriptCommandProvider cp in script.RegionInfo.CommandProviders.Values)
            {
                Type t = cp.GetType();
                foreach (MethodInfo mi in t.GetMethods())
                {
                    MethodBuilder methodBuilder = classBuilder.DefineMethod(mi.Name, mi.Attributes, mi.GetType(), Type.EmptyTypes);
                    methodBuilder.SetParameters(paramsTypeArray);
                    //ParameterBuilder paramBuilder = methodBuilder.DefineParameter(1, ParameterAttributes.None, "args");
                    
                    ilgen = methodBuilder.GetILGenerator();
                    //ilgen.Emit(OpCodes.Nop);
                    //ilgen.Emit(OpCodes.Ldarg_0);
                    //ilgen.Emit(OpCodes.Ldc_I4_0);
                    //ilgen.Emit(OpCodes.Ldelem_Ref);
                    //ilgen.MarkSequencePoint(doc, 6, 1, 6, 100);

                        //MethodInfo ExecuteFunction = typeof(ScriptAssemblies.IScript).GetMethod(
                        // "ExecuteFunction",
                        // executeFunctionTypeArray);

                        ilgen.DeclareLocal(typeof(string));
                        ilgen.Emit(OpCodes.Nop);
                    ilgen.Emit(OpCodes.Ldstr, mi.Name);
                    ilgen.Emit(OpCodes.Stloc_0);
                    ilgen.Emit(OpCodes.Ldarg_0);
                    ilgen.Emit(OpCodes.Ldloc_0);
                    ilgen.Emit(OpCodes.Ldarg_1);

    //                FieldInfo testInfo = classBuilder.
    //BindingFlags.NonPublic | BindingFlags.Instance);

                    //ilgen.Emit(OpCodes.Ldfld, testInfo);

                    //ilgen.EmitCall(OpCodes.Call, ExecuteFunction, executeFunctionTypeArray);
                    ilgen.EmitCall(OpCodes.Call, typeof(System.Console).GetMethod("WriteLine"), executeFunctionTypeArray);

                //    // string.Format("Hello, {0} World!", toWhom)
                //    //
                //    ilgen.Emit(OpCodes.Ldstr, "Hello, {0} World!");
                //    ilgen.Emit(OpCodes.Ldarg_1);
                //    ilgen.Emit(OpCodes.Call, typeof(string).GetMethod
                //("Format", new Type[] { typeof(string), typeof(object) }));

                //    // Console.WriteLine("Hello, World!");
                //    //
                //    ilgen.Emit(OpCodes.Call, typeof(Console).GetMethod
                //    ("WriteLine", new Type[] { typeof(string) }));
                    ilgen.Emit(OpCodes.Ret);

                    

                    //Label eom = ilgen.DefineLabel();
                    //ilgen.Emit(OpCodes.Br_S, eom);
                    //ilgen.MarkLabel(eom);
                    //ilgen.Emit(OpCodes.Ret);
                    //Type test = methodBuilder.SetParameters();


                    //methodBuilder.SetParameters(typeof (object[]));

                    
                }
            }


            //// Two fields: m_firstname, m_lastname
            //FieldBuilder fBuilderFirstName = classBuilder.DefineField("m_firstname", typeof(string), FieldAttributes.Private);
            //FieldBuilder fBuilderLastName = classBuilder.DefineField("m_lastname", typeof(string), FieldAttributes.Private);

            //// Two properties for this object: FirstName, LastName
            //PropertyBuilder pBuilderFirstName = classBuilder.DefineProperty("FirstName", System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);
            //PropertyBuilder pBuilderLastName = classBuilder.DefineProperty("LastName", System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);

            //// Custom attributes for get, set accessors
            //MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;

            //// get,set accessors for FirstName
            //MethodBuilder mGetFirstNameBuilder = classBuilder.DefineMethod("get_FirstName", getSetAttr, typeof(string), Type.EmptyTypes);

            //// Code generation
            //ilgen = mGetFirstNameBuilder.GetILGenerator();
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Ldfld, fBuilderFirstName); // returning the firstname field
            //ilgen.Emit(OpCodes.Ret);

            //MethodBuilder mSetFirstNameBuilder = classBuilder.DefineMethod("set_FirstName", getSetAttr, null, new Type[] { typeof(string) });

            //// Code generation
            //ilgen = mSetFirstNameBuilder.GetILGenerator();
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Ldarg_1);
            //ilgen.Emit(OpCodes.Stfld, fBuilderFirstName); // setting the firstname field from the first argument (1)
            //ilgen.Emit(OpCodes.Ret);

            //// get,set accessors for LastName
            //MethodBuilder mGetLastNameBuilder = classBuilder.DefineMethod("get_LastName", getSetAttr, typeof(string), Type.EmptyTypes);

            //// Code generation
            //ilgen = mGetLastNameBuilder.GetILGenerator();
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Ldfld, fBuilderLastName); // returning the firstname field
            //ilgen.Emit(OpCodes.Ret);

            //MethodBuilder mSetLastNameBuilder = classBuilder.DefineMethod("set_LastName", getSetAttr, null, new Type[] { typeof(string) });

            //// Code generation
            //ilgen = mSetLastNameBuilder.GetILGenerator();
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Ldarg_1);
            //ilgen.Emit(OpCodes.Stfld, fBuilderLastName); // setting the firstname field from the first argument (1)
            //ilgen.Emit(OpCodes.Ret);

            //// Assigning get/set accessors
            //pBuilderFirstName.SetGetMethod(mGetFirstNameBuilder);
            //pBuilderFirstName.SetSetMethod(mSetFirstNameBuilder);

            //pBuilderLastName.SetGetMethod(mGetLastNameBuilder);
            //pBuilderLastName.SetSetMethod(mSetLastNameBuilder);

            //// Now, a custom method named GetFullName that concatenates FirstName and LastName properties
            //MethodBuilder mGetFullNameBuilder = classBuilder.DefineMethod("GetFullName", MethodAttributes.Public, typeof(string), Type.EmptyTypes);

            //// Code generation
            //ilgen = mGetFullNameBuilder.GetILGenerator();
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Call, mGetFirstNameBuilder); // getting the firstname
            //ilgen.Emit(OpCodes.Ldstr, " "); // an space
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Call, mGetLastNameBuilder); // getting the lastname

            //// We need the 'Concat' method from string type
            //MethodInfo concatMethod = typeof(String).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

            //ilgen.Emit(OpCodes.Call, concatMethod); // calling concat and returning the result
            //ilgen.Emit(OpCodes.Ret);

            //// Another constructor that initializes firstname and lastname
            //ConstructorBuilder ctorBuilder2 = classBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(string), typeof(string) });
            //ctorBuilder2.DefineParameter(1, ParameterAttributes.In, "firstname");
            //ctorBuilder2.DefineParameter(2, ParameterAttributes.In, "lastname");

            //// Code generation
            //ilgen = ctorBuilder2.GetILGenerator();

            //// First of all, we need to call the base constructor, 
            //// the Object's constructor in this sample
            //Type objType = Type.GetType("System.Object");
            //ConstructorInfo objCtor = objType.GetConstructor(Type.EmptyTypes);

            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Call, objCtor); // calling the Object's constructor

            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Ldarg_1);
            //ilgen.Emit(OpCodes.Call, mSetFirstNameBuilder); // setting the firstname field from the first argument (1)
            //ilgen.Emit(OpCodes.Ldarg_0);
            //ilgen.Emit(OpCodes.Ldarg_2);
            //ilgen.Emit(OpCodes.Call, mSetLastNameBuilder);  // setting the lastname field from the second argument (2)
            //ilgen.Emit(OpCodes.Ret);

            // Finally, create the type and save the assembly
            classBuilder.CreateType();

            asmBuilder.Save(asmFileName);
            string toFile = Path.Combine(moveToDir, asmFileName);
            if (File.Exists(toFile))
                File.Delete(toFile);
            File.Move(asmFileName, toFile);

            //string a = "";
        }
    }
}