using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

using ClosedXML;
using ClosedXML.Utils;
using ClosedXML.Excel;
using ClosedXML.Excel.Drawings;

using ImageMagick;

using BarcodeStandard;
using SkiaSharp;

namespace Common
{
    public class BarcodeXlsxImporter
    {
        public string preChars = "{";
        public string postChars = "}";
        public bool enabledVerbose = false;
        public bool enabledProgress = false;
        public bool enabledLabel = false;
        public bool enabledRemoveTag = false;
        public int imageWidth = 256;
        public int imageHeight = 64;
        public int marginHeight = 4;
        public int marginWidth = 4;

        public BarcodeXlsxImporter()
        {

        }
        public void Convert(string sourceFileName)
        {
            XLWorkbook book = new (sourceFileName);
            Convert(book);
            book.Save();
        }

        public void Convert(string sourceFileName, string destFileName)
        {
            XLWorkbook book = new (sourceFileName);
            Convert(book);
            book.SaveAs(destFileName);
        }

        public void Convert(Stream inputStream, Stream outputStream)
        {
            XLWorkbook book = new (inputStream);
            Convert(book);
            book.SaveAs(outputStream);
        }

        public void Convert(XLWorkbook book)
        {
            foreach (var sheet in book.Worksheets)
            {
                foreach (var cell in sheet.Cells())
                {
                    string cellValue = cell.GetString();
                    if (cellValue.Length > preChars.Length + postChars.Length)
                    {
                        string preChars = cellValue[0..this.preChars.Length];
                        string postChears = cellValue.Substring(cellValue.Length - this.postChars.Length);

                        if (preChars == this.preChars && postChears == this.postChars)
                        {
                            string barcodeData = cellValue.Substring(preChars.Length, cellValue.Length - preChars.Length - postChars.Length);
                            int delimiterPos = barcodeData.IndexOf(":");
                            if (delimiterPos > 0)
                            {
                                string barcodeType = barcodeData.Substring(0, delimiterPos);
                                string barcodeValue = barcodeData.Substring(delimiterPos + 1);

                                try
                                {

                                    BarcodeStandard.Barcode barcode = new BarcodeStandard.Barcode();
                                    barcode.Height = imageHeight - marginHeight * 2;
                                    barcode.Width = imageWidth - marginWidth * 2;
                                    barcode.Alignment = BarcodeStandard.AlignmentPositions.Center;
                                    barcode.IncludeLabel = enabledLabel;
                                    barcode.LabelFont = new SKFont(SKTypeface.Default, 8);
                                    barcode.BackColor = SKColors.White;
                                    barcode.ImageFormat = SKEncodedImageFormat.Bmp;
                                    barcode.Encode(DecodeBarcodeStyle(barcodeType), barcodeValue);

                                    MemoryStream tempStream1 = new MemoryStream();
                                    barcode.SaveImage(tempStream1, SaveTypes.Png);

                                    tempStream1.Position = 0;
                                    MagickImage image = new MagickImage(tempStream1);
                                    image.MatteColor = MagickColors.White;
                                    image.Frame(marginWidth, marginHeight, 0, 0);
                                    image.Transparent(MagickColors.White);
                                    MemoryStream tempStream2 = new MemoryStream();
                                    image.Write(tempStream2, MagickFormat.Png);

                                    var picture = sheet.AddPicture(tempStream2);
                                    picture.MoveTo(cell);
                                    picture.Scale(0.5, true);
                                    picture.Height = (int)(cell.WorksheetRow().Height / 0.75);
                                    picture.Width = (int)(cell.WorksheetColumn().Width / 0.118);

                                    if (enabledRemoveTag)
                                    {
                                        cell.SetValue("");
                                    }
                                }
                                catch (Exception exp)
                                {
                                    Console.Error.WriteLine("{0}", exp.Message);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Decode barcode style name to enum value
        /// </summary>
        /// <param name="barcodeStyle"></param>
        /// <returns></returns>
        public BarcodeStandard.Type DecodeBarcodeStyle(string barcodeStyle)
        {
            BarcodeStandard.Type barcodeType = BarcodeStandard.Type.Code128;
            switch (barcodeStyle.ToUpper())
            {
                case "UNSPECIFIED":
                    barcodeType = BarcodeStandard.Type.Unspecified;
                    break;

                case "UPCA":
                    barcodeType = BarcodeStandard.Type.UpcA;
                    break;

                case "UPCE":
                    barcodeType = BarcodeStandard.Type.UpcE;
                    break;

                case "UPC_SUPPLEMENTAL_2DIGIT":
                    barcodeType = BarcodeStandard.Type.UpcSupplemental2Digit;
                    break;

                case "UPC_SUPPLEMENTAL_5DIGIT":
                    barcodeType = BarcodeStandard.Type.UpcSupplemental5Digit;
                    break;

                case "EAN13":
                    barcodeType = BarcodeStandard.Type.Ean13;
                    break;

                case "EAN8":
                    barcodeType = BarcodeStandard.Type.Ean8;
                    break;

                case "Interleaved2of5":
                    barcodeType = BarcodeStandard.Type.Interleaved2Of5;
                    break;

                case "Interleaved2of5_Mod10":
                    barcodeType = BarcodeStandard.Type.Interleaved2Of5Mod10;
                    break;

                case "Standard2of5":
                    barcodeType = BarcodeStandard.Type.Standard2Of5;
                    break;

                case "Standard2of5_Mod10":
                    barcodeType = BarcodeStandard.Type.Standard2Of5Mod10;
                    break;

                case "Industrial2of5":
                    barcodeType = BarcodeStandard.Type.Industrial2Of5;
                    break;

                case "Industrial2of5_Mod10":
                    barcodeType = BarcodeStandard.Type.Industrial2Of5Mod10;
                    break;

                case "CODE39":
                    barcodeType = BarcodeStandard.Type.Code39;
                    break;

                case "CODE39Extended":
                    barcodeType = BarcodeStandard.Type.Code39Extended;
                    break;

                case "CODE39_Mod43":
                    barcodeType = BarcodeStandard.Type.Code39Mod43;
                    break;

                case "Codabar":
                    barcodeType = BarcodeStandard.Type.Codabar;
                    break;

                case "PostNet":
                    barcodeType = BarcodeStandard.Type.PostNet;
                    break;

                case "BOOKLAND":
                    barcodeType = BarcodeStandard.Type.Bookland;
                    break;

                case "ISBN":
                    barcodeType = BarcodeStandard.Type.Isbn;
                    break;

                case "JAN13":
                    barcodeType = BarcodeStandard.Type.Jan13;
                    break;

                case "MSI_Mod10":
                    barcodeType = BarcodeStandard.Type.MsiMod10;
                    break;

                case "MSI_2Mod10":
                    barcodeType = BarcodeStandard.Type.Msi2Mod10;
                    break;

                case "MSI_Mod11":
                    barcodeType = BarcodeStandard.Type.MsiMod11;
                    break;

                case "MSI_Mod11_Mod10":
                    barcodeType = BarcodeStandard.Type.MsiMod11Mod10;
                    break;

                case "Modified_Plessey":
                    barcodeType = BarcodeStandard.Type.ModifiedPlessey;
                    break;

                case "CODE11":
                    barcodeType = BarcodeStandard.Type.Code11;
                    break;

                case "USD8":
                    barcodeType = BarcodeStandard.Type.Usd8;
                    break;

                case "UCC12":
                    barcodeType = BarcodeStandard.Type.Ucc12;
                    break;

                case "UCC13":
                    barcodeType = BarcodeStandard.Type.Ucc13;
                    break;

                case "LOGMARS":
                    barcodeType = BarcodeStandard.Type.Logmars;
                    break;

                case "CODE128":
                    barcodeType = BarcodeStandard.Type.Code128;
                    break;

                case "CODE128A":
                    barcodeType = BarcodeStandard.Type.Code128A;
                    break;

                case "CODE128B":
                    barcodeType = BarcodeStandard.Type.Code128B;
                    break;

                case "CODE128C":
                    barcodeType = BarcodeStandard.Type.Code128C;
                    break;

                case "ITF14":
                    barcodeType = BarcodeStandard.Type.Itf14;
                    break;

                case "CODE93":
                    barcodeType = BarcodeStandard.Type.Code93;
                    break;

                case "TELEPEN":
                    barcodeType = BarcodeStandard.Type.Telepen;
                    break;

                case "FIM":
                    barcodeType = BarcodeStandard.Type.Fim;
                    break;

                case "PHARMACODE":
                    barcodeType = BarcodeStandard.Type.Pharmacode;
                    break;

            }

            return barcodeType;
        }
    }
}
