using System.Drawing;
using System.Drawing.Imaging;
using IWS5.Server.Enums;

namespace IWS5.Server.Utils.Merger;

public class TiffMerger : IFileMerger
{
    public bool CanMerge(string filesExtension) =>
        string.Equals(filesExtension, nameof(ConvertibleFileTypes.tiff), StringComparison.OrdinalIgnoreCase);
   

    /// <summary>
    /// Merges multiple TIFF files from byte arrays into a single multi-page TIFF file
    /// </summary>
    /// <param name="tiffFiles">Array of byte arrays representing TIFF files to merge</param>
    /// <returns>Byte array of the merged TIFF file</returns>
    /// <exception cref="ArgumentNullException">Thrown when tiffFiles is null</exception>
    /// <exception cref="ArgumentException">Thrown when tiffFiles array is empty</exception>
    public byte[] Merge(params byte[][] tiffFiles)
    {
        if (tiffFiles == null)
            throw new ArgumentNullException(nameof(tiffFiles), "TIFF files array cannot be null");
        
        if (tiffFiles.Length == 0)
            throw new ArgumentException("TIFF files array cannot be empty", nameof(tiffFiles));

        var frames = new List<Image>();
        
        try
        {
            // Load all frames from all TIFF files
            foreach (var tiffBytes in tiffFiles)
            {
                if (tiffBytes == null || tiffBytes.Length == 0)
                    continue;

                using var sourceStream = new MemoryStream(tiffBytes);
                using var sourceTiff = Image.FromStream(sourceStream);
                
                // Get frame count for multi-page TIFF
                int frameCount = sourceTiff.GetFrameCount(FrameDimension.Page);
                
                for (int i = 0; i < frameCount; i++)
                {
                    sourceTiff.SelectActiveFrame(FrameDimension.Page, i);
                    // Create a copy of the frame to avoid disposal issues
                    frames.Add(new Bitmap(sourceTiff));
                }
            }

            if (frames.Count == 0)
                throw new InvalidOperationException("No valid TIFF files found to merge");

            // Create the merged TIFF
            using var outputStream = new MemoryStream();
            
            // Use the first frame to initialize the encoder parameters
            var firstFrame = frames[0];
            var encoder = GetEncoder(ImageFormat.Tiff);
            var encoderParams = new EncoderParameters(2);
            encoderParams.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
            encoderParams.Param[1] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW);
            
            // Save the first frame
            firstFrame.Save(outputStream, encoder, encoderParams);
            
            // Add subsequent frames
            for (int i = 1; i < frames.Count; i++)
            {
                encoderParams.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
                firstFrame.SaveAdd(frames[i], encoderParams);
            }
            
            // Finalize the multi-frame TIFF
            encoderParams.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
            firstFrame.SaveAdd(encoderParams);
            
            return outputStream.ToArray();
        }
        finally
        {
            // Clean up all created frame copies
            foreach (var frame in frames)
            {
                frame?.Dispose();
            }
        }
    }
    
    private static ImageCodecInfo GetEncoder(ImageFormat format)
    {
        var codecs = ImageCodecInfo.GetImageDecoders();
        foreach (var codec in codecs)
        {
            if (codec.FormatID == format.Guid)
                return codec;
        }
        throw new ArgumentException($"No encoder found for format: {format}");
    }
}