/*
  * The contents of this file are subject to the Mozilla Public
  * License Version 1.1 (the "License"); you may not use this
  * file except in compliance with the License. You may obtain
  * a copy of the License at http://www.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an
  * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
  * or implied. See the License for the specific language governing
  * rights and limitations under the License.
  *
  *
  * The Original Code is .NET RASP toolkit.
  *
  * The Initial Developer of the Original Code is Accenture and Avanade.
  * Portions created by Accenture and Avanade are Copyright (C) 2007
  * Danish National IT and Telecom Agency (http://www.itst.dk). 
  * All Rights Reserved.
  *
  * Contributor(s):
  *   Gert Sylvest (gerts@avanade.com)
  *   Patrik Johansson (p.johansson@accenture.com)
  *   Michael Nielsen (michaelni@avanade.com)
  *   Dennis Søgaard (dennis.j.sogaard@accenture.com)
  *   Ramzi Fadel (ramzif@avanade.com)
  *   Mikkel Hippe Brun (mhb@itst.dk)
  *   Finn Hartmann Jordal (fhj@itst.dk)
  *   Christian Lanng (chl@itst.dk)
  *
  */
using System;
using System.Collections.Generic;
using System.Text;
using dk.gov.oiosi.uddi.ars;
using dk.gov.oiosi.uddi;

namespace dk.gov.oiosi.oioublProfileLibrary {

    internal enum BindingProtocolType { Http, Smtp };

    /// <summary>
    /// Wrapper class for the ArsBinding class.
    /// A binding is the combination of a binding protocol (http, smtp, ...) and a service definition reference.
    /// Use the factory to create an instance.
    /// </summary>
    internal class OioublBinding {
        private ArsBinding _arsBinding;
        private List<OioublProcessDefinition> _processDefinitions;

        /// <summary>
        /// Returns the binding UDDI id
        /// </summary>
        public UddiId UddiId {
            get { return _arsBinding.ID; }
        }

        /// <summary>
        /// Returns the UDDI id for the binding's ServiceDefinition
        /// </summary>
        public UddiId ServiceDefinitionUddiId {
            get { return _arsBinding.ServiceRegistration.PortTypeRegistrationRef.ID; }
        }

        /// <summary>
        /// Returns the UDDI id for the binding protocol
        /// </summary>
        public string BindingProtocolUddiId {
            get { return GetBindingProtocolUddiId(); }
        }

        /// <summary>
        /// Returns the list of ProcessDefinitions referenced by the binding
        /// </summary>
        public IEnumerable<OioublProcessDefinition> ProcessDefinitions {
            get {
                _processDefinitions = new List<OioublProcessDefinition>();
                _processDefinitions.AddRange(GetProcessDefinitions());
                return _processDefinitions;
            }
        }

        /// <summary>
        /// Returns the endpoint address type for the binding (Http, smtp, ...)
        /// </summary>
        public OioublEndpointAddressType EndpointAddressType {
            get { return GetOioublEndpointAddressType(); }
        }

        /// <summary>
        /// Returns the wrapped ArsBinding
        /// </summary>
        public ArsBinding ArsBinding {
            get { return _arsBinding; }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="arsBinding">The ArsBinding to wrap. Use the OioublBindingFactory to create an instance.</param>
        public OioublBinding(ArsBinding arsBinding) {
            _arsBinding = arsBinding;
        }

        /// <summary>
        /// Returns true if the endpoint URI equals this bindings endpoint URI.
        /// </summary>
        /// <param name="endpointAddressUri"></param>
        /// <returns></returns>
        public bool HasEndpointAddressUri(string endpointAddressUri) {
            return endpointAddressUri.Equals(_arsBinding.AccessPoint.AccessEndpointUri.AbsoluteUri);
        }

        /// <summary>
        /// Compares the processes in this binding with the processes in the provided binding.
        /// </summary>
        /// <param name="binding">The binding to compare with.</param>
        /// <returns>Returns true if the bindings have the same processes</returns>
        public bool CompareProcesses(OioublBinding binding) {
            foreach (OioublProcessDefinition processDefinition in ProcessDefinitions) {
                if (!HasProcess(processDefinition)) return false;
            }
            return true;
        }

        /// <summary>
        /// Compares the service definition in this binding with the provided binding.
        /// </summary>
        /// <param name="binding">The binding to compare with</param>
        /// <returns>Returns true if the bindings have the same UddiId</returns>
        public bool CompareServiceDefinition(OioublBinding binding) {
            if (ServiceDefinitionUddiId.ID == binding.ServiceDefinitionUddiId.ID) return true;
            return false;
        }

        /// <summary>
        /// Adds the process defined in the OioublProfile to the binding.
        /// Behind the scenes:
        /// A process instance with a reference to the process definition is created,
        /// and the process instance is added to the binding.
        /// </summary>
        /// <param name="profile">The profile to add</param>
        public void AddProcess(IOioublProfile profile) {
            OioublProcessInstance oioublProcessInstance = new OioublProcessInstance(profile);
            ArsProcessInstance arsProcessInstance = oioublProcessInstance.CreateArsProcessInstance();

            ArsProcessInstanceSet existingProcessInstanceSet = _arsBinding.ProcessInstanceSet;
            existingProcessInstanceSet.Add(arsProcessInstance);
            existingProcessInstanceSet.Save();

            _arsBinding.ProcessInstanceSet = existingProcessInstanceSet;
        }

        /// <summary>
        /// Removes the process from the binding.
        /// The process instance is removed from the ArsBinding.
        /// </summary>
        /// <param name="oioublProfile"></param>
        public void RemoveProcessDefinition(IOioublProfile oioublProfile) {
            foreach (ArsProcessInstance arsProcessInstance in _arsBinding.ProcessInstanceSet.Processes) {
                if (arsProcessInstance.ProcessDefinitionReference.Value /*ID.ID*/ == oioublProfile.UddiId) {
                    _arsBinding.RemoveProcessInstance(arsProcessInstance);
                }
            }
        }

        # region Helper methods

        private bool HasProcess(OioublProcessDefinition processDefinition) {
            foreach (OioublProcessDefinition definition in ProcessDefinitions) {
                if (definition.Equals(processDefinition)) return true;
            }
            return false;
        }

        private IEnumerable<OioublProcessDefinition> GetProcessDefinitions() {
            List<OioublProcessDefinition> processDefinitions = new List<OioublProcessDefinition>();
            ArsProcessInstanceSet processInstanceSet = _arsBinding.ProcessInstanceSet;
            foreach (ArsProcessInstance processInstance in processInstanceSet.Processes) {
                OioublProcessDefinition processDefinition = new OioublProcessDefinition(processInstance);
                processDefinitions.Add(processDefinition);
            }
            return processDefinitions;
        }

        private string GetBindingProtocolUddiId() {

            categoryBag bag = _arsBinding.BindingTemplate.CategoryBag.Value;
            if (bag.Items == null || bag.Items.Length == 0) throw new ApplicationException("No protocol defined for binding");
            
            keyedReference reference = bag.Items[0] as keyedReference;
            if (reference == null) throw new ApplicationException("Wrong format for binding protocol");

            return reference.tModelKey;
        }

        private OioublEndpointAddressType GetOioublEndpointAddressType() {
            return OioublEndpointAddress.GetOioublEndpointAddressType(_arsBinding.AddressType.ToCode());
        }

        # endregion

    }
}
