using CareFusion.Lib.StorageSystem;
using CareFusion.Lib.StorageSystem.Output;
using Rowa.Lib.Log.Extensions;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace CareFusion.ITSystemSimulator
{
    /// <summary>
    /// Form to create a new Output Order.
    /// </summary>
    public partial class FOrder : Form
    {
        #region Members

        /// <summary>
        /// List of currently active articles.
        /// </summary>
        private string[] _articleList;

        /// <summary>
        /// Storage system instance to use.
        /// </summary>
        private IStorageSystem _storageSystem;

        /// <summary>
        /// Newly created output order.
        /// </summary>
        private IOutputProcess _order;

        /// <summary>
        /// Order number generator.
        /// </summary>
        private static int _orderNumberGenerator = 99;

        private const string CheckExpiryDate = "checkExpiryDate";
        private const string DtpExpiryDate = "dtpExpiryDate";
        #endregion

        #region Properties

        /// <summary>
        /// Gets the newly created output order.
        /// </summary>
        public IOutputProcess Order
        {
            get { return _order; }
        }

        #endregion


        /// <summary>
        /// Initializes a new instance of the <see cref="FOrder" /> class.
        /// </summary>
        /// <param name="storageSystem">The storage system instance to use.</param>
        /// <param name="articleList">The article list to use.</param>
        public FOrder(IStorageSystem storageSystem, string[] articleList)
        {
            const int rowIndex = 0;

            InitializeComponent();
            cmbArticleId.DropDownStyle = ComboBoxStyle.DropDown;

            _storageSystem = storageSystem ?? throw new ArgumentException("Invalid storageSystem specified.");
            _articleList = articleList;
            _orderNumberGenerator++;
            txtOrderNumber.Text = _orderNumberGenerator.ToString();


            try
            {
                InitDataGridViewNewRow(rowIndex);
            }
            catch (Exception exception)
            {
                this.Error(exception.Message);
                MessageBox.Show(exception.Message);
                CancelCurrentOrder();
            }
        }

        /// <summary>
        /// Handles the Load event of the FOrder control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void FOrder_Load(object sender, EventArgs e)
        {
            cmbPriority.SelectedIndex = 2;
        }

        /// <summary>
        /// Creates a new output order.
        /// </summary>
        /// <returns>Newly created order or null.</returns>
        private IOutputProcess CreateNewOrder()
        {
            OutputProcessPriority priority = OutputProcessPriority.Normal;

            switch (cmbPriority.SelectedIndex)
            {
                case 0: priority = OutputProcessPriority.Lowest; break;
                case 1: priority = OutputProcessPriority.Low; break;
                case 2: priority = OutputProcessPriority.Normal; break;
                case 3: priority = OutputProcessPriority.High; break;
                case 4: priority = OutputProcessPriority.Highest; break;
            }

            var result = _storageSystem.CreateOutputProcess(txtOrderNumber.Text,
                                                            (int)numDestination.Value,
                                                            (int)numPoint.Value,
                                                            priority,
                                                            txtBoxNumber.Text);

            return result;
        }

        /// <summary>
        /// Handles the Click event of the btnOK control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void BtnOK_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtOrderNumber.Text))
            {
                MessageBox.Show(@"Order Number is empty", @"Error");
                return;
            }

            try
            {
                _order = CreateNewOrder();

                if (!VerifyData())
                    return;

                AddOrderCriteria();

                this.DialogResult = DialogResult.OK;
            }
            catch (Exception exception)
            {
                this.Error(exception.Message);
                MessageBox.Show(exception.Message);
                CancelCurrentOrder();
            }
        }

        /// <summary>
        /// Add order criteria to a created order.
        /// </summary>
        private void AddOrderCriteria()
        {
            foreach (DataGridViewRow currentRow in dataGridNewOrders.Rows)
            {
                DateTime? expiryDate = null;

                if (Convert.ToBoolean(currentRow.Cells["checkExpiryDate"].Value))
                {
                    expiryDate = DateTime.Parse(currentRow.Cells["dtpExpiryDate"].Value.ToString());
                }

                if (!ulong.TryParse(currentRow.Cells["txtPackID"].Value.ToString(), out var packID))
                {
                    packID = 0;
                }

                _order.AddCriteria(currentRow.Cells["cmbArticleId"].Value.ToString(),
                    Convert.ToUInt32(currentRow.Cells["numQuantity"].Value.ToString()),
                    currentRow.Cells["txtBatchNumber"].Value.ToString(),
                    currentRow.Cells["txtExternalID"].Value.ToString(),
                    expiryDate,
                    packID,
                    Convert.ToUInt32(currentRow.Cells["numSubItemQuantity"].Value.ToString()),
                    string.IsNullOrEmpty(currentRow.Cells["txtStockLocation"].Value.ToString()) ? null : currentRow.Cells["txtStockLocation"].Value.ToString(),
                    string.IsNullOrEmpty(currentRow.Cells["txtMachineLocation"].Value.ToString()) ? null : currentRow.Cells["txtMachineLocation"].Value.ToString(),
                    Convert.ToBoolean(currentRow.Cells["chkSingleBatch"].Value),
                    string.IsNullOrEmpty(currentRow.Cells["txtSerialNumber"].Value.ToString()) ? null : currentRow.Cells["txtSerialNumber"].Value.ToString());

                if (!string.IsNullOrEmpty(currentRow.Cells["txtTemplateID"].Value.ToString()))
                {
                    _order.Criteria[_order.Criteria.Length - 1].AddLabel(currentRow.Cells["txtTemplateID"].Value.ToString(),
                        currentRow.Cells["txtLabelContent"].Value.ToString());
                }
            }
        }

        /// <summary>
        /// Cancel the current order in case of an error.
        /// </summary>
        private void CancelCurrentOrder()
        {
            _order = null;
            this.DialogResult = DialogResult.Cancel;
        }

        /// <summary>
        /// Handles the Click event of the btnCancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void BtnCancel_Click(object sender, EventArgs e)
        {
            CancelCurrentOrder();
        }

        /// <summary>
        /// Verifies the validity of the current configuration.
        /// </summary>
        /// <returns><c>true</c> if data is valid; <c>false</c> otherwise.</returns>
        private bool VerifyData()
        {
            // setting to false will ignore the last uncommitted row (row which we do not need)
            dataGridNewOrders.AllowUserToAddRows = false;

            foreach (DataGridViewRow currentRow in dataGridNewOrders.Rows)
            {
                if (string.IsNullOrEmpty(currentRow.Cells["cmbArticleId"].Value.ToString()) &&
                    string.IsNullOrEmpty(currentRow.Cells["txtBatchNumber"].Value.ToString()) &&
                    string.IsNullOrEmpty(currentRow.Cells["txtExternalID"].Value.ToString()) &&
                    Convert.ToBoolean(currentRow.Cells["checkExpiryDate"].Value) == false &&
                    (long.TryParse(currentRow.Cells["txtPackID"].Value.ToString(), out long packID) == false))
                {
                    return false;
                }

                if (Convert.ToUInt32(currentRow.Cells["numQuantity"].Value.ToString()) == 0 &&
                    Convert.ToUInt32(currentRow.Cells["numSubItemQuantity"].Value.ToString()) == 0)
                {
                    return false;
                }

                if (string.IsNullOrEmpty(currentRow.Cells["txtTemplateID"].Value.ToString()) !=
                    string.IsNullOrEmpty(currentRow.Cells["txtLabelContent"].Value.ToString()))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Initializes the newly added DataGridView row.
        /// </summary>
        /// <param name="rowIndex">Index of the new added row.</param>
        private void InitDataGridViewNewRow(int rowIndex)
        {
            dataGridNewOrders.Rows[rowIndex].Cells["cmbArticleId"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["txtBatchNumber"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["txtExternalID"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["txtPackID"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["checkExpiryDate"].Value = false;
            dataGridNewOrders.Rows[rowIndex].Cells["numQuantity"].Value = 1;
            dataGridNewOrders.Rows[rowIndex].Cells["numSubItemQuantity"].Value = 0;
            dataGridNewOrders.Rows[rowIndex].Cells["txtStockLocation"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["txtMachineLocation"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["chkSingleBatch"].Value = false;
            dataGridNewOrders.Rows[rowIndex].Cells["txtTemplateID"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["txtLabelContent"].Value = string.Empty;
            dataGridNewOrders.Rows[rowIndex].Cells["txtSerialNumber"].Value = string.Empty;

            lblNumItems.Text = rowIndex.ToString();
        }

        /// <summary>
        /// Handles the RowsRemoved event of the dataGridNewOrders control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="DataGridViewRowsRemovedEventArgs"/> instance containing the event data.</param>
        private void DataGridNewOrders_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            // do not count last row, as it is an uncommitted row
            lblNumItems.Text = (dataGridNewOrders.Rows.Count - 1).ToString();
        }

        /// <summary>
        /// Handles the RowsAdded event of the dataGridNewOrders control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="DataGridViewRowsAddedEventArgs"/> instance containing the event data.</param>
        private void DataGridNewOrders_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            InitDataGridViewNewRow(dataGridNewOrders.Rows.Count - 1);
        }

        /// <summary>
        /// Handles the EditingControlShowing event of the dataGridNewOrders control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="DataGridViewEditingControlShowingEventArgs"/> instance containing the event data.</param>
        private void DataGridNewOrders_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            if (e.Control is ComboBox comboBox)
            {
                comboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
                comboBox.AutoCompleteMode = AutoCompleteMode.SuggestAppend;

                if (_articleList != null)
                {
                    Array.Sort(_articleList, StringComparer.InvariantCulture);
                    comboBox.DataSource = _articleList;
                }
            }
        }

        /// <summary>
        /// Handles the DataError event of the dataGridNewOrders control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="DataGridViewDataErrorEventArgs"/> instance containing the event data.</param>
        private void dataGridNewOrders_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
        }

        private void dataGridNewOrders_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex != -1 && e.ColumnIndex == 4)
            {
                UpdateDatePickerExpiryDateCellState(e.RowIndex);
            }
        }

        private void UpdateDatePickerExpiryDateCellState(int rowIndex)
        {
            var checkExpiryDateCell = dataGridNewOrders.Rows[rowIndex].Cells[CheckExpiryDate] as DataGridViewCheckBoxCell;
            var dtpExpiryDateCell = dataGridNewOrders.Rows[rowIndex].Cells[DtpExpiryDate];

            var isCheckCellChecked = Convert.ToBoolean(checkExpiryDateCell.Value);
            dtpExpiryDateCell.ReadOnly = !isCheckCellChecked;

            dtpExpiryDateCell.Style.BackColor = isCheckCellChecked ? Color.White : Color.LightGray;
            dtpExpiryDateCell.Style.ForeColor = isCheckCellChecked ? Color.Black : Color.DarkGray;
        }

        private void dataGridNewOrders_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            if (dataGridNewOrders.IsCurrentCellDirty)
            {
                dataGridNewOrders.CommitEdit(DataGridViewDataErrorContexts.Commit);
            }
        }
    }
}
