using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Windows;
using System.Windows.Controls;
using Telerik.Windows.Controls;

namespace FilteringByCustomTypeSilverlight
{
    public partial class Page3 : UserControl
    {
        public Page3()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Page3_Loaded);

            ((GridViewDataColumn) this.GridView.Columns[1]).DataType = typeof(double);
        }

        void Page3_Loaded(object sender, RoutedEventArgs e)
        {
            IList genericRunTimeList = GetListOfDynamicallyCreatedType();
            this.GridView.ItemsSource = genericRunTimeList;
        }

        private IList GetListOfDynamicallyCreatedType()
        {
            // Much of this taken from MSDN: http://msdn.microsoft.com/en-us/library/system.reflection.emit.typebuilder.aspx
            AssemblyName assemblyName = new AssemblyName();
            assemblyName.Name = "DynamicData";

            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicData");

            TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicRow", TypeAttributes.Public | TypeAttributes.Class);

            CreateProperty(typeBuilder, typeof(int), "_number", "Number");
            CreateProperty(typeBuilder, typeof(OlapValue), "_salesDollars", "SalesDollars");

            // Instantiate the type of row
            Type dynamicRow = typeBuilder.CreateType();

            // Because AssemblyBuilderAccess includes Run, the code can be
            // executed immediately. Start by getting reflection objects for
            // the method and the property.

            // Get the properties of our new type
            PropertyInfo number = dynamicRow.GetProperty("Number");
            PropertyInfo salesDollars = dynamicRow.GetProperty("SalesDollars");
            // PropertyInfo salesDollarsPriorPeriod = dynamicRow.GetProperty("SalesDollarsPriorPeriod");
            // PropertyInfo salesDollarsMTD = dynamicRow.GetProperty("SalesDollarsPriorMTD");
            // PropertyInfo salesDollarsYTD = dynamicRow.GetProperty("SalesDollarsPriorYTD");
            // ..... etc. (any number of properties)

            // Now let's create some DynamicRows of data

            // Put them in a typed list
            //  http://www.devx.com/tips/Tip/41965
            IList genericRunTimeList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(dynamicRow));

            for (int i = 0; i < 10; i++)
            {
                // Create an instance of MyDynamicType using the default 
                // constructor. 
                object o = Activator.CreateInstance(dynamicRow);

                // Set the value of the Number property
                number.SetValue(o, i, null);

                // Set the value of the SalesDollars property
                OlapValue olapValue = new OlapValue();

                // Make even numbers negative to test sorting
                if (i % 2 == 0)
                {
                    // even
                    olapValue.Value = Convert.ToDouble(string.Format("-{0}.{0}{0}", i));
                }
                else
                {
                    // odd
                    olapValue.Value = Convert.ToDouble(string.Format("{0}.{0}{0}", i));
                }

                olapValue.ConverterParameter = "{0:C}";
                salesDollars.SetValue(o, olapValue, null);

                genericRunTimeList.Add(o);
            }

            return genericRunTimeList;
        }

        private void CreateProperty(TypeBuilder typeBuilder, Type propertyType, string privateFieldName, string publicPropertyName)
        {
            // Add a private field of type int (Int32).
            FieldBuilder fb = typeBuilder.DefineField(
                privateFieldName,
                propertyType,
                FieldAttributes.Private);

            // Define a property named Number that gets and sets the private 
            // field.
            //
            // The last argument of DefineProperty is null, because the
            // property has no parameters. (If you don't specify null, you must
            // specify an array of Type objects. For a parameterless property,
            // use the built-in array with no elements: Type.EmptyTypes)
            PropertyBuilder pb = typeBuilder.DefineProperty(
                publicPropertyName,
                PropertyAttributes.HasDefault,
                propertyType,
                null);

            // The property "set" and property "get" methods require a special
            // set of attributes.
            MethodAttributes getSetAttr = MethodAttributes.Public |
                MethodAttributes.SpecialName | MethodAttributes.HideBySig;

            // Define the "get" accessor method for Number. The method returns
            // an integer and has no arguments. (Note that null could be 
            // used instead of Types.EmptyTypes)
            MethodBuilder mbGetAccessor = typeBuilder.DefineMethod(
                string.Format("get_{0}", publicPropertyName),
                getSetAttr,
                propertyType,
                Type.EmptyTypes);

            ILGenerator getIL = mbGetAccessor.GetILGenerator();
            // For an instance property, argument zero is the instance. Load the 
            // instance, then load the private field and return, leaving the
            // field value on the stack.
            getIL.Emit(OpCodes.Ldarg_0);
            getIL.Emit(OpCodes.Ldfld, fb);
            getIL.Emit(OpCodes.Ret);


            // Define the "set" accessor method for Number, which has no return
            // type and takes one argument of type int (Int32).
            MethodBuilder mbSetAccessor = typeBuilder.DefineMethod(
                string.Format("set_{0}", publicPropertyName),
                getSetAttr,
                null,
                new Type[] { propertyType });

            ILGenerator setIL = mbSetAccessor.GetILGenerator();
            // Load the instance and then the numeric argument, then store the
            // argument in the field.
            setIL.Emit(OpCodes.Ldarg_0);
            setIL.Emit(OpCodes.Ldarg_1);
            setIL.Emit(OpCodes.Stfld, fb);
            setIL.Emit(OpCodes.Ret);

            // Last, map the "get" and "set" accessor methods to the 
            // PropertyBuilder. The property is now complete. 
            pb.SetGetMethod(mbGetAccessor);
            pb.SetSetMethod(mbSetAccessor);
        }
    }
}
