using Compunet.YoloV8;
using Hawkeye.VisionBuilder.Features.ColorProgileGenerator;
using Hawkeye.VisionBuilder.Features.ImagePreview;
using Hawkeye.VisionBuilder.Features.ImagesStrip;
using Hawkeye.VisionBuilder.Features.ToolEditor;
using Hawkeye.VisionBuilder.Features.VisionSteps;
using Hawkeye.VisionBuilder.Features.Yolo;
using Hawkeye.VisionBuilder.Panels;
using Hawkeye.VisionBuilder.Workflow;
using Hawkeye.VisionBuilder.Workflow.ColorStudioClasses;
using Hawkeye.VisionBuilder.Workflow.Configuration;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
using Hawkeye.VisionBuilder.Workflow.Operations.AI;
using Hawkeye.VisionBuilder.Workflow.Operations.Filters;
using Hawkeye.VisionBuilder.Workflow.Operations.Memory;

using Inspectron.Settings.WindowsWizard;
using Inspectron.Settings.WindowsWizard.Controls;
using Microsoft.WindowsAPICodePack.Dialogs;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.Windows.Forms;
using Hawkeye.VisionBuilder.Features.ImageStatistics;
using Size = OpenCvSharp.Size;

namespace Hawkeye.VisionBuilder
{
    public partial class MainWindow : Form
    {
        private readonly WorkflowList _workflowList;
        private readonly OperationDiscoveryService _discoveryService;
        private readonly ToolConfigurationPanel _toolConfigurationPanel;
        private readonly ImagePreviewPanel _previewPanel;
        private readonly VisionStepsPanel _visionStepsPanel;
        private ImageStripPanel _imagesStripPanel;
        public MainWindow(WorkflowList workflowList, OperationDiscoveryService discoveryService)
        {
            _workflowList = workflowList;
            _discoveryService = discoveryService;
            InitializeComponent();


            {
                _previewPanel = new ImagePreviewPanel();
                _previewPanel.MdiParent = this;
                _previewPanel.Show();
            }
            {
                _visionStepsPanel = new VisionStepsPanel(workflowList, discoveryService, _previewPanel);
                _visionStepsPanel.MdiParent = this;
                _visionStepsPanel.Show();
            }
            {
                _toolConfigurationPanel = new ToolConfigurationPanel(workflowList, _visionStepsPanel);
                _toolConfigurationPanel.MdiParent = this;
                _toolConfigurationPanel.Show();
            }
            {
                ReinitCameraPanel(workflowList);
            }
            _visionStepsPanel.OperationSelected += operation =>
            {
                _toolConfigurationPanel.SetOperation(operation);

            };

            //var codeEditor = new ScriptEditorPanel();
            //codeEditor.MdiParent = this;
            //codeEditor.Show();


        }

        private void ReinitCameraPanel(WorkflowList workflowList)
        {
            if (_imagesStripPanel != null)
            {
                _imagesStripPanel.Close();
                _imagesStripPanel.Dispose();
                _imagesStripPanel = null;
            }




            _imagesStripPanel = new ImageStripPanel();

            _workflowList.ImageSource= _imagesStripPanel;

            _imagesStripPanel.ImageSelected += data =>
            {
                if (_imagesStripPanel is ImageStripPanel imagesStripPanel)
                {
                    if (imagesStripPanel.IsExecutingWorkflow)
                    {
                        _visionStepsPanel.ExecuteWorkflow();
                        var result = _visionStepsPanel.WorkflowList.Operations.All(x => x.Result == true);
                        ProcessResult(result);
                        (_imagesStripPanel as ImageStripPanel).InformImageProcessed(result);
                        return;
                    }
                }

                _visionStepsPanel.ProcessRowSelection();
                var selectedOperation = _visionStepsPanel.GetSelectedOperation();
                if (selectedOperation != null)
                {
                    ProcessResult(selectedOperation.Result);
                    (_imagesStripPanel as ImageStripPanel).InformImageProcessed(selectedOperation.Result);
                }

            };
            if(Directory.Exists(_workflowList.Configuration.EmulationPath))
                _imagesStripPanel.LoadImages(_workflowList.Configuration.EmulationPath);

            _imagesStripPanel.MdiParent = this;
            _imagesStripPanel.Show();
        }

        string _saveErrorDirectory = null;
        void ProcessResult(bool result)
        {
            if (_statisticsDialog != null)
            {
                if (_statisticsDialog.cbExpectedMode.Checked)
                {
                    _statisticsDialog.AddExpected((_imagesStripPanel as ImageStripPanel).GetImagePath(),
                        _visionStepsPanel.WorkflowList.Operations.Where(x => x.Result == false).Select(x => x.Label)
                            .ToArray());
                }
                else
                {
                    if (result)
                    {
                        _statisticsDialog.AddGoodImage((_imagesStripPanel as ImageStripPanel).GetImagePath());
                    }
                    else
                    {
                        _statisticsDialog.AddBadImage((_imagesStripPanel as ImageStripPanel).GetImagePath(), _visionStepsPanel.WorkflowList.Operations.Where(x => x.Result == false).Select(x => x.Label).ToArray());
                    }
                }
                
            }

            if (!copyOnStopToolStripMenuItem.Checked) return;
            var shouldSaveOnError = (_imagesStripPanel as ImageStripPanel).StopOnErrors;
            var shouldSaveOnNoError = (_imagesStripPanel as ImageStripPanel).StopOnNoErrors;
            if ((!result && shouldSaveOnError) || (result && shouldSaveOnNoError))
            {
                var resultFile = Path.Combine(_saveErrorDirectory, Path.GetFileName((_imagesStripPanel as ImageStripPanel).GetImagePath()));
                if (!File.Exists(resultFile))
                {
                    File.Copy((_imagesStripPanel as ImageStripPanel).GetImagePath(), Path.Combine(_saveErrorDirectory, Path.GetFileName((_imagesStripPanel as ImageStripPanel).GetImagePath())));
                }

            }


        }


        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            WindowState= FormWindowState.Maximized;
            Application.DoEvents();
            OrganizeWindows(null, null);
            _previewPanel.Fit();
        }

        private void OrganizeWindows(object sender, EventArgs e)
        {
            _previewPanel.Left = 0;
            _previewPanel.Top = 0;
            _previewPanel.Width = this.ClientSize.Width / 2;
            _previewPanel.Height = (this.ClientSize.Height - 40) - 30 - _imagesStripPanel.Height;

            _imagesStripPanel.Top = _previewPanel.Height;
            _imagesStripPanel.Left = 0;
            _imagesStripPanel.Width = _previewPanel.Width;


            _toolConfigurationPanel.Top = 0;
            _toolConfigurationPanel.Left = this.ClientSize.Width / 2;
            _toolConfigurationPanel.Width = this.ClientSize.Width / 2 - 10;
            _toolConfigurationPanel.Height = (this.ClientSize.Height - 40) / 2;

            _visionStepsPanel.Top = (this.ClientSize.Height - 40) / 2;
            _visionStepsPanel.Left = this.ClientSize.Width / 2;
            _visionStepsPanel.Width = this.ClientSize.Width / 2 - 10;
            _visionStepsPanel.Height = (this.ClientSize.Height - 40) / 2 - 30;

        }

        private void loadRecipeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Suported types|*.jhrcp;*.hrcp";
            PrepareDialog(dialog);

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var path = dialog.FileName;
                var ext = Path.GetExtension(path);
                if (ext == ".jhrcp")
                {
                    LoadRecipeJSON(path);
                }
                else if (ext == ".hrcp")
                {
                    LoadRecipeBinary(path);
                }
                else
                {
                    MessageBox.Show("Unsupported file format. Please use .hrcp or .jhrcp files.");
                    return;
                }
            }

        }

        private void PrepareDialog(FileDialog dialog)
        {
            if (_loadedPath != null)
            {
                dialog.InitialDirectory = Path.GetDirectoryName(Path.GetFullPath(_loadedPath));
                dialog.FileName = Path.GetFileNameWithoutExtension(_loadedPath);
            }
            else
            {
                Directory.CreateDirectory(Path.GetFullPath(@"..\Data\Recipes"));
                dialog.InitialDirectory = Path.GetFullPath(@"..\Data\Recipes");
            }
        }

        private string _loadedPath = null;


        public void LoadRecipeBinary(string path)
        {
            _loadedPath = path;
            var bytes = File.ReadAllBytes(path);
            MemoryStream stream = new MemoryStream(bytes);
            BinaryReader reader = new BinaryReader(stream);


            
            _workflowList.Load(reader, _discoveryService);
            ReinitCameraPanel(_workflowList);
            OrganizeWindows(null, null);
            _visionStepsPanel.RefreshWorkflow();
        }

        public void LoadRecipeJSON(string path)
        {
            _loadedPath = path;
            _workflowList.LoadJSON(path,_discoveryService);
            ReinitCameraPanel(_workflowList);
            OrganizeWindows(null, null);
            _visionStepsPanel.RefreshWorkflow();

        }

        private void _workflowList_PythonMissing()
        {
            SetupConfig();

        }

        private void saveRecipeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "*.jhrcp|*.jhrcp|*.hrcp|*.hrcp";
            PrepareDialog(dialog);
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var ext = Path.GetExtension(dialog.FileName);
                if (ext == ".jhrcp")
                {
                    _workflowList.RecipeImage = _workflowList.Context.LastCameraImage?.ImageData;
                    _workflowList.SaveJSON(dialog.FileName);
                }
                else
                {
                    SaveBinary(dialog.FileName);
                }
            }
        }

        private void SaveBinary(string path)
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);
                
            _workflowList.RecipeImage = _workflowList.Context.LastCameraImage.ImageData;
            _workflowList.Save(bw);
            ms.Close();
            File.WriteAllBytes(path, ms.ToArray());
        }

        private void toolBtnConfiguration_Click(object sender, EventArgs e)
        {
            SetupConfig();
            ReinitCameraPanel(_workflowList);
            OrganizeWindows(null, null);
        }

        private void SetupConfig()
        {
            SetupPage.Begin("Camera", "Configuration")
                .AddItem<EnumEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.RuntimeCameraType))
                .AddItem<EnumEditor>(_workflowList.Configuration,
                    nameof(_workflowList.Configuration.DevelopmentCameraType))
                .AddItem<PathEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.EmulationPath),
                    editor => editor.FolderPick = true)
                .Next("Recipe selection")
                .AddItem<CheckEditor>(_workflowList.Configuration.RecipeSelectionConfig, nameof(RecipeSelectionConfig.SerialEnabled))
                .AddItem<StringEditor>(_workflowList.Configuration.RecipeSelectionConfig, nameof(RecipeSelectionConfig.ComPort))
                .Next("Outputs")
                .AddItem<EnumEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.Outputs))
                .AddItem<NumberEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.ResultPin))
                .AddItem<StringEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.SerialPort))
                .AddItem<NumberEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.Delay), editor => editor.Max = 1000)
                .Next("Python")
                .AddItem<PathEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.PythonPath),
                    editor => editor.FolderPick = false)
                .Next("Bad tweak")
                .AddItem<CheckEditor>(_workflowList.Configuration.BadTweakLearning, nameof(BadTweakLearning.Enabled))
                .AddItem<NumberEditor>(_workflowList.Configuration.BadTweakLearning, nameof(BadTweakLearning.ThresholdLevel),
                    editor =>
                    {
                        editor.Min = 0;
                        editor.Max = 255;
                    })
                .Next("Statistics")
                .ShowSetupDialog();
        }

        private void generateFromColorProfileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var tool = new ColorProfileSelection(_workflowList);
            tool.ShowDialog();
            _visionStepsPanel.RefreshWorkflow();
        }

        private void updateAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dialog = new OpenFileDialog();
            dialog.Filter = "*.colorprofile|*.colorprofile";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var path = dialog.FileName;
                var operations = _workflowList.Operations;
                foreach (var operation in operations)
                {
                    if (operation is ColorProfileOperation colorProfileOperation)
                    {
                        colorProfileOperation.ColorProfilePath.Path = path;
                    }
                }
            }

        }

        private void copyPathToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_imagesStripPanel is not ImageStripPanel panel) return;
            var imagePath = panel.GetImagePath();
            if (imagePath == null) return;
            Clipboard.SetText(imagePath);
        }

        private void copyOnErrorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                _saveErrorDirectory = dialog.SelectedPath;
                return;
            }
            copyOnStopToolStripMenuItem.Checked = false;
        }

        private void generateFromYoloToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // pick yolo file
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "*.onnx|*.onnx";
            dialog.InitialDirectory = Path.GetFullPath(@"..\Data\Models");
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                var path = dialog.FileName;
                var predictor = YoloV8Predictor.Create(path);
                var classes = predictor.Metadata.Names;
                foreach (var cls in classes)
                {
                    {
                        var operation = new YoloPickDetected();
                        operation.Label = cls.Name;
                        operation.TypeId = cls.Id + 1;
                        _workflowList.Operations.Add(operation);
                    }
                }
                _visionStepsPanel.RefreshWorkflow();
            }
        }

        private void massSetAttributeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AttributeSetter setter = new AttributeSetter(_workflowList);
            if (setter.ShowDialog() == DialogResult.OK)
            {
                _visionStepsPanel.RefreshWorkflow();
            }
        }

        private string? _saveAsPath = null;

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_imagesStripPanel is not ImageStripPanel panel) return;

            var path = GetSaveAsPath();
            if (path == null) return;
            var imagePath = panel.GetImagePath();
            if (imagePath == null) return;
            var bytes = File.ReadAllBytes(imagePath);
            File.WriteAllBytes(Path.Combine(path, Path.GetFileName(imagePath)), bytes);
        }

        string GetSaveAsPath()
        {
            if (_saveAsPath == null)
            {
                CommonOpenFileDialog dialog = new CommonOpenFileDialog();
                dialog.IsFolderPicker = true;
                if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
                {
                    _saveAsPath = dialog.FileName;
                }
            }
            return _saveAsPath;
        }

        ImageStatisticsDialog _statisticsDialog = null;
        private void imageStatisticsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_statisticsDialog!=null)
            {
                return;
            }

            _statisticsDialog = new ImageStatisticsDialog();
            _statisticsDialog.Show(this);
            _statisticsDialog.FormClosed +=
                (s, args) =>
                {
                    _statisticsDialog = null;
                };
            _statisticsDialog.FileSelected += _statisticsDialog_FileSelected;
        }

        private void _statisticsDialog_FileSelected(string file)
        {
            (_imagesStripPanel as ImageStripPanel).SelectImage(file);
        }
    }
}