using Inspectron.Fastbuffer;
using Inspectron.Fastbuffer.Filesystems;
using Lindt.Colorballs.Duo.Utils;
using OpenCvSharp;
using Serilog;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Processing;

namespace VisionBuilder.UI.Ringbuffer;

public class VisionBuilderRingbuffer:IVisionBuilderModule
{
    private readonly VisionBuilderRingbufferSettings _settings;
    private readonly IRecognitionControl _recognitionControl;
    private readonly ILoadingService _loadingService;

    public VisionBuilderRingbuffer(VisionBuilderRingbufferSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
    {
        _settings = settings;
        _recognitionControl = recognitionControl;
        _loadingService = loadingService;
    }
    public void InitializeModule()
    {
        _recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
        _recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
        _recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
    }

    private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
    {
        _loadingService.StartLoading("Processing ringbuffer");
        Log.Information("Processing ringbuffer");
        try
        {
            CleanUp();
        }
        catch (Exception e)
        {
            
        }
        finally
        {
            Log.Information("Finished processing ringbuffer");
            _loadingService.StopLoading("Processing ringbuffer");
        }
    }

    private void _recognitionControl_ImageProcessed(VisionBuilder.UI.Common.Commands.ImageProcessedEvent obj)
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        try
        {
            if (obj.HasError)
            {
                WriteBadResult(obj);
                return;
            }
            WriteGoodResult(obj);
        }
        finally
        {
            sw.Stop();
            Console.WriteLine($"Ringbuffer: wrote image in {sw.ElapsedMilliseconds}ms");
        }
        
    }

    private void _recognitionControl_SessionStarted(VisionBuilder.UI.Common.Commands.SessionStartedEvent obj)
    {
        NewProduct(obj.RecipeName);
    }

    private IsolatedRingbuffer _goodRingbufferIsolated;
    private string _path;
    private DateTime _startTime;
    private string _product;

    private void CleanUp()
    {
        _goodRingbufferIsolated?.Dispose();
        foreach (var ringbuffer in _badRingbuffers)
        {
            ringbuffer.Value.Dispose();
        }
        _badRingbuffers.Clear();
    }

    public void Dispose()
    {
        CleanUp();
    }


    public void WriteGoodResult(ImageProcessedEvent result)
    {
        if (result.ImageOriginal == null) return;
        var filename = PathSettingsUtils.ConvertVariables(_settings.FilenameTemplate, DateTime.Now, _startTime, _product,cameraName: _settings.CameraName) + ".png";
        try
        {
            var image = result.ImageOriginal;
            _goodRingbufferIsolated.Write(image, filename);
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error writing good result at {filename}: " + e.Message);
        }
    }

    Dictionary<string, IsolatedRingbuffer> _badRingbuffers = new Dictionary<string, IsolatedRingbuffer>();

    private IsolatedRingbuffer GetBadRingbuffer(string defectName)
    {
        if (_badRingbuffers.ContainsKey(defectName))
        {
            return _badRingbuffers[defectName];
        }
        var path = PathSettingsUtils.ConvertVariables(Path.Combine(_settings.Root, _settings.BadImagesPathTemplate), _startTime, _startTime, _product, cameraName: _settings.CameraName);
        var ringbuffer = new IsolatedRingbuffer(path, _settings.MaxBad, new AsyncFilesystem());
        _badRingbuffers[defectName] = ringbuffer;
        return ringbuffer;
    }

    private void SaveResultImage(Mat image, string defectName, DateTime time, bool isAnalysis)
    {
        var filename = PathSettingsUtils.ConvertVariables(_settings.FilenameTemplate,time,_startTime,_product,defectName,_settings.CameraName) + (isAnalysis ? "_analysis" : "") + ".png";
        var ringbuffer = GetBadRingbuffer(defectName);
        try
        {
            ringbuffer.Write(image, filename);
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error writing bad result for {filename}: " + e.Message);
        }
    }

    public void WriteBadResult(ImageProcessedEvent result)
    {
        var defectName = "unknown";

        if (result.ErrorNames.Any())
        {
            defectName = result.ErrorNames.First();
        }

        var time = DateTime.Now;
        SaveResultImage(result.ImageOriginal, defectName, time, false);
        SaveResultImage(result.ImageAnalysis, defectName, time, true);
    }

    public void NewProduct(string product)
    {
        _startTime = DateTime.Now;
        _product = product;
        _path = PathSettingsUtils.ConvertVariables(Path.Combine(_settings.Root,_settings.GoodImagesPathTemplate), _startTime, _startTime, _product,cameraName:_settings.CameraName);
        
        _goodRingbufferIsolated = new IsolatedRingbuffer(_path, _settings.MaxGood, new AsyncFilesystem());

        

    }

    
}