/*
  * 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 System.Security.Cryptography.X509Certificates;

using dk.gov.oiosi.uddi.ars;
using dk.gov.oiosi.uddi;
using dk.gov.oiosi.oioublProfileLibrary.Exceptions;

namespace dk.gov.oiosi.oioublProfileLibrary {

    /// <summary>
    /// Class for performing profile operations on a BusinessEntity.
    /// Use the factory class to create or get a BusinessEntity instance.
    /// </summary>
    public class OioublBusinessEntity: IOioublBusinessEntity, IOioublServiceCollection {
        private ArsBusinessEntity _arsBusinessEntity;
        private string _cvr;
        private X509Certificate2 _endpointCertificate;
        private List<IOioublService> _oioublServices;


        #region IBusinessEntity Members

        /// <summary>
        /// The BusinessEntity UddiId
        /// </summary>
        public string UddiId {
            get { return _arsBusinessEntity.ID.ID; }
        }

        /// <summary>
        /// The name of the BusinessEntity
        /// </summary>
        public string Name {
            get { return _arsBusinessEntity.Name.Text; }
            set { _arsBusinessEntity.Name.Text = value; }
        }

        /// <summary>
        /// The description of the BusinessEntity
        /// </summary>
        public string Description {
            get { return _arsBusinessEntity.Description.Text; }
            set { _arsBusinessEntity.Description.Text = value; }
        }

        /// <summary>
        /// The BusinessEntity CVR-number
        /// </summary>
        public string Cvr {
            get { return _arsBusinessEntity.OrganizationKey.Value; }
            set { _arsBusinessEntity.OrganizationKey.Value = value; }
        }

        /// <summary>
        /// Returns a list of OioublServices registered with the Business Entity
        /// </summary>
        internal IEnumerable<IOioublService> OioublServices {
            get {
                if (_oioublServices == null) {
                    _oioublServices = new List<IOioublService>();
                    _oioublServices.AddRange(GetOioublServicesInArsBusinessEntity());
                }
                return _oioublServices;
            }
        }

        /// <summary>
        /// Constructor for the BusinessEntity.
        /// The constructor is internal, as the BusinessEntity should be created using the BusinessEntityFactory.
        /// </summary>
        /// <param name="arsBusinessEntity">The business entity</param>
        /// <param name="cvr">The cvr number</param>
        /// <param name="endpointCertificate">The endpoint certificate</param>
        internal OioublBusinessEntity(ArsBusinessEntity arsBusinessEntity, string cvr, X509Certificate2 endpointCertificate) {
            _arsBusinessEntity = arsBusinessEntity;
            _cvr = cvr;
            _endpointCertificate = endpointCertificate;
        }

        /// <summary>
        /// Activates the OIO UBL Profile for the Business Entity.
        /// The profile must exist in the UDDI registry, if not an exception is thrown.
        /// The endpoint key is set to the BusinessEntity CVR.
        /// </summary>
        /// <param name="oioublProfile">The OIO UBL profile to activate</param>
        /// <param name="endpointAddressList">The physical endpoint address to use</param>
        public void ActivateOioublProfile(IOioublProfile oioublProfile, OioublEndpointAddressList endpointAddressList) {
            OioublEndpointKey endpointKey = new OioublEndpointKey(OioublEndpointKeyType.Cvr, Cvr);
            ActivateOioublProfile(oioublProfile, endpointKey, endpointAddressList);
        }

        /// <summary>
        /// Activates the OIO UBL Profile for the Business Entity.
        /// The profile must exist in the UDDI registry, if not an exception is thrown.
        /// </summary>
        /// <param name="oioublProfile">The OIO UBL profile to activate</param>
        /// <param name="endpointKey">The endpoint key type and value to use (eg. CVR, EAN)</param>
        /// <param name="endpointAddressList">The physical endpoint address to use</param>
        public void ActivateOioublProfile(IOioublProfile oioublProfile, OioublEndpointKey endpointKey, OioublEndpointAddressList endpointAddressList) {
            // Activating a profile is done by making sure the business entity has 
            // a service for each document type (service definition) in the profile.
            // 1. A list of missing service definitions are returned
            // 2. For each missing service definition a new service is created
            // 3. For each active service a reference to the process definition is created

            ValidateProfileRegistration(oioublProfile);

            OioublProfileValidator validator = new OioublProfileValidator(oioublProfile);
            OioublProfileValidationResult validationResult = validator.Validate(this);

            // Create new services with reference to process
            foreach (OioublServiceDefinition missingService in validationResult.ServicesMissingInProfileAndBusinessEntity) {
                OioublEndpointAddress endpointAddress = endpointAddressList.GetEndpointAddress(missingService);
                CreateService(oioublProfile, endpointKey, missingService, endpointAddress);
            }

            // Add process to active services
            // All services are deleted each time, so the profiles are added each time.
            foreach (OioublService activeService in validationResult.ServicesActiveInBusinessEntityButNotInProfile) {
                OioublEndpointAddress endpointAddress = endpointAddressList.GetEndpointAddress(activeService.ServiceDefinition);
                bool serviceHasSameEndpoint = activeService.HasEndpoint(endpointKey, endpointAddress);

                if (serviceHasSameEndpoint) {
                    activeService.AddProcessDefinition(oioublProfile);
                    activeService.Save();
                }
                else {
                    throw new NotImplementedException();
                }
            }

        }

        /// <summary>
        /// Deactivates an OIO UBL profile for the BusinessEntity.
        /// If the profile is not active for the BusinessEntity, no change is made.
        /// </summary>
        /// <param name="oioublProfile">The profile to deactivate</param>
        public void DeactivateOioublProfile(IOioublProfile oioublProfile) {
            // Deactivating a profile is done my making sure that no services
            // are referencing the process definition (defined in the profile)
            // In practice this means that all services that reference a profile
            // are deleted.
            // Services that reference more than one profile, are not deleted.
            // Instead the profile is removed from their bindings.
            
            ValidateProfileRegistration(oioublProfile);

            OioublProfileValidator validator = new OioublProfileValidator(oioublProfile);
            OioublProfileValidationResult validationResult = validator.Validate(this);

            foreach (OioublService activeService in validationResult.ServicesActiveInProfile) {
                if (activeService.HasMoreThanOneProcessDefinition()) {
                    activeService.RemoveProcessDefinition(oioublProfile);
                    activeService.Save();
                }
                else {
                    activeService.Delete();
                    _oioublServices.Remove(activeService);
                }
            }
            
        }

        /// <summary>
        /// Returns true if the BusinessEntity has the OIO UBL profile registered.
        /// </summary>
        /// <param name="oioublProfile">The profile to look for</param>
        /// <returns></returns>
        public bool HasOioublProfile(IOioublProfile oioublProfile) {
            OioublProfileValidator validator = new OioublProfileValidator(oioublProfile);
            OioublProfileValidationResult validationResult = validator.Validate(this);
            return validationResult.Success;
            //return validationResult.ProfileReferenced;
        }

        /// <summary>
        /// Clear all services of the business entity
        /// </summary>
        public void ClearServices() {
            foreach (ArsEndpoint endpoint in _arsBusinessEntity.Endpoints.Endpoints) {
                _arsBusinessEntity.DeleteEndpoint(endpoint);
            }
            _arsBusinessEntity.Save();
            _oioublServices.Clear();
        }


        /// <summary>
        /// Deletes the BusinessEntity and all related UDDI registrations from the registry.
        /// </summary>
        public void Delete() {
            _arsBusinessEntity.Delete();
        }

        #endregion


        # region Helper methods

        private void CreateService(IOioublProfile oioublProfile, OioublEndpointKey endpointKey, OioublServiceDefinition missingService, OioublEndpointAddress endpointAddress) {
            OioublService oioublService =
                OioublServiceFactory.CreateOioublService(oioublProfile, missingService, endpointKey, endpointAddress, _endpointCertificate.Subject, this);
            _oioublServices.Add(oioublService);
        }

        private IEnumerable<IOioublService> GetOioublServicesInArsBusinessEntity() {
            List<IOioublService> services = new List<IOioublService>();
            foreach (ArsEndpoint arsEndpoint in _arsBusinessEntity.Endpoints.Endpoints) {
                OioublService oioublService = new OioublService(arsEndpoint, this);
                services.Add(oioublService);
            }
            return services;
        }

        private IEnumerable<OioublService> GetOioublServices(OioublProcessDefinition processDefinition) {
            List<OioublService> services = new List<OioublService>();
            foreach (OioublService service in OioublServices) {
                if (service.HasProcess(processDefinition)) {
                    services.Add(service);
                }
            }
            return services;
        }

        private void ValidateProfileRegistration(IOioublProfile profile) {
            if (!profile.IsRegisteredInUddi()) {
                string message = "The profile or one of it's services is not registered in the UDDI registry: " + profile.Name + " with id " + profile.UddiId;
                throw new OioublProfileNotRegisteredException(message);
            }
        }

        # endregion


        #region IOioublServiceContainer Members

        IEnumerable<IOioublService> IOioublServiceCollection.OioublServices {
            get { return OioublServices; }
        }

        #endregion
    }
}
