using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Comal.Classes;
using InABox.Clients;
using InABox.Configuration;
using InABox.Core;
using InABox.DynamicGrid;
using InABox.Core.Reports;
using InABox.Wpf.Reports;
using InABox.WPF;
using Motorola.Snapi;
using Motorola.Snapi.Constants.Enums;
using Motorola.Snapi.EventArguments;
using BarcodeType = Comal.Classes.BarcodeType;
using Color = System.Drawing.Color;
using Image = System.Windows.Controls.Image;
using InABox.Wpf;
using System.ComponentModel;
namespace PRSDesktop
{
    /// 
    ///     Interaction logic for FactoryPanel.xaml
    /// 
    public partial class FactoryPanel : IPanel
    {
        private readonly BitmapImage barcode = PRSDesktop.Resources.barcode.AsBitmapImage();
        private readonly BitmapImage disabled = PRSDesktop.Resources.disabled.AsBitmapImage();
        private readonly BitmapImage speechbubble = PRSDesktop.Resources.speechbubble.AsBitmapImage();
        private readonly BitmapImage starred = PRSDesktop.Resources.report.AsBitmapImage(48, 48);
        private readonly BitmapImage grouped = PRSDesktop.Resources.grouped.AsBitmapImage();
        
        private SetoutDocument[] documents = Array.Empty();
        private readonly List _scanners = new List();
        private readonly string NEARLYDUE_COLOR = "Orange";
        private readonly string NOTYETDUE_COLOR = "PaleGreen";
        private readonly string OVERDUE_COLOR = "Salmon";
        private readonly string PRIORITY_COLOR = "Red";
        private readonly string QA_COLOR = "Silver";
        private readonly string SELECTED_COLOR = "Yellow";
        private readonly string SHARED_COLOR = "Lime";
        private readonly string TREATED_COLOR = "DarkOrchid";
        private Button? _currentButton;
        private PDFEditorControl? PDFEditor;
        private QAGrid? QAGrid;
        
        private readonly FactoryFloorLocalSettings _settings;
        private readonly PanelAction _togglePackPanel;
        
        public FactoryPanel()
        {
            
            _settings = new LocalConfiguration().Load();
            if (_settings.Section.Equals(CoreUtils.FullGuid) && _settings.Station.Equals(-1))
            {
                var user = new UserConfiguration().Load();
                _settings.Section = user.Section;
                _settings.Station = user.Station;
                new LocalConfiguration().Save(_settings);
            }
            
            InitializeComponent();
            var CanConfigure = Security.IsAllowed();
            Section.IsEnabled = CanConfigure;
            Station.IsEnabled = CanConfigure;
            PendingVisible = Security.IsAllowed();
            Kanbans = new ObservableCollection();
            RackContents.ItemsSource = rackcontents;
            RackPanel.Visibility = _settings.SidePanel == FactorySidePanel.Racks
                ? Visibility.Visible
                : Visibility.Collapsed;
            
            PackPanel.Visibility = _settings.SidePanel == FactorySidePanel.Packs
                ? Visibility.Visible
                : Visibility.Collapsed;
            
        }
        public bool IsReady { get; set; }
        public void Setup()
        {
            var setups = new MultiQuery();
            
            setups.Add();
            
            setups.Add(
                new Filter(x => x.Hidden).IsEqualTo(false),
                null,
                new SortOrder(x => x.Factory.Sequence).ThenBy(x => x.Sequence)
            );
            setups.Add();
            
            setups.Add(
                LookupFactory.DefineFilter(),
                Columns.None().Add(x => x.ID, x => x.Name, x => x.UserLink.ID),
                new SortOrder(x => x.Name)
            );
            
            setups.Add(
                LookupFactory.DefineFilter(),
                Columns.None().Add(x => x.ID, x => x.Code, x => x.Description, x => x.BarCode)
                );
            
            if (_settings.AreaID != Guid.Empty)
                setups.Add(
                    new Filter(x=>x.ID).IsEqualTo(_settings.AreaID),
                    Columns.None().Add(x=>x.ID).Add(x=>x.Code).Add(x=>x.Description)
                );
            
            setups.Query();
            Factories = setups.Get().Rows.Select(x => x.ToObject()).ToArray();
            Sections = setups.Get().Rows.Select(x => x.ToObject()).ToArray();
            Trolleys = setups.Get().Rows.Select(x => x.ToObject()).ToArray();
            Shipments = setups.Get();
            CurrentSection = Sections.FirstOrDefault(x => x.ID.Equals(_settings.Section))
                ?? Sections.FirstOrDefault();
            var iSection = 0;
            var iCount = 0;
            foreach (var section in Sections)
            {
                if (section == CurrentSection)
                {
                    iSection = Section.Items.Count;
                    iCount = section.Stations; //Sections[id].Item2;
                }
                Section.Items.Add($"{section.Factory.Name}: {section.Name}");
            }
            Section.SelectedIndex = iSection;
            Station.Items.Clear();
            for (var i = 1; i <= (CurrentSection?.Stations ?? 0); i++)
                Station.Items.Add($"Station #{i}");
            CurrentStation = _settings.Station + 1;
            if (CurrentStation < 1)
                CurrentStation = 1;
            if (CurrentStation > iCount)
                CurrentStation = iCount;
            Station.SelectedIndex = CurrentStation - 1;
            if (_settings.AreaID != Guid.Empty)
            {
                var area = setups.Get().ToObjects().FirstOrDefault();
                if (area != null)
                {
                    _packGrid.Area = area;
                    _areaDescription.Text = $"{area.Code}";
                    _addPack.IsEnabled = true;
                }
                else
                {
                    _packGrid.Area = null;
                    _areaDescription.Text = "Set\r\nArea";
                    _settings.AreaID = Guid.Empty;
                    new LocalConfiguration().Save(_settings);
                    _addPack.IsEnabled = false;
                }
            }
            else
            {
                _packGrid.Area = null;
                _areaDescription.Text = "Set\r\nArea";
            }
            _packGrid.Refresh(true,true);
            SetupScanner();
        }
        public void Shutdown(CancelEventArgs? cancel)
        {
            ShutdownScanner();
            //UpdateTimeTracking(true);
            LoadDrawing(null);
        }
        public void CreateToolbarButtons(IPanelHost host)
        {
            ManufacturingSetupActions.Standard(host);
            //if (Security.IsAllowed())
            //    host.CreatePanelAction(new PanelAction() { Caption = "Self Assessment", Image = PRSDesktop.Resources.star, OnExecute = DoSelfAssessment });
            host.CreatePanelAction(new PanelAction { Caption = "Lost Time", Image = PRSDesktop.Resources.smiley, OnExecute = DoSelectLostTime });
            host.CreatePanelAction(new PanelAction
            { Caption = "Treament PO", Image = PRSDesktop.Resources.purchase, OnExecute = DoCreatePurchaseOrder });
            host.CreatePanelAction(new PanelAction
            { Caption = "Treatment Delivery", Image = PRSDesktop.Resources.barcode, OnExecute = DoScanDeliveryItems });
            host.CreatePanelAction(new PanelAction { Caption = "Select Rack", Image = PRSDesktop.Resources.forklift, OnExecute = DoSelectRack });
            host.CreatePanelAction(new PanelAction { Caption = "Close Rack", Image = PRSDesktop.Resources.forklift, OnExecute = DoCloseRack });
            host.CreatePanelAction(new PanelAction { Caption = "Scan Barcode", Image = PRSDesktop.Resources.product, OnExecute = DoScanBarcode });
            
            host.CreatePanelAction(new PanelAction { Caption = _settings.SidePanel == FactorySidePanel.Packs ? "Hide Stock" : "Show Stock", Image = PRSDesktop.Resources.trolley, OnExecute = TogglePackPanel});
        }
        private void TogglePackPanel(PanelAction pa)
        {
            RackPanel.Visibility = Visibility.Collapsed;
            PackPanel.Visibility = _settings.SidePanel == FactorySidePanel.Packs
                ? Visibility.Collapsed
                : Visibility.Visible;
            _settings.SidePanel = PackPanel.Visibility == Visibility.Visible
                ? FactorySidePanel.Packs
                : FactorySidePanel.None;
            new LocalConfiguration().Save(_settings);
            pa.Caption = _settings.SidePanel == FactorySidePanel.Packs
                ? "Hide Stock"
                : "Show Stock";
            _addPack.IsEnabled = _settings.AreaID != Guid.Empty;
        }
        public Dictionary Selected()
        {
            var rows = Packets.Rows.Where(p => Kanbans.Any(k => k.Checked && p.Get(c => c.ID).ToString().Equals(k.ID)));
            var pkts = rows.ToArray().Select(r => r.ToObject());
            return new Dictionary { { typeof(ManufacturingPacket).EntityName(), pkts.ToArray() } };
        }
        public string SectionName => "Factory";
        public DataModel DataModel(Selection selection)
        {
            var rows = (Packets == null) || (selection == Selection.None)
                ? new CoreRow[] { }
                : selection == Selection.Selected
                    ? Packets.Rows.Where(p => Kanbans.Any(k => k.Checked && p.Get(c => c.ID).ToString().Equals(k.ID)))
                    : Packets.Rows;
            var ids = rows.Any() ? rows.Select(r => r.Get(x => x.ID)).ToArray() : new[] { CoreUtils.FullGuid };
            return new ManufacturingPacketDataModel(new Filter(x => x.ID).InList(ids));
        }
        public event DataModelUpdateEvent? OnUpdateDataModel;
        public void Heartbeat(TimeSpan time)
        {
            // Security Descriptor should be disabled for non-manufacturing staff
            if (!Security.IsAllowed() || CurrentSection == null)
                return;
            // If Lost Time is Active, let's record that
            if (LostTime != null)
            {
                var history = new ManufacturingHistory();
                history.Date = DateTime.Today;
                history.Employee.ID = App.EmployeeID;
                history.Packet.ID = Guid.Empty;
                history.LostTime.ID = LostTime.ID;
                history.Description = LostTime.Description;
                //history.Setout.ID = CheckedPackets[id].Item3;
                //history.Job.ID = CheckedPackets[id].Item4;
                history.Section.ID = CurrentSection.ID;
                history.Station = CurrentStation;
                history.Activity.ID = LostTime.Activity.ID;
                history.WorkDuration = new TimeSpan(time.Ticks);
                history.QACompleted = 0;
                history.WorkCompleted = 0;
                history.Window = time;
                new Client().Save(history, "", (o, e) => { });
            }
            else
            {
                var CheckedPackets = new Dictionary>();
                foreach (var kanban in Kanbans.Where(x => x.Checked))
                {
                    var packet = KanbanToPacket(kanban);
                    if (packet != null)
                    {
                        var stagerow = Stages.Rows.FirstOrDefault(r => r.Get(c => c.Parent.ID).Equals(packet.ID)
                                                                       && r.Get(c => c.ManufacturingSectionLink.ID)
                                                                           .Equals(_settings.Section));
                        if (stagerow != null)
                        {
                            var stage = stagerow.ToObject();
                            if (stage.Station != 0)
                                CheckedPackets[packet.ID] = new Tuple(stage.Started, stage.Completed,
                                    packet.SetoutLink.ID, packet.SetoutLink.JobLink.ID, packet.Serial);
                        }
                    }
                }
                if (!CheckedPackets.Any())
                    CheckedPackets[Guid.Empty] =
                        new Tuple(DateTime.MinValue, DateTime.MinValue, Guid.Empty, Guid.Empty, "No Packet");
                var updates = new List();
                var slice = time.Ticks / CheckedPackets.Count;
                foreach (var id in CheckedPackets.Keys)
                {
                    var history = new ManufacturingHistory();
                    history.Date = DateTime.Today;
                    history.Employee.ID = App.EmployeeID;
                    history.Packet.ID = id;
                    history.Description = CheckedPackets[id].Item5;
                    //history.Setout.ID = CheckedPackets[id].Item3;
                    //history.Job.ID = CheckedPackets[id].Item4;
                    history.Section.ID = CurrentSection.ID;
                    history.Activity.ID = CurrentSection.Activity.ID;
                    history.Station = CurrentStation;
                    if (CheckedPackets[id].Item1.Equals(DateTime.MinValue))
                        history.QADuration = new TimeSpan(slice);
                    else
                        history.WorkDuration = new TimeSpan(slice);
                    history.QACompleted = CheckedPackets[id].Item1.Equals(DateTime.MinValue) ? 0 : 0;
                    history.WorkCompleted = CheckedPackets[id].Item2.Equals(DateTime.MinValue) ? 0 : 1;
                    history.Window = time;
                    updates.Add(history);
                }
                if (updates.Any())
                    new Client().Save(updates, "", (o, e) => { });
            }
        }
        private void ShutdownScanner()
        {
            try
            {
                foreach (var scanner in _scanners) scanner.Actions.ToggleLed(LedMode.GreenOff);
                BarcodeScannerManager.Instance.DataReceived -= Instance_DataReceived;
                BarcodeScannerManager.Instance.Close();
            }
            catch (Exception e)
            {
                MessageBox.Show("Error Shutting down Scanner!\n\n" + e.Message);
            }
        }
        private void SetupScanner()
        {
            _scanners.Clear();
            BarcodeScannerManager.Instance.Open();
            BarcodeScannerManager.Instance.RegisterForEvents(EventType.Barcode, EventType.Pnp, EventType.Image, EventType.Other, EventType.Rmd);
            BarcodeScannerManager.Instance.GetDevices();
            foreach (var scanner in BarcodeScannerManager.Instance.GetDevices())
            {
                _scanners.Add(scanner);
                scanner.Actions.ToggleLed(LedMode.RedOn);
                scanner.Actions.SoundBeeper(BeepPattern.FastWarble);
            }
            BarcodeScannerManager.Instance.DataReceived += Instance_DataReceived;
        }
        private void Instance_DataReceived(object? sender, BarcodeScanEventArgs e)
        {
            Dispatcher.Invoke(() => { ProcessCode(_scanners[(int)e.ScannerId], e.Data); });
        }
        private void ProcessCode(IMotorolaBarcodeScanner scanner, string code)
        {
            try
            {
                var isGuid = Guid.TryParse(code, out var guid);
                if (isGuid)
                {
                    if (Sections.Any(x => x.ID.Equals(guid)))
                    {
                        IsReady = false;
                        CurrentSection = Sections.First(x => x.ID.Equals(guid));
                        Section.SelectedIndex = Array.IndexOf(Sections, CurrentSection);
                        IsReady = true;
                        Progress.Show("Changing Sections");
                        DoRefresh(true);
                        Progress.Close();
                        scanner?.Actions.SoundBeeper(BeepPattern.FastWarble);
                    }
                    else if (Trolleys.Any(x => x.ID.Equals(guid)))
                    {
                        var trolley = Trolleys.First().Code;
                        var updates = new List();
                        foreach (var kanban in Kanbans.Where(x => x.Checked))
                        {
                            var packet = KanbanToPacket(kanban);
                            packet.Trolleys = trolley;
                            updates.Add(packet);
                            var pktrow = Packets.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(Guid.Parse(kanban.ID)));
                            var stagerow = Stages.Rows.FirstOrDefault(r => r.Get(c => c.Parent.ID).Equals(packet.ID)
                                                                           && r.Get(
                                                                                   c => c.ManufacturingSectionLink.ID)
                                                                               .Equals(_settings.Section));
                            pktrow.Set(x => x.Trolleys, packet.Trolleys);
                            LoadModel(kanban, pktrow, stagerow, true);
                            UpdateSelectedKanban(false);
                        }
                        if (updates.Any())
                            new Client().Save(updates, "Set Trolley to " + trolley, (o, e) => { });
                        scanner?.Actions.SoundBeeper(BeepPattern.LowHigh);
                    }
                    else
                    {
                        scanner?.Actions.SoundBeeper(BeepPattern.FourLowShort);
                    }
                }
                else
                {
                    var shiprow = Shipments.Rows.FirstOrDefault(r => r.Get(c => c.BarCode).Equals(code));
                    if (shiprow != null)
                    {
                        if (string.Equals(code, rackbarcode))
                        {
                            RackPanel.Visibility = Visibility.Collapsed;
                            _settings.SidePanel = FactorySidePanel.None;
                            new LocalConfiguration().Save(_settings);
                            
                            RackContents.ItemsSource = null;
                            rackid = Guid.Empty;
                            rackbarcode = "";
                            scanner?.Actions.SoundBeeper(BeepPattern.ThreeLowShort);
                        }
                        else
                        {
                            RackContents.ItemsSource = null;
                            rackid = shiprow.Get(c => c.ID);
                            rackbarcode = code;
                            var rows = DeliveryItems.Rows.Where(r => r.Get(c => c.ShipmentLink.ID).Equals(rackid)).ToArray();
                            rackcontents.Clear();
                            rackcontents.AddRange(rows.Select(x => x.ToObject()));
                            RackContents.ItemsSource = rackcontents;
                            RackName.Content = $"Rack {shiprow.Get(c => c.Code)}";
                            RackCount.Content = rows.Length.ToString();
                            PackPanel.Visibility = Visibility.Collapsed;
                            RackPanel.Visibility = Visibility.Visible;
                            _settings.SidePanel = FactorySidePanel.Racks;
                            new LocalConfiguration().Save(_settings);
                            scanner?.Actions.SoundBeeper(BeepPattern.ThreeHighShort);
                        }
                    }
                    else
                    {
                        if (RackPanel.Visibility == Visibility.Visible)
                        {
                            var row = DeliveryItems.Rows.FirstOrDefault(r => r.Get(c => c.Barcode).Equals(code));
                            if (row != null)
                            {
                                var delitem = rackcontents.FirstOrDefault(x => string.Equals(x.Barcode, code));
                                if (delitem != null)
                                {
                                    rackcontents.Remove(delitem);
                                    delitem.ShipmentLink.ID = Guid.Empty;
                                    new Client().Save(delitem, "Item Removed From Rack", (o, e) => { });
                                    row.Set(x => x.ShipmentLink.ID, Guid.Empty);
                                    scanner?.Actions.SoundBeeper(BeepPattern.HighLow);
                                }
                                else
                                {
                                    delitem = row.ToObject();
                                    delitem.ShipmentLink.ID = rackid;
                                    rackcontents.Add(delitem);
                                    new Client().Save(delitem, "Item Added to " + RackName.Content, (o, e) => { });
                                    row.Set(x => x.ShipmentLink.ID, rackid);
                                    if (Kanbans.Any(x => string.Equals(x.ID, delitem.ManufacturingPacketLink.ID.ToString())))
                                        ReloadPackets(true);
                                    scanner?.Actions.SoundBeeper(BeepPattern.LowHigh);
                                }
                                RackContents.ItemsSource = null;
                                RackContents.ItemsSource = rackcontents;
                                RackCount.Content = rackcontents.Count.ToString();
                            }
                        }
                        else
                        {
                            var id = new Client().Query(
                                new Filter(x => x.Barcode).IsEqualTo(code),
                                Columns.None().Add(x => x.ManufacturingPacketLink.ID)
                            ).Rows.FirstOrDefault()?.Get(x => x.ManufacturingPacketLink.ID);
                            if (!id.HasValue)
                            {
                                scanner?.Actions.SoundBeeper(BeepPattern.FourLowShort);
                                return;
                            }
                            var kanban = Kanbans.FirstOrDefault(x => string.Equals(x.ID, id.Value.ToString()));
                            if (kanban == null)
                            {
                                // Error - packet not visible from this station
                                scanner?.Actions.SoundBeeper(BeepPattern.FourLowShort);
                                return;
                            }
                            var stage = Stages.Rows.FirstOrDefault(r => r.Get(c => c.Parent.ID).Equals(id)
                                                                        && r.Get(c => c.ManufacturingSectionLink.ID)
                                                                            .Equals(_settings.Section))?.ToObject();
                            if (stage == null)
                                scanner?.Actions.SoundBeeper(BeepPattern.FourHighShort);
                            if (stage.Station == 0) AddPacketToCurrentWorkload(stage);
                            if (kanban != CurrentKanban)
                            {
                                CurrentKanban = kanban;
                                UpdateSelectedKanban(false);
                            }
                            scanner?.Actions.SoundBeeper(BeepPattern.LowHigh);
                        }
                    }
                }
            }
            catch (Exception)
            {
                scanner?.Actions.SoundBeeper(BeepPattern.FourLowShort);
            }
        }
        private void LoadDrawing(Guid? id)
        {
            if (PDFEditor != null)
            {
                PDFEditor.Document = null;
                // Unload PDF Annotations
                PDFEditor = null;
            }
            else if (QAGrid != null)
            {
                Editor.Content = null;
                // Unload QA Answers
                QAGrid = null;
            }
            if (id.HasValue)
            {
                var pktid = CurrentKanban != null ? Guid.Parse(CurrentKanban.ID) : Guid.Empty;
                var row = Packets.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(pktid));
                var stagerow = Stages.Rows.FirstOrDefault(r => r.Get(c => c.Parent.ID).Equals(pktid)
                                                               && r.Get(c => c.ManufacturingSectionLink.ID)
                                                                   .Equals(_settings.Section));
                var packet = row.ToObject();
                if (id.Value == Guid.Empty)
                {
                    QAGrid = new QAGrid();
                    var qadata = stagerow != null ? stagerow.Get(c => c.FormData) : "";
                    var values = string.IsNullOrWhiteSpace(qadata)
                        ? new Dictionary()
                        : Serialization.Deserialize>(qadata);
                    if (CurrentSection != null && row != null)
                    {
                        var genquestions = LoadQAQuestions(packet, true, false);
                        QAGrid.LoadChecks("Global Checks for " + CurrentSection.Name, genquestions, values);
                        //var specquestions = LoadQAQuestions(packet, false, true);
                        //QAGrid.LoadChecks(String.Format("Specific Checks for {0} [{1}] template", packet.ManufacturingTemplateLink.Name, packet.ManufacturingTemplateLink.Code), specquestions, values);
                    }
                    QAGrid.OnChanged += QAGridChanged;
                    Editor.Content = QAGrid;
                }
                else
                {
                    PDFEditor = new PDFEditorControl();
                    PDFEditor.LineColor = _settings.LineColor;
                    PDFEditor.TextSize = _settings.FontSize;
                    //PDFEditor.PrintAllowed = Security.IsAllowed();
                    PDFEditor.SaveAllowed = Security.IsAllowed();
                    var document = documents.FirstOrDefault(x => x.DocumentLink.ID.Equals(id));
                    PDFEditor.Watermark = packet.WaterMark;
                    PDFEditor.Document = document;
                    PDFEditor.PDFSettingsChanged += PDFEditorSettingsChanged;
                    Editor.Content = PDFEditor;
                }
            }
        }
        private void DoSelectRack(PanelAction obj)
        {
            var dlg = new MultiSelectDialog(
                null,
                Columns.None().Add(x => x.ID, x => x.Code, x => x.Description, x => x.BarCode),
                false
            );
            if (dlg.ShowDialog())
            {
                var id = dlg.IDs().FirstOrDefault();
                var barcode = dlg.Data().Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(id))?.Get(x => x.BarCode);
                ProcessCode(_scanners.FirstOrDefault(), barcode);
            }
        }
        private void DoCloseRack(PanelAction obj)
        {
            if (!string.IsNullOrWhiteSpace(rackbarcode))
                ProcessCode(_scanners.FirstOrDefault(), rackbarcode);
        }
        private void DoScanBarcode(PanelAction obj)
        {
            var dlg = new MultiSelectDialog(
                new Filter(x => x.DeliveredDate).IsEqualTo(DateTime.MinValue)
                    .And(x => x.ManufacturingPacketLink).LinkValid(),
                Columns.None().Add(x => x.ID, x => x.Barcode, x => x.Description),
                false
            );
            if (dlg.ShowDialog())
            {
                var id = dlg.IDs().FirstOrDefault();
                var barcode = dlg.Data().Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(id))
                    ?.Get(x => x.Barcode);
                ProcessCode(_scanners.FirstOrDefault(), barcode);
            }
        }
        private void DoScanDeliveryItems(PanelAction obj)
        {
            new DeliveryBuilder(Guid.Empty, Guid.Empty).ShowDialog();
        }
        private void DoCreatePurchaseOrder(PanelAction obj)
        {
            //Guid section = CurrentSection != null ? CurrentSection.ID : CoreUtils.FullGuid;
            //var stages = Stages.Rows.Where(row => row.Get(col => col.ManufacturingSectionLink.ID).Equals(section) && row.Get(col => col.Station).Equals(CurrentStation));
            //var ids = stages.Select(row => row.Get(col => col.ManufacturingPacketLink.ID)).ToArray();
            var ids = Kanbans.Where(x => !x.ColorKey.Equals(QA_COLOR)).Select(x => Guid.Parse(x.ID)).ToArray();
            var treatments = new Client().Query(new Filter(x => x.Packet.ID).InList(ids));
            var window = new ManufacturingTreatmentWindow(treatments);
            if (window.ShowDialog() != true)
                return;
            Progress.Show("Creating Purchase Order");
            var order = new PurchaseOrder();
            //Supplier supplier = new Client().Query(
            //    new Filter(x => x.ID).IsEqualTo(window.SupplierID),
            //    new Columns(x=>x.ID
            //).Rows.FirstOrDefault()?.ToObject());
            order.SupplierLink.ID = window.SupplierID;
            order.SupplierLink.Name = window.SupplierName; //supplier != null ? supplier.Name : "Unknown Supplier";
            order.RaisedBy.ID = App.EmployeeID;
            order.IssuedBy.ID = App.EmployeeID;
            order.IssuedDate = DateTime.Today;
            order.DueDate = DateTime.Today.AddDays(7);
            new Client().Save(order,
                $"Materials Processing Request raised by {App.EmployeeName} from Factory Floor");
            Progress.SetMessage("Processing Order");
            var orderitems = new List();
            var packetupdates = new List();
            var query = new MultiQuery();
            query.Add(
                new Filter(x => x.Product.ID).IsEqualTo(window.ProductID).And(x => x.SupplierLink.ID)
                    .IsEqualTo(window.SupplierID),
                Columns.None().Add(x => x.Job.ID).Add(x => x.CostPrice).Add(x => x.Product.NettCost)
            );
            query.Add(
                new Filter(x => x.ID).IsEqualTo(window.ProductID),
                LookupFactory.DefineLookupColumns(x => x.Product).Add(x => x.NettCost)
            );
            query.Query();
            var supprods = query.Get().Rows.Select(x => x.ToObject()).ToArray();
            var supprice = supprods.FirstOrDefault(x => Equals(x.Job.ID, Guid.Empty));
            var product = query.Get().Rows.FirstOrDefault()?.ToObject();
            var stdcost = supprice != null
                ? supprice.CostPrice
                : product != null
                    ? product.NettCost
                    : 0.0F;
            foreach (var row in window.Selected)
            {
                var treatment = row.ToObject();
                var packet = Packets.Rows.First(p => p.Get(c => c.ID).Equals(treatment.Packet.ID))
                    .ToObject();
                packetupdates.Add(packet);
                var item = new PurchaseOrderItem();
                item.PurchaseOrderLink.ID = order.ID;
                item.Job.ID = treatment.Packet.SetoutLink.JobLink.ID;
                item.Packet.ID = packet.ID;
                item.Product.ID = window.ProductID;
                if(product is not null)
                {
                    item.Product.Synchronise(product);
                }
                item.Qty = packet.BarcodeQty;
                item.Dimensions.Length = treatment.Parameter == 0.0F ? 1.0F : treatment.Parameter;
                var jobprice = supprods.FirstOrDefault(x => x.Job.ID.Equals(item.Job.ID));
                item.Cost = (jobprice != null ? jobprice.CostPrice : stdcost) * treatment.Parameter;
                var description = new List();
                description.Add($"{packet.BarcodeQty} x {packet.Serial} - {packet.Title}");
                var dimensions = new List();
                if (packet.Height > 0.0F)
                    dimensions.Add($"H:{packet.Height:F2}mm");
                if (packet.Width > 0.0F)
                    dimensions.Add($"W:{packet.Width:F2}mm");
                if (packet.Length > 0.0F)
                    dimensions.Add($"L:{packet.Length:F2}mm");
                dimensions.Add($"Param:{treatment.Parameter:F4}");
                if (dimensions.Any())
                    description.Add(string.Format("Dimensions: {0}", string.Join(" ", dimensions)));
                description.Add($"Treatment: {treatment.Product.Code}: {treatment.Product.Name}");
                item.Description = string.Join("\n", description);
                orderitems.Add(item);
            }
            new Client().Save(orderitems, "Created by Factory Floor Purchase");
            Progress.SetMessage("Updating Packets");
            foreach (var orderitem in orderitems)
            {
                var packet = packetupdates.FirstOrDefault(x => x.ID.Equals(orderitem.Packet.ID));
                if (packet != null)
                    packet.OrderItem.ID = orderitem.ID;
            }
            new Client().Save(packetupdates.Where(x => x.IsChanged()), "");
            Progress.SetMessage("Creating Delivery");
            var delivery = new Delivery();
            delivery.Date = DateTime.Today;
            delivery.Due = DateTime.Today;
            delivery.Employee.ID = App.EmployeeID;
            var sb = new StringBuilder();
            sb.Append("Serial # ");
            sb.AppendLine();
            sb.AppendLine();
            delivery.Notes = string.Format("Delivery of Items for processing to {0}\nOrder #{1} (raised on {2:dd MMM yy} by {3})\nItems: {4}",
                order.SupplierLink.Name,
                order.PONumber,
                delivery.Date,
                App.EmployeeName,
                string.Join(", ",
                    orderitems.Select(x =>
                        string.Format("{0}{1}", x.Description.Split('\n').FirstOrDefault(), x.Qty > 1 ? " (x" + x.Qty.ToString("F0") + ")" : "")))
            );
            new Client().Save(delivery, "Created by Factory Floor Purchase");
            Progress.Close();
            PrintDeliveryBarcode(delivery, order);
            PrintOrderItemBarcodes(delivery, order);
            SendPurchaseNotification(order.PONumber);
            MessageBox.Show("All Done");
            new DeliveryBuilder(delivery.ID, order.ID).ShowDialog();
            Refresh();
        }
        private void SendPurchaseNotification(string PONumber)
        {
            var updates = new List();
            var roles = new Client().Query(new Filter(x => x.RoleLink.Code).IsEqualTo("PURCHASES"));
            foreach (var role in roles.Rows)
            {
                var notification = new Notification
                {
                    Title = string.Format("Treatment PO #{0} has been raised", PONumber),
                    Description = "The above Purchase Order has been created, and is ready to be checked and issued to the relevant supplier."
                };
                notification.Sender.ID = App.EmployeeID;
                notification.Employee.ID = role.Get(x => x.EmployeeLink.ID);
                updates.Add(notification);
            }
            new Client().Save(updates, "Sent Notification");
        }
        private void PrintDeliveryBarcode(Delivery delivery, PurchaseOrder order)
        {
            var model = new ManufacturingTreatmentDataModel(order, delivery);
            var templatename = "Print Treatment Delivery Bar Code";
            var sectionName = "Treatment Delivery Bar Code";
            var template = new Client()
                .Load(
                    new Filter(x => x.Name).IsEqualTo(templatename)
                        .And(x => x.DataModel).IsEqualTo(model.Name)
                        .And(x => x.Section).IsEqualTo(sectionName)
                ).FirstOrDefault();
            if (template == null)
            {
                template = new ReportTemplate
                {
                    DataModel = model.Name,
                    Section = sectionName,
                    Name = templatename
                };
                new Client().Save(template, "Auto Created Report Template");
            }
            ReportUtils.PreviewReport(template, model, !Security.IsAllowed(), Security.IsAllowed());
        }
        private void PrintOrderItemBarcodes(Delivery delivery, PurchaseOrder order)
        {
            var model = new ManufacturingTreatmentDataModel(order, delivery);
            var templatename = "Print Treatment Item Bar Codes";
            var sectionName = "Treatment Item Bar Code";
            var template = new Client()
                .Load(
                    new Filter(x => x.Name).IsEqualTo(templatename)
                        .And(x => x.DataModel).IsEqualTo(model.Name)
                        .And(x => x.Section).IsEqualTo(sectionName)
                ).FirstOrDefault();
            if (template == null)
            {
                template = new ReportTemplate
                {
                    DataModel = model.Name,
                    Section = sectionName,
                    Name = templatename
                };
                new Client().Save(template, "Auto Created Report Template");
            }
            ReportUtils.PreviewReport(template, model, !Security.IsAllowed(), Security.IsAllowed());
        }
        private CoreTable POItemTable(IEnumerable items)
        {
            var result = new CoreTable();
            result.LoadColumns(typeof(PurchaseOrderItem));
            result.LoadRows(items);
            return result;
        }
        private void DoSelectLostTime(PanelAction obj)
        {
            var chooser = new FactoryLostTimeChooser();
            if (chooser.ShowDialog() == true)
            {
                LostTime = chooser.SelectedLostTime;
                LostTimeDescription.Content = LostTime.Description;
                LostTimeActive.Visibility = Visibility.Visible;
            }
        }
        private void CancelLostTime_Click(object sender, RoutedEventArgs e)
        {
            LostTime = null;
            LostTimeActive.Visibility = Visibility.Collapsed;
        }
        //private void Current_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        //{
        //    MessageBox.Show("Exception!\n\n" + e.Exception.Message + "\n\n" + e.Exception.StackTrace);
        //    e.Handled = true;
        //}
        private void Section_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!IsReady)
                return;
            CurrentSection = Sections[Section.SelectedIndex];
            //Kanban.Columns.Clear();
            //Kanban.Columns.Add(new KanbanColumn() { Title = CurrentSection, Categories = CurrentSection.ID.ToString() });
            //Kanban.Columns[0].AllowDrag = false;
            //Kanban.Columns[0].PreviewMouseWheel += CardBorder_PreviewMouseWheel;
            IsReady = false;
            Station.Items.Clear();
            for (var i = 1; i <= CurrentSection.Stations; i++)
                Station.Items.Add(string.Format("Station #{0}", i));
            IsReady = true;
            Station.SelectedIndex = 0;
        }
        private void Station_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!IsReady)
                return;
            SelectSectionAndStation(CurrentSection.ID, Station.SelectedIndex + 1);
        }
        private void SelectSectionAndStation(Guid section, int station)
        {
            CurrentKanban = null;
            CurrentStation = station;
            _settings.Section = section;
            _settings.Station = CurrentStation - 1;
            new LocalConfiguration().Save(_settings);
            DoRefresh(true);
        }
        private void LoadKanban()
        {
            using (new WaitCursor())
            {
                if (CurrentKanban == null)
                {
                    ButtonStack.Children.Clear();
                    LoadDrawing(null);
                    _packGrid.CurrentPacket = null;
                }
                else if (true) // Kanban has changed - how do we figure this out 
                {
                    var packet = KanbanToPacket(CurrentKanban);
                    _packGrid.CurrentPacket = packet;
                    
                    //SetoutStage stage = packet.GetCurrentStage();
                    var row = Stages.Rows.FirstOrDefault(r => r.Get(c => c.Parent.ID).Equals(packet.ID)
                                                              && r.Get(c => c.ManufacturingSectionLink.ID)
                                                                  .Equals(_settings.Section));
                    var station = row != null ? row.Get(c => c.Station) : 0;
                    var quality = row != null ? row.Get(c => c.QualityStatus) : QualityStatus.NotChecked;
                    var percentage = row != null ? row.Get(c => c.PercentageComplete) : 0.00F;
                    var Pending = station == 0; //packet.StageLink.Station == 0;
                    // Set the Proper Button Set for this type of Packet
                    MfgRow.Height = Pending ? new GridLength(00) : new GridLength(50);
                    foreach (var btn in ButtonGrid.FindVisualChildren