using CareFusion.Lib.StorageSystem.Attributes;
using CareFusion.Lib.StorageSystem.Helpers.Enums;
using CareFusion.Lib.StorageSystem.Input;
using CareFusion.Lib.StorageSystem.Output;
using CareFusion.Lib.StorageSystem.Stock;
using Rowa.Mosaic.Shared.Converters;
using System;
using System.Xml.Serialization;

namespace CareFusion.Lib.StorageSystem.Wwks2.Types
{
    /// <summary>
    /// Class which represents the Pack datatype in the WWKS 2.0 protocol.
    /// </summary>
    public class Pack : IPack, IInputPack, IDispensedPack
    {
        #region WWKS 2.0 Properties

        [XmlAttribute]
        public string Id { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(int))]
        public string Index { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string ScanCode { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string DeliveryNumber { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string BatchNumber { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string ExternalId { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string SerialNumber { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(DateTime))]
        public string ExpiryDate { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(ExpiryDateSource))]
        public string ExpiryDateSource { get; set; }

        [XmlAttribute]
        public string StockInDate { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(int))]
        public string SubItemQuantity { get; set; }

        [XmlAttribute]
        public string Depth { get; set; }

        [XmlAttribute]
        public string Width { get; set; }

        [XmlAttribute]
        public string Height { get; set; }

        [XmlAttribute]
        public string Shape { get; set; }

        [XmlAttribute]
        public string State { get; set; }

        [XmlAttribute]
        public string IsInFridge { get; set; }

        [XmlAttribute]
        public string BoxNumber { get; set; }

        [XmlAttribute]
        public string OutputDestination { get; set; }

        [XmlAttribute]
        public string OutputSubDestination { get; set; }

        [XmlAttribute]
        public string OutputPoint { get; set; }

        [XmlAttribute]
        public string OutputUser { get; set; }

        [XmlAttribute]
        public string LabelStatus { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string StockLocationId { get; set; }

        [XmlAttribute]
        [UserSelectableProperty(propertyType: typeof(string))]
        public string MachineLocation { get; set; }

        [XmlAttribute(AttributeName = "GUID")]
        public string Guid { get; set; }

        [XmlElement]
        public Handling Handling { get; set; }

        [XmlElement]
        public Error Error { get; set; }

        #endregion

        #region IPack Specific Properties

        /// <inheritdoc/>
        [XmlIgnore]
        ulong IPack.Id { get { return TypeConverter.ConvertULong(this.Id); } }

        /// <inheritdoc/>
        [XmlIgnore]
        string IPack.DeliveryNumber { get { return TextConverter.UnescapeInvalidXmlChars(this.DeliveryNumber); } }

        /// <inheritdoc/>
        [XmlIgnore]
        string IPack.BatchNumber { get { return TextConverter.UnescapeInvalidXmlChars(this.BatchNumber); } }

        /// <inheritdoc/>
        [XmlIgnore]
        string IPack.ExternalId { get { return TextConverter.UnescapeInvalidXmlChars(this.ExternalId); } }

        /// <inheritdoc/>
        [XmlIgnore]
        DateTime IPack.ExpiryDate
        {
            get { return TypeConverter.ConvertDate(this.ExpiryDate); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        ExpiryDateSource IPack.ExpiryDateSource
        {
            get { return TypeConverter.ConvertEnum(this.ExpiryDateSource, Helpers.Enums.ExpiryDateSource.Unknown); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        DateTime IPack.StockInDate
        {
            get { return TypeConverter.ConvertDate(this.StockInDate); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        string IPack.ScanCode
        {
            get { return TextConverter.UnescapeInvalidXmlChars(this.ScanCode); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        uint IPack.SubItemQuantity
        {
            get { return TypeConverter.ConvertUInt(this.SubItemQuantity); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        int IPack.Depth
        {
            get { return TypeConverter.ConvertInt(this.Depth); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        int IPack.Width
        {
            get { return TypeConverter.ConvertInt(this.Width); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        int IPack.Height
        {
            get { return TypeConverter.ConvertInt(this.Height); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        PackShape IPack.Shape
        {
            get { return TypeConverter.ConvertEnum<PackShape>(this.Shape, PackShape.Cuboid); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        PackState IPack.State
        {
            get { return TypeConverter.ConvertEnum<PackState>(this.State, PackState.Available); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        bool IPack.IsInFridge
        {
            get { return TypeConverter.ConvertBool(this.IsInFridge); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        string IPack.StockLocationId
        {
            get { return this.StockLocationId != null ? TextConverter.EscapeInvalidXmlChars(this.StockLocationId) : string.Empty; }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        string IPack.MachineLocation
        {
            get { return this.MachineLocation != null ? TextConverter.EscapeInvalidXmlChars(this.MachineLocation) : string.Empty; }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        Guid IPack.Guid { get { return System.Guid.Parse(TextConverter.UnescapeInvalidXmlChars(string.IsNullOrEmpty(this.Guid) ? System.Guid.Empty.ToString() : this.Guid)); } }

        #endregion

        #region IInputPack Specific Properties

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.ScanCode { get { return TextConverter.UnescapeInvalidXmlChars(this.ScanCode); } }

        /// <inheritdoc/>
        [XmlIgnore]
        Guid IInputPack.Guid { get { return System.Guid.Parse(TextConverter.UnescapeInvalidXmlChars(this.Guid ?? System.Guid.Empty.ToString())); } }

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.BatchNumber { get { return TextConverter.UnescapeInvalidXmlChars(this.BatchNumber); } }

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.ExternalId { get { return TextConverter.UnescapeInvalidXmlChars(this.ExternalId); } }

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.SerialNumber { get { return TextConverter.UnescapeInvalidXmlChars(this.SerialNumber); } }

        /// <inheritdoc/>
        [XmlIgnore]
        DateTime? IInputPack.ExpiryDate
        {
            get
            {
                if (string.IsNullOrEmpty(this.ExpiryDate))
                {
                    return null;
                }

                return TypeConverter.ConvertDate(this.ExpiryDate);
            }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.ExpiryDateSource
        {
            get
            {
                return this.ExpiryDateSource ?? TextConverter.UnescapeInvalidXmlChars(this.ExpiryDateSource);
            }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        uint? IInputPack.SubItemQuantity
        {
            get { return TypeConverter.ConvertUIntNull(this.SubItemQuantity); }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.StockLocationId
        {
            get { return this.StockLocationId != null ? TextConverter.EscapeInvalidXmlChars(this.StockLocationId) : string.Empty; }
        }

        /// <inheritdoc/>
        [XmlIgnore]
        string IInputPack.MachineLocation
        {
            get { return this.MachineLocation != null ? TextConverter.EscapeInvalidXmlChars(this.MachineLocation) : string.Empty; }
        }

        #endregion

        #region IDispensedPack Specific Properties

        /// <summary>
        /// Gets or sets the list of packs which were dispensed by the output process.
        /// This property is set after the output process finished.
        /// </summary>
        [XmlIgnore]
        public string ArticleId { get; set; }

        /// <summary>
        /// If the pack was dispensed to a box within a box system scenario,
        /// this property identifies the box the pack was dispensed to.
        /// </summary>
        [XmlIgnore]
        string IDispensedPack.BoxNumber { get { return TextConverter.UnescapeInvalidXmlChars(this.BoxNumber); } }

        /// <summary>
        /// Gets the destination that was used to dispense this pack. 
        /// In some error scenarios it might happen that the pack is dispensed to another 
        /// output destination than the requested one (e.g. the output destination was not 
        /// operational when the output process was running).
        /// </summary>
        [XmlIgnore]
        int IDispensedPack.OutputDestination { get { return TypeConverter.ConvertInt(this.OutputDestination); } }

        /// <summary>
        /// Gets the number which may define a more detailed  part of the destination
        /// that was used to dispense the pack (e.g. a specific conveyor belt or slide).
        /// </summary>
        [XmlIgnore]
        int IDispensedPack.OutputPoint { get { return TypeConverter.ConvertInt(this.OutputPoint); } }

        /// <summary>
        /// Gets the user which was used to dispense the pack. 
        /// </summary>
        [XmlIgnore]
        string IDispensedPack.OutputUser { get { return TextConverter.UnescapeInvalidXmlChars(this.OutputUser); } }

        /// <summary>
        /// Gets the labelling status of the dispensed pack. 
        /// Only relevant when pack labelling was requested.
        /// </summary>
        [XmlIgnore]
        LabelState IDispensedPack.LabelState { get { return TypeConverter.ConvertEnum<LabelState>(this.LabelStatus, LabelState.NotLabelled); } }

        #endregion

        #region Additional Properties

        /// <summary>
        /// Gets the article object which is currently assigned to this pack.
        /// This property is used to temporarily store the article related properties for this pack. 
        /// </summary>
        [XmlIgnore]
        public Article Article { get; private set; }

        #endregion

        #region IInputPack Specific Methods

        /// <inheritdoc/>
        void IInputPack.SetArticleInformation(string articleId,
                                              string articleName,
                                              string dosageForm,
                                              string packagingUnit,
                                              uint maxSubItemQuantity,
                                              string virtualArticleId,
                                              string virtualArticleName,
                                              bool requiresFridge)
        {
            if (string.IsNullOrEmpty(articleId))
            {
                throw new ArgumentException("Invalid articleId specified.");
            }

            this.Article = new Article()
            {
                Id = TextConverter.EscapeInvalidXmlChars(articleId),
                Name = string.IsNullOrEmpty(articleName) ? string.Empty : TextConverter.EscapeInvalidXmlChars(articleName),
                DosageForm = string.IsNullOrEmpty(dosageForm) ? string.Empty : TextConverter.EscapeInvalidXmlChars(dosageForm),
                PackagingUnit = string.IsNullOrEmpty(packagingUnit) ? string.Empty : TextConverter.EscapeInvalidXmlChars(packagingUnit),
                MaxSubItemQuantity = maxSubItemQuantity.ToString(),
                VirtualId = string.IsNullOrEmpty(virtualArticleId) ? string.Empty : TextConverter.EscapeInvalidXmlChars(virtualArticleId),
                VirtualName = string.IsNullOrEmpty(virtualArticleName) ? string.Empty : TextConverter.EscapeInvalidXmlChars(virtualArticleName),
                RequiresFridge = requiresFridge.ToString()
            };
        }

        /// <inheritdoc/>
        void IInputPack.SetPackInformation(string batchNumber, string externalId, string serialNumber)
        {
            this.BatchNumber = TextConverter.EscapeInvalidXmlChars(batchNumber);
            this.ExternalId = TextConverter.EscapeInvalidXmlChars(externalId);

            if (serialNumber != null)
            {
                this.SerialNumber = TextConverter.EscapeInvalidXmlChars(serialNumber);
            }
        }

        /// <inheritdoc/>
        void IInputPack.SetPackInformation(string batchNumber,
                                           string externalId,
                                           DateTime? expiryDate,
                                           uint? subItemQuantity,
                                           string stockLocationId,
                                           string serialNumber,
                                           ExpiryDateSource? expiryDateSource)
        {
            this.BatchNumber = TextConverter.EscapeInvalidXmlChars(batchNumber);
            this.ExternalId = TextConverter.EscapeInvalidXmlChars(externalId);

            if (expiryDate.HasValue)
            {
                this.ExpiryDate = TypeConverter.ConvertDate(expiryDate.Value);
            }

            if (subItemQuantity.HasValue)
            {
                this.SubItemQuantity = subItemQuantity.ToString();
            }

            if (stockLocationId != null)
            {
                this.StockLocationId = TextConverter.EscapeInvalidXmlChars(stockLocationId);
            }

            if (serialNumber != null)
            {
                this.SerialNumber = TextConverter.EscapeInvalidXmlChars(serialNumber);
            }

            this.ExpiryDateSource = expiryDateSource.HasValue ? expiryDateSource.ToString() : null;
        }

        /// <inheritdoc/>
        void IInputPack.SetHandling(InputHandling handling)
        {
            this.Handling = new Handling() { Input = handling.ToString() };
        }

        /// <inheritdoc/>
        void IInputPack.SetHandling(InputHandling handling, string message)
        {
            this.Handling = new Handling() { Input = handling.ToString(), Text = TextConverter.EscapeInvalidXmlChars(message) };
        }

        #endregion
    }
}