using System.CodeDom;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Hawkeye.VisionBuilder.Workflow;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
using Hawkeye.VisionBuilder.Workflow.Operations;
using OpenCvSharp;
using Serilog;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Processing;
using VisionBuilder.UI.Common.ViewModel.Classes;

namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
{
    public class HawkeyeRecognitionControl: BaseRecognitionControl
    {
        private readonly IImageSource _imageSource;
        private readonly HawkeyeRecognitionSettings _recognitionConfiguration;


        private CancellationTokenSource _cts;
        private Task _loopTask;

        private FileSystemWatcher? _fileWatcher;
        private System.Timers.Timer? _debounceTimer;
        private string? _pendingHotReloadFile;
        private string? _currentRecipeFilePath;

        public HawkeyeRecognitionControl(IImageSource imageSource, HawkeyeRecognitionSettings recognitionConfiguration, ILoadingService loadingService): base(recognitionConfiguration,loadingService)
        {
            _imageSource = imageSource;
            _recognitionConfiguration = recognitionConfiguration;
            
        }

        public override List<RecipeData> GetRecipesData()
        {
            LoadingService.StartLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
            try
            {
                var files = WorkflowRecipeHelper.ListRecipeFiles(_recognitionConfiguration.RecipeNameFilter);
                List<RecipeData> recipes = new List<RecipeData>();
                foreach (var file in files)
                {
                    var workFlow = WorkflowList.LoadJSONFromFile(file);

                    var recipe = new RecipeData
                    {
                        RecipeName = Path.GetFileNameWithoutExtension(file),
                        Image = workFlow.RecipeImage
                    };
                    recipes.Add(recipe);
                }

                return recipes;
            }
            finally
            {
                LoadingService.StopLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
            }
        }

        private WorkflowList _workflow;

      

        protected override void Initialize(RecipeData currentRecipe)
        {
            var path = Path.GetFullPath(Path.Combine("..", "Data", "Recipes", currentRecipe.RecipeName + ".jhrcp"));
            _currentRecipeFilePath = path;
            _workflow = WorkflowList.LoadJSONFromFile(path);
            StartFileWatcher(_currentRecipeFilePath);
        }

        protected override void WarmUp()
        {
            _workflow.ImageSource = new EmptyImageSource();
            _workflow.Context = new Context();
            _workflow.Context.CancellationToken = CancellationToken.None;
            _workflow.Execute(); 
        }

        protected override (Mat originalImage, Mat analysisImage,TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)? ProcessImage(CancellationToken token)
        {
            var pendingFile = Interlocked.Exchange(ref _pendingHotReloadFile, null);
            if (pendingFile != null)
                _workflow.HotReloadParameters(pendingFile);

            var sw = Stopwatch.StartNew();
            _workflow.Context.CancellationToken = token;
            _workflow.ImageSource = _imageSource;
            try
            {
                _workflow.Execute();
            }
            catch (Exception e) when (token.IsCancellationRequested)
            {
                sw.Stop();
                return null;
            }

            sw.Stop();

            var annotated = _workflow.Context.ActiveImage.ImageData.Clone();
            var canvas = new OpenCVCanvas(annotated);
            _workflow.Context.GraphicsElements.ForEach(x => x.Draw(canvas));

            var originalImage = _workflow.Context.LastCameraImage.ImageData;
            var analysisImage = annotated;

            var cameraOperation = _workflow.Operations.FirstOrDefault(x => x is GetImageOperation);
            TimeSpan cameraTime = TimeSpan.FromMilliseconds(0);
            if (cameraOperation != null)
            {
                cameraTime = cameraOperation.ExecutionTime;
            }

            string[] errorNames = _workflow.Operations.Where(x => !x.Result).Select(x => x.Label).ToArray();
            if (_recognitionConfiguration.AlwaysError)
            {
                errorNames = errorNames.Concat(new[] { "Forced error" }).ToArray();
            }
            return (originalImage, analysisImage, sw.Elapsed, cameraTime, errorNames);
        }

        private void StartFileWatcher(string filePath)
        {
            StopFileWatcher();

            var directory = Path.GetDirectoryName(filePath)!;
            var fileName = Path.GetFileName(filePath);

            _debounceTimer = new System.Timers.Timer(500) { AutoReset = false };
            _debounceTimer.Elapsed += (_, _) => RaiseRecipeFileChanged();

            _fileWatcher = new FileSystemWatcher(directory, fileName)
            {
                NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
                EnableRaisingEvents = true
            };
            _fileWatcher.Changed += (_, _) =>
            {
                _debounceTimer.Stop();
                _debounceTimer.Start();
            };
        }

        private void StopFileWatcher()
        {
            if (_fileWatcher != null)
            {
                _fileWatcher.EnableRaisingEvents = false;
                _fileWatcher.Dispose();
                _fileWatcher = null;
            }
            if (_debounceTimer != null)
            {
                _debounceTimer.Stop();
                _debounceTimer.Dispose();
                _debounceTimer = null;
            }
        }

        public override void HotReload()
        {
            Interlocked.Exchange(ref _pendingHotReloadFile, _currentRecipeFilePath);
        }

        protected override void Cleanup()
        {
            StopFileWatcher();
        }
    }
}
