//===============================================================================
// Microsoft patterns & practices
// Web Service Software Factory
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom;
using System.Reflection;
using Microsoft.Practices.SoftwareFactories.ServiceFactory.CommonHelpers;

namespace Microsoft.Practices.SoftwareFactories.ServiceFactory.Actions.CodeDom.Helpers
{
    /// <summary/>
    public class TranslatorGenerator
    {
        CodeCompileUnit root;
        CodeNamespace ourNamespace;

        /// <summary>
        /// Gets the compile unit.
        /// </summary>
        /// <value>The compile unit.</value>
        public CodeCompileUnit CompileUnit { get { return root; } }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:TranslatorGenerator"/> class.
        /// </summary>
        /// <param name="theNamespace">The namespace.</param>
        public TranslatorGenerator(string theNamespace)
        {
            this.root = new CodeCompileUnit();

            this.ourNamespace = new CodeNamespace(theNamespace);
            this.ourNamespace.Imports.Add(new CodeNamespaceImport("System"));
            this.root.Namespaces.Add(ourNamespace);
        }

        /// <summary>
        /// Maps the specified first.
        /// </summary>
        /// <param name="first">The first.</param>
        /// <param name="second">The second.</param>
        /// <param name="className">Name of the class.</param>
        /// <param name="mappedProperties">The mapped properties.</param>
        public void Map(Type first, Type second, string className, MappedPropertyList mappedProperties)
        {
            if (first == null)
                throw new ArgumentNullException("first");

            if (second == null)
                throw new ArgumentNullException("second");
            
            ourNamespace.Imports.Add(new CodeNamespaceImport(first.Namespace));
            ourNamespace.Imports.Add(new CodeNamespaceImport(second.Namespace));

            CodeTypeDeclaration myClass = new CodeTypeDeclaration();
            myClass.Name = className;
            myClass.Members.Add(GenerateMappingMethod(first, second, mappedProperties, DirectedMappingMethod.MappingDirection.ForwardMap));
            myClass.Members.Add(GenerateMappingMethod(second, first, mappedProperties, DirectedMappingMethod.MappingDirection.ReverseMap));

            ourNamespace.Types.Add(myClass);
        }

        private CodeMemberMethod GenerateMappingMethod(Type fromType, Type toType, MappedPropertyList mappedProperties, DirectedMappingMethod.MappingDirection mappingDirection)
        {
            DirectedMappingMethod mappingMethod = new DirectedMappingMethod(fromType, toType);
            mappingMethod.Generate(mappedProperties, mappingDirection);

            CodeMemberMethod method = mappingMethod.MethodDeclaration.ToCodeDom();
            method.Statements.Add(mappingMethod.ToDeclaration.ToCodeDom());

            foreach (MappingStatement assignment in mappingMethod.AssignmentStatements)
            {
                method.Statements.Add(assignment.ToCodeDom());
            }

            foreach (UnmappedProperty comment in mappingMethod.UnmappedProperties)
            {
                method.Statements.Add(comment.ToCodeDom());
            }

            method.Statements.Add(mappingMethod.ReturnStatement.ToCodeDom());

            return method;
        }
    }
}
