using CareFusion.Mosaic.Interfaces.Converters;
using CareFusion.Mosaic.Interfaces.Messages;
using CareFusion.Mosaic.Interfaces.Messages.Task;
using CareFusion.Mosaic.Interfaces.Types.Output;
using CareFusion.Mosaic.Interfaces.Types.Packs;
using Rowa.Mosaic.Shared.Converters;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace CareFusion.Mosaic.Converters.Wwks2.Messages.Task
{
    /// <summary>
    /// Class which represents the WWKS 2.0 TaskInfoResponse message envelope.
    /// </summary>
    [XmlType(TypeName = "WWKS")]
    public class TaskInfoResponseEnvelope : EnvelopeBase, IMessageConversion
    {
        [XmlElement]
        public TaskInfoResponse TaskInfoResponse { get; set; }

        /// <summary>
        /// Translates this object instance into a Mosaic message.
        /// </summary>
        /// <param name="converterStream">The converter stream instance which request the message conversion.</param>
        /// <returns>
        /// The Mosaic message representation of this object.
        /// </returns>
        public MosaicMessage ToMosaicMessage(IConverterStream converterStream)
        {
            return this.TaskInfoResponse.ToMosaicMessage(converterStream);
        }
    }

    /// <summary>
    /// Class which represents the WWKS 2.0 TaskInfoResponse message.
    /// <see cref="https://portalrowa.carefusion.com/Unternehmen/Entwicklung/TechCom/Writing/P-010-044-B-I-388-DEU.docx" />
    /// </summary>
    public class TaskInfoResponse : MessageBase, IMessageConversion
    {
        #region Properties

        [XmlElement]
        public Types.Task[] Task { get; set; }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskInfoResponse"/> class.
        /// </summary>
        public TaskInfoResponse()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskInfoResponse"/> class.
        /// </summary>
        /// <param name="message">The message to use for initialization.</param>
        public TaskInfoResponse(MosaicMessage message)
        {
            var response = (Interfaces.Messages.Task.TaskInfoResponse)message;

            this.Id = response.ID;
            this.Source = response.Source;
            this.Destination = response.Destination;

            if (response.Tasks.Count == 0)
            {
                return;
            }

            this.Task = new Types.Task[response.Tasks.Count];

            for (int i = 0; i < this.Task.Length; ++i)
            {
                var task = response.Tasks[i];

                var articles = new List<Types.Article>();
                var boxes = new List<Types.Box>();

                foreach (string boxNumber in task.BoxNumbers)
                {
                    boxes.Add(new Types.Box { Number = boxNumber });
                }

                if (task.OutputPacks.Count > 0)
                {
                    this.ProcessStockOutputTaskResponsePacks(task, articles);
                }
                else if (task.DeliveryItems.Count > 0)
                {
                    this.ProcessStockDeliveryTaskResponsePacks(task, articles);
                }

                this.Task[i] = new Types.Task
                {
                    Id = task.ID,
                    Status = task.State.ToString(),
                    Type = task.Type.ToString(),
                    Article = articles.Count > 0 ? articles.ToArray() : null,
                    Box = boxes.Count > 0 ? boxes.ToArray() : null
                };
            }
        }

        /// <summary>
        /// Translates this object instance into a Mosaic message.
        /// </summary>
        /// <param name="converterStream">The converter stream instance which request the message conversion.</param>
        /// <returns>
        /// The Mosaic message representation of this object.
        /// </returns>
        public MosaicMessage ToMosaicMessage(IConverterStream converterStream)
        {
            var response = new Interfaces.Messages.Task.TaskInfoResponse(converterStream);

            response.ID = this.Id;
            response.Source = this.Source;
            response.Destination = this.Destination;

            if (this.Task != null)
            {
                for (int i = 0; i < this.Task.Length; ++i)
                {
                    var task = this.Task[i];
                    var msgTask = new Interfaces.Messages.Task.Task()
                    {
                        ID = task.Id,
                        State = InternalTypeConverter.ConvertTaskState(task.Status),
                        Type = TypeConverter.ConvertEnum<TaskType>(task.Type, TaskType.Output)
                    };

                    if (task.Article == null)
                    {
                        response.Tasks.Add(msgTask);
                        continue;
                    }

                    foreach (var article in task.Article)
                    {
                        if (msgTask.Type == Interfaces.Messages.Task.TaskType.Output)
                        {
                            if (article.Pack == null)
                                continue;

                            foreach (var pack in article.Pack)
                            {
                                msgTask.OutputPacks.Add(new Interfaces.Types.Output.StockOutputOrderItemPack()
                                {
                                    RobotArticleCode = article.Id,
                                    ID = (long)TypeConverter.ConvertULong(pack.Id),
                                    BatchNumber = TextConverter.UnescapeInvalidXmlChars(pack.BatchNumber),
                                    ExternalID = TextConverter.UnescapeInvalidXmlChars(pack.ExternalId),
                                    ExpiryDate = TypeConverter.ConvertDate(pack.ExpiryDate),
                                    ExpiryDateSource = TypeConverter.ConvertEnum(pack.ExpiryDateSource, Interfaces.Types.Stock.ExpiryDateSource.Unknown),
                                    StockInDate = TypeConverter.ConvertDate(pack.StockInDate),
                                    ScanCode = TextConverter.UnescapeInvalidXmlChars(pack.ScanCode),
                                    DeliveryNumber = TextConverter.UnescapeInvalidXmlChars(pack.DeliveryNumber),
                                    Depth = TypeConverter.ConvertInt(pack.Depth),
                                    Width = TypeConverter.ConvertInt(pack.Width),
                                    Height = TypeConverter.ConvertInt(pack.Height),
                                    Shape = TypeConverter.ConvertEnum<PackShape>(pack.Shape, PackShape.Cuboid),
                                    IsInFridge = TypeConverter.ConvertBool(pack.IsInFridge),
                                    BoxNumber = TextConverter.UnescapeInvalidXmlChars(pack.BoxNumber),
                                    SubItemQuantity = TypeConverter.ConvertInt(pack.SubItemQuantity),
                                    LabelState = TypeConverter.ConvertEnum<StockOutputOrderItemPackLabelState>(pack.LabelStatus, StockOutputOrderItemPackLabelState.NotLabelled),
                                    OutputNumber = TypeConverter.ConvertInt(pack.OutputDestination),
                                    OutputPoint = TypeConverter.ConvertInt(pack.OutputPoint),
                                    OutputUser = TextConverter.UnescapeInvalidXmlChars(pack.OutputUser),
                                    StockLocationID = TextConverter.UnescapeInvalidXmlChars(pack.StockLocationId),
                                    MachineLocation = TextConverter.UnescapeInvalidXmlChars(pack.MachineLocation),
                                    Guid = Guid.Parse(TextConverter.UnescapeInvalidXmlChars(pack.GUID.ToString()))
                                });
                            }
                        }
                        else
                        {
                            var deliveryItem = new Interfaces.Types.Input.StockDeliveryItem()
                            {
                                ArticleCode = TextConverter.UnescapeInvalidXmlChars(article.Id),
                                RequestedQuantity = TypeConverter.ConvertInt(article.Quantity)
                            };

                            foreach (var pack in article.Pack)
                            {
                                deliveryItem.Packs.Add(new Interfaces.Types.Input.StockDeliveryItemPack()
                                {
                                    ID = (long)TypeConverter.ConvertULong(pack.Id),
                                    BatchNumber = TextConverter.UnescapeInvalidXmlChars(pack.BatchNumber),
                                    ExternalID = TextConverter.UnescapeInvalidXmlChars(pack.ExternalId),
                                    ExpiryDate = TypeConverter.ConvertDate(pack.ExpiryDate),
                                    ExpiryDateSource = TypeConverter.ConvertEnum(pack.ExpiryDateSource, Interfaces.Types.Stock.ExpiryDateSource.Unknown),
                                    StockInDate = TypeConverter.ConvertDate(pack.StockInDate),
                                    ScanCode = TextConverter.UnescapeInvalidXmlChars(pack.ScanCode),
                                    Depth = TypeConverter.ConvertInt(pack.Depth),
                                    Width = TypeConverter.ConvertInt(pack.Width),
                                    Height = TypeConverter.ConvertInt(pack.Height),
                                    Shape = TypeConverter.ConvertEnum<PackShape>(pack.Shape, PackShape.Cuboid),
                                    IsInFridge = TypeConverter.ConvertBool(pack.IsInFridge),
                                    SubItemQuantity = TypeConverter.ConvertInt(pack.SubItemQuantity),
                                    StockLocationID = TextConverter.UnescapeInvalidXmlChars(pack.StockLocationId),
                                    MachineLocation = TextConverter.UnescapeInvalidXmlChars(pack.MachineLocation),
                                    Guid = Guid.Parse(TextConverter.UnescapeInvalidXmlChars(pack.GUID.ToString()))
                                });
                            }

                            msgTask.DeliveryItems.Add(deliveryItem);
                        }
                    }

                    response.Tasks.Add(msgTask);
                }
            }

            return response;
        }

        #region Private methods

        /// <summary>
        /// Processes the packs for the stock output task response.
        /// </summary>
        /// <param name="processingTask">The processing task.</param>
        /// <param name="articles">The task's articles.</param>
        private void ProcessStockOutputTaskResponsePacks(Interfaces.Messages.Task.Task processingTask, List<Types.Article> articles)
        {
            foreach (var pack in processingTask.OutputPacks)
            {
                var article = articles.Find(a => string.Compare(a.Id, pack.RobotArticleCode) == 0);

                if (article == null)
                {
                    article = new Types.Article
                    {
                        Id = TextConverter.EscapeInvalidXmlChars(pack.RobotArticleCode),
                        Pack = new List<Types.Pack>()
                    };

                    articles.Add(article);
                }

                article.Pack.Add(new Types.Pack
                {
                    Id = ((ulong)pack.ID).ToString(),
                    BatchNumber = TextConverter.EscapeInvalidXmlChars(pack.BatchNumber),
                    ExternalId = TextConverter.EscapeInvalidXmlChars(pack.ExternalID),
                    ExpiryDate = TypeConverter.ConvertDateNull(pack.ExpiryDate),
                    ExpiryDateSource = pack.ExpiryDateSource.ToString(),
                    StockInDate = TypeConverter.ConvertDateNull(pack.StockInDate),
                    ScanCode = TextConverter.EscapeInvalidXmlChars(pack.ScanCode),
                    DeliveryNumber = TextConverter.EscapeInvalidXmlChars(pack.DeliveryNumber),
                    Depth = pack.Depth.ToString(),
                    Width = pack.Width.ToString(),
                    Height = pack.Height.ToString(),
                    Shape = pack.Shape.ToString(),
                    IsInFridge = pack.IsInFridge.ToString(),
                    BoxNumber = TextConverter.EscapeInvalidXmlChars(pack.BoxNumber),
                    SubItemQuantity = pack.SubItemQuantity.ToString(),
                    LabelStatus = pack.LabelState.ToString(),
                    OutputDestination = pack.OutputNumber.ToString(),
                    OutputPoint = pack.OutputPoint.ToString(),
                    OutputUser = TextConverter.EscapeInvalidXmlChars(pack.OutputUser),
                    StockLocationId = TextConverter.EscapeInvalidXmlChars(pack.StockLocationID),
                    MachineLocation = TextConverter.EscapeInvalidXmlChars(pack.MachineLocation),
                    GUID = Guid.Parse(TextConverter.UnescapeInvalidXmlChars(pack.Guid.ToString()))
                });
            }
        }

        /// <summary>
        /// Processes the packs for the stock delivery task response.
        /// </summary>
        /// <param name="processingTask">The processing task.</param>
        /// <param name="articles">The task's articles.</param>
        private void ProcessStockDeliveryTaskResponsePacks(Interfaces.Messages.Task.Task processingTask, List<Types.Article> articles)
        {
            foreach (var item in processingTask.DeliveryItems)
            {
                var article = articles.Find(a => a.Id.Equals(item.ArticleCode));

                if (article == null)
                {
                    article = new Types.Article
                    {
                        Id = TextConverter.EscapeInvalidXmlChars(item.ArticleCode),
                        Quantity = item.RequestedQuantity.ToString(),
                        Pack = new List<Types.Pack>()
                    };
                }


                foreach (var pack in item.Packs)
                {
                    article.Pack.Add(new Types.Pack
                    {
                        Id = ((ulong)pack.ID).ToString(),
                        BatchNumber = TextConverter.EscapeInvalidXmlChars(pack.BatchNumber),
                        ExternalId = TextConverter.EscapeInvalidXmlChars(pack.ExternalID),
                        ExpiryDate = TypeConverter.ConvertDateNull(pack.ExpiryDate),
                        ExpiryDateSource = pack.ExpiryDateSource.ToString(),
                        StockInDate = TypeConverter.ConvertDateNull(pack.StockInDate),
                        ScanCode = TextConverter.EscapeInvalidXmlChars(pack.ScanCode),
                        DeliveryNumber = TextConverter.EscapeInvalidXmlChars(processingTask.ID),
                        Depth = pack.Depth.ToString(),
                        Width = pack.Width.ToString(),
                        Height = pack.Height.ToString(),
                        Shape = pack.Shape.ToString(),
                        IsInFridge = pack.IsInFridge.ToString(),
                        SubItemQuantity = pack.SubItemQuantity.ToString(),
                        StockLocationId = TextConverter.EscapeInvalidXmlChars(pack.StockLocationID),
                        MachineLocation = TextConverter.EscapeInvalidXmlChars(pack.MachineLocation),
                        GUID = Guid.Parse(TextConverter.UnescapeInvalidXmlChars(pack.Guid.ToString()))
                    });
                }

                articles.Add(article);
            }
        }

        #endregion
    }
}