using Adaptation.Shared;
using Adaptation.Shared.Metrology;
using Adaptation.Shared.Properties;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;

namespace Adaptation.FileHandlers.OpenInsightMetrologyViewer;

public class WSRequest
{

    public int Id { get; set; }
    public string AreaCountAvg { get; set; }
    public string AreaCountMax { get; set; }
    public string AreaCountMin { get; set; }
    public string AreaCountStdDev { get; set; }
    public string AreaTotalAvg { get; set; }
    public string AreaTotalMax { get; set; }
    public string AreaTotalMin { get; set; }
    public string AreaTotalStdDev { get; set; }
    public string Date { get; set; }
    public string HazeAverageAvg { get; set; }
    public string HazeAverageMax { get; set; }
    public string HazeAverageMin { get; set; }
    public string HazeAverageStdDev { get; set; }
    public string HazeRegionAvg { get; set; }
    public string HazeRegionMax { get; set; }
    public string HazeRegionMin { get; set; }
    public string HazeRegionStdDev { get; set; }
    public string Layer { get; set; }
    public string LotID { get; set; }
    public string LPDCM2Avg { get; set; }
    public string LPDCM2Max { get; set; }
    public string LPDCM2Min { get; set; }
    public string LPDCM2StdDev { get; set; }
    public string LPDCountAvg { get; set; }
    public string LPDCountMax { get; set; }
    public string LPDCountMin { get; set; }
    public string LPDCountStdDev { get; set; }
    public string Operator { get; set; }
    public string ParseErrorText { get; set; }
    public string PSN { get; set; }
    public string RDS { get; set; }
    public string Reactor { get; set; }
    public string Recipe { get; set; }
    public string ScratchCountAvg { get; set; }
    public string ScratchCountMax { get; set; }
    public string ScratchCountMin { get; set; }
    public string ScratchCountStdDev { get; set; }
    public string ScratchTotalAvg { get; set; }
    public string ScratchTotalMax { get; set; }
    public string ScratchTotalMin { get; set; }
    public string ScratchTotalStdDev { get; set; }
    public string SumOfDefectsAvg { get; set; }
    public string SumOfDefectsMax { get; set; }
    public string SumOfDefectsMin { get; set; }
    public string SumOfDefectsStdDev { get; set; }
    public string Title { get; set; }
    public string UniqueId { get; set; }
    public string Zone { get; set; }

    public string CellName { get; set; }
    public string Data { get; set; }
#pragma warning disable IDE1006
    public int i { get; set; }
    public List<pcl.Detail> Details { get; protected set; }

    [Obsolete("For json")] public WSRequest() { }

#pragma warning disable IDE0060
    internal WSRequest(IFileRead fileRead, Logistics logistics, List<pcl.Description> descriptions, string processDataStandardFormat = null)
#pragma warning restore IDE0060
    {
        i = -1;
        Id = -1;
        Zone = null;
        Layer = null;
        Title = null;
        Data = "*Data*";
        Details = new List<pcl.Detail>();
        CellName = logistics.MesEntity;
        pcl.Description x = descriptions[0];
        //Header
        {
            AreaCountAvg = x.AreaCountAvg;
            AreaCountMax = x.AreaCountMax;
            AreaCountMin = x.AreaCountMin;
            AreaCountStdDev = x.AreaCountStdDev;
            AreaTotalAvg = x.AreaTotalAvg;
            AreaTotalMax = x.AreaTotalMax;
            AreaTotalMin = x.AreaTotalMin;
            AreaTotalStdDev = x.AreaTotalStdDev;
            Date = x.Date;
            HazeAverageAvg = x.HazeAverageAvg;
            HazeAverageMax = x.HazeAverageMax;
            HazeAverageMin = x.HazeAverageMin;
            HazeAverageStdDev = x.HazeAverageStdDev;
            HazeRegionAvg = x.HazeRegionAvg;
            HazeRegionMax = x.HazeRegionMax;
            HazeRegionMin = x.HazeRegionMin;
            HazeRegionStdDev = x.HazeRegionStdDev;
            LotID = x.Lot;
            LPDCM2Avg = x.LPDCM2Avg;
            LPDCM2Max = x.LPDCM2Max;
            LPDCM2Min = x.LPDCM2Min;
            LPDCM2StdDev = x.LPDCM2StdDev;
            LPDCountAvg = x.LPDCountAvg;
            LPDCountMax = x.LPDCountMax;
            LPDCountMin = x.LPDCountMin;
            LPDCountStdDev = x.LPDCountStdDev;
            ParseErrorText = x.ParseErrorText;
            PSN = x.PSN;
            RDS = x.RDS;
            Reactor = x.Reactor;
            Recipe = x.Recipe;
            Operator = x.Employee;
            ScratchCountAvg = x.ScratchCountAvg;
            ScratchCountMax = x.ScratchCountMax;
            ScratchCountMin = x.ScratchCountMin;
            ScratchCountStdDev = x.ScratchCountStdDev;
            ScratchTotalAvg = x.ScratchTotalAvg;
            ScratchTotalMax = x.ScratchTotalMax;
            ScratchTotalMin = x.ScratchTotalMin;
            ScratchTotalStdDev = x.ScratchTotalStdDev;
            SumOfDefectsAvg = x.SumOfDefectsAvg;
            SumOfDefectsMax = x.SumOfDefectsMax;
            SumOfDefectsMin = x.SumOfDefectsMin;
            SumOfDefectsStdDev = x.SumOfDefectsStdDev;
            UniqueId = x.UniqueId;
        }
        pcl.Detail detail;
        foreach (pcl.Description description in descriptions)
        {
            detail = new pcl.Detail
            {
                Data = "*Data*",
                i = -1,
                Id = 0, //item.Id,
                AreaCount = description.AreaCount,
                AreaTotal = description.AreaTotal,
                Bin1 = description.Bin1,
                Bin2 = description.Bin2,
                Bin3 = description.Bin3,
                Bin4 = description.Bin4,
                Bin5 = description.Bin5,
                Bin6 = description.Bin6,
                Bin7 = description.Bin7,
                Bin8 = description.Bin8,
                Comments = description.Comments,
                Date = description.Date,
                Diameter = description.Diameter,
                Exclusion = description.Exclusion,
                Gain = description.Gain,
                HazeAverage = description.HazeAverage,
                HazePeak = description.HazePeak,
                HazeRegion = description.HazeRegion,
                HazeRng = description.HazeRng,
                HeaderUniqueId = description.HeaderUniqueId,
                LPDCM2 = description.LPDCM2,
                LPDCount = description.LPDCount,
                Laser = description.Laser,
                Mean = description.Mean,
                Recipe = description.Recipe,
                ScratchCount = description.ScratchCount,
                ScratchTotal = description.ScratchTotal,
                Slot = description.Slot,
                Sort = description.Sort,
                StdDev = description.StdDev,
                SumOfDefects = description.SumOfDefects,
                Thresh = description.Thresh,
                Thruput = description.Thruput,
                Title = null,
                UniqueId = description.UniqueId
            };
            Details.Add(detail);
        }
        Date = logistics.DateTimeFromSequence.ToString();
        if (UniqueId is null && Details.Count != 0)
            UniqueId = Details[0].HeaderUniqueId;
        for (int i = 0; i < Details.Count; i++)
        {
            if (string.IsNullOrEmpty(Details[i].Bin1))
                Details[i].Bin1 = null;
            if (string.IsNullOrEmpty(Details[i].Bin2))
                Details[i].Bin2 = null;
            if (string.IsNullOrEmpty(Details[i].Bin3))
                Details[i].Bin3 = null;
            if (string.IsNullOrEmpty(Details[i].Bin4))
                Details[i].Bin4 = null;
            if (string.IsNullOrEmpty(Details[i].Bin5))
                Details[i].Bin5 = null;
            if (string.IsNullOrEmpty(Details[i].Bin6))
                Details[i].Bin6 = null;
            if (string.IsNullOrEmpty(Details[i].Bin7))
                Details[i].Bin7 = null;
            if (string.IsNullOrEmpty(Details[i].Bin8))
                Details[i].Bin8 = null;
        }
    }

    private static void UpdateDataPDF(List<pcl.Description> descriptions, string checkFileName)
    {
        string value;
        string fullName;
        object possiblePage;
        object possibleString;
        object possibleCOSArray;
        java.util.List tokenList;
        java.util.List arrayList;
        java.io.OutputStream outputStream;
        List<string> updateValues = new();
        StringBuilder stringBuilder = new();
        java.util.ListIterator tokenIterator;
        java.util.ListIterator arrayIterator;
        java.io.File file = new(checkFileName);
        string reactorLoadLock = descriptions[0].Comments;
        org.apache.pdfbox.pdmodel.common.PDStream pdStream;
        org.apache.pdfbox.pdmodel.common.PDStream updatedStream;
        org.apache.pdfbox.pdfparser.PDFStreamParser pdfStreamParser;
        org.apache.pdfbox.pdfwriter.ContentStreamWriter contentStreamWriter;
        org.apache.pdfbox.pdmodel.PDDocument pdDocument = org.apache.pdfbox.pdmodel.PDDocument.load(file);
        org.apache.pdfbox.pdmodel.PDDocumentCatalog pdDocumentCatalog = pdDocument.getDocumentCatalog();
        java.util.List pagesList = pdDocumentCatalog.getAllPages();
        java.util.ListIterator pageIterator = pagesList.listIterator();
        if (string.IsNullOrEmpty(descriptions[0].Lot) || string.IsNullOrEmpty(descriptions[0].Reactor) || descriptions[0].Lot.StartsWith(descriptions[0].Reactor))
            fullName = string.Empty;
        else
            fullName = $"{descriptions[0].Reactor}-{descriptions[0].RDS}-{descriptions[0].PSN} ";
        for (short i = 1; i < short.MaxValue; i++)
        {
            if (!pageIterator.hasNext())
                break;
            possiblePage = pageIterator.next();
            if (possiblePage is not org.apache.pdfbox.pdmodel.PDPage page)
                continue;
            pdStream = page.getContents();
            pdfStreamParser = new org.apache.pdfbox.pdfparser.PDFStreamParser(pdStream);
            pdfStreamParser.parse();
            tokenList = pdfStreamParser.getTokens();
            tokenIterator = tokenList.listIterator();
            for (short t = 1; i < short.MaxValue; t++)
            {
                if (!tokenIterator.hasNext())
                    break;
                possibleCOSArray = tokenIterator.next();
                if (possibleCOSArray is not org.apache.pdfbox.cos.COSArray cossArray)
                    continue;
                _ = stringBuilder.Clear();
                arrayList = cossArray.toList();
                arrayIterator = arrayList.listIterator();
                for (short a = 1; i < short.MaxValue; a++)
                {
                    if (!arrayIterator.hasNext())
                        break;
                    possibleString = arrayIterator.next();
                    if (possibleString is not org.apache.pdfbox.cos.COSString cossString)
                        continue;
                    value = cossString.getString();
                    _ = stringBuilder.Append(value);
                    if (value != "]")
                        continue;
                    updateValues.Add(value);
                    value = stringBuilder.ToString();
                    if (value.Contains("[]"))
                        cossArray.setString(a - 1, string.Concat("*", fullName, reactorLoadLock, "]"));
                    else
                        cossArray.setString(a - 1, string.Concat(" {*", fullName, reactorLoadLock, "}]"));
                }
            }
            if (updateValues.Count != 0)
            {
                updatedStream = new org.apache.pdfbox.pdmodel.common.PDStream(pdDocument);
                outputStream = updatedStream.createOutputStream();
                contentStreamWriter = new org.apache.pdfbox.pdfwriter.ContentStreamWriter(outputStream);
                contentStreamWriter.writeTokens(tokenList);
                outputStream.close();
                page.setContents(updatedStream);
            }
        }
        if (updateValues.Count != 0)
            pdDocument.save(checkFileName);
        pdDocument.close();
    }

    internal static long GetHeaderId(IFileRead fileRead, Logistics logistics, string openInsightMetrologyViewerAPI, DateTime dateTime, int weekOfYear, string json, List<pcl.Description> descriptions)
    {
        long result;
        if (string.IsNullOrEmpty(json))
        {
            WSRequest wsRequest = new(fileRead, logistics, descriptions);
            string directory = Path.Combine(openInsightMetrologyViewerAPI, dateTime.Year.ToString(), $"WW{weekOfYear:00}");
            (json, WS.Results wsResults) = WS.SendData(openInsightMetrologyViewerAPI, logistics.Sequence, directory, wsRequest);
            if (!wsResults.Success)
                throw new Exception(wsResults.ToString());
        }
        WS.Results metrologyWSRequest = JsonSerializer.Deserialize<WS.Results>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        result = metrologyWSRequest.HeaderID;
        return result;
    }

#pragma warning disable IDE0060
    internal static void PostOpenInsightMetrologyViewerAttachments(IFileRead fileRead, Logistics logistics, string openInsightMetrologyViewerAPI, List<pcl.Description> descriptions, string matchDirectory, string subGroupId, long headerId, string headerIdDirectory)
#pragma warning restore IDE0060
    {
        string checkFileName;
        string[] pclFiles = Directory.GetFiles(matchDirectory, "*.pcl", SearchOption.TopDirectoryOnly);
        if (pclFiles.Length != 1)
            throw new Exception($"Invalid source file count for <{headerId}>!");
        string sourceFileNameNoExt = Path.GetFileNameWithoutExtension(pclFiles[0]);
        List<WS.Attachment> dataAttachments = new();
        List<WS.Attachment> headerAttachments = new();
        checkFileName = Path.Combine(matchDirectory, $"{sourceFileNameNoExt}_data.pdf");
        if (!File.Exists(checkFileName))
            throw new Exception("Header file doesn't exist!");
        else
        {
            UpdateDataPDF(descriptions, checkFileName);
            headerAttachments.Add(new WS.Attachment(subGroupId, headerId, headerIdDirectory, descriptions[0].HeaderUniqueId, "Data.pdf", checkFileName));
        }
        foreach (pcl.Description description in descriptions)
        {
            checkFileName = Path.Combine(matchDirectory, $"{sourceFileNameNoExt}_{description.Slot.Replace('*', 's')}_image.pdf");
            if (File.Exists(checkFileName))
                dataAttachments.Add(new WS.Attachment(subGroupId, headerId, headerIdDirectory, description.UniqueId, "Image.pdf", checkFileName));
            else
            {
                checkFileName = Path.Combine(matchDirectory, $"{sourceFileNameNoExt}_{description.Slot.Replace('*', 's')}_data.pdf");
                if (File.Exists(checkFileName))
                    dataAttachments.Add(new WS.Attachment(subGroupId, headerId, headerIdDirectory, description.UniqueId, "Image.pdf", checkFileName));
            }
        }
        if (dataAttachments.Count == 0 || dataAttachments.Count != descriptions.Count)
            throw new Exception($"Invalid attachment count! {dataAttachments.Count} != {descriptions.Count}");
        WS.AttachFiles(openInsightMetrologyViewerAPI, headerAttachments, dataAttachments);
    }

}