using System.Reflection;
using Hawkeye.VisionBuilder.Features.ImagePreview;
using Hawkeye.VisionBuilder.Features.VisionSteps;
using Hawkeye.VisionBuilder.Panels;
using Hawkeye.VisionBuilder.Workflow;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
using Hawkeye.VisionBuilder.Workflow.Links;
using Hawkeye.VisionBuilder.Workflow.Operations.Attributes;
using Hawkeye.VisionBuilder.Workflow.Operations.Filters;
using Rectangle = Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Rectangle.RectangleElement;

namespace Hawkeye.VisionBuilder.Features.ToolEditor
{
    public partial class ToolConfigurationPanel : BasePannel
    {
        private readonly WorkflowList _workflowList;
        private readonly VisionStepsPanel _visionStepsPanel;
        private BaseOperation _selectedOperation;
        private Dictionary<string, object> _parameters;

        public ToolConfigurationPanel(WorkflowList workflowList,VisionStepsPanel visionStepsPanel)
        {
            _workflowList = workflowList;
            _visionStepsPanel = visionStepsPanel;
            InitializeComponent();
        }


        public void SetOperation(BaseOperation operation)
        {
            _selectedOperation=operation;
            panel2.Enabled=false;

            //todo: generate header
            
            var parameters = operation.GetParameters();
            _parameters=parameters;
            this.flowLayoutPanel1.Controls.Clear();
            foreach (var p in parameters)
            {
                GenerateParameterField(parameters,p.Key, operation);
            }
            
        }

        private void GenerateParameterField(Dictionary<string, object> toolParameter, string pKey,
            BaseOperation operation)
        {
        
            
            var value = toolParameter[pKey];
            if (value is int i)
            {
                var control = new NumericUpDown() { Minimum = -99999999, Maximum = 99999999, Value = i, };
                control.TextChanged += (sender, args) =>
                {
                    
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                control.ValueChanged += (sender, args) =>
                {
                    
                    toolParameter[pKey] = ((int) control.Value);
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
            }else if (value is double d)
            {
                var control = new NumericUpDown() { Minimum = -99999999, Maximum = 99999999, Value = (decimal)d, Increment = (decimal)0.1, DecimalPlaces = 2};
                control.TextChanged += (sender, args) =>
                {
                    
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                control.ValueChanged += (sender, args) =>
                {

                    toolParameter[pKey] = ((double) control.Value);
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
            }else if (value is string s)
            {
                var control = new TextBox() { Text = s };
                control.TextChanged += (sender, args) =>
                {
                    toolParameter[pKey] = (control.Text);
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
            }
            else if (value is FilePath filePath)
            {
                
                var control = new Button()
                {
                    Text = filePath.Path,
                    
                };
                control.Click += (sender, args) =>
                {
                    OpenFileDialog dialog = new OpenFileDialog();
                    dialog.Filter = filePath.Format;
                    dialog.FileName = filePath.Path;
                    if (dialog.ShowDialog() == DialogResult.OK)
                    {
                        control.Text = dialog.FileName;
                        filePath.Path = MakeRelative(dialog.FileName,Environment.CurrentDirectory);
                        control.Text = filePath.Path;
                        panel2.Enabled = true;
                        TriggerChanged();
                    }
                };
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);

            }
            else if (value is SelectFromList selectFromList)
            {
                var control = new ComboBox()
                {
                    DropDownStyle = ComboBoxStyle.DropDownList
                };
                control.Items.AddRange(selectFromList.GetList().ToArray());
                control.SelectedItem = selectFromList.Value;
                control.SelectedIndexChanged += (sender, args) =>
                {
                    selectFromList.Value = (control.SelectedItem as string);
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
            }
            else if (value is ScriptValue scriptValue)
            {
                var control = new TextBox() { Text = scriptValue.Script };
                control.TextChanged += (sender, args) =>
                {
                    scriptValue.Script = (control.Text);
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
                var cc = new CodeCompletionControl(control);
                cc.NeedSugestions += Cc_NeedSugestions;
            }
            else if (value is bool b)
            {
                var control = new CheckBox() { Checked = b };
                control.CheckedChanged += (sender, args) =>
                {
                    toolParameter[pKey] = (control.Checked);
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
            }
            if (value is Guid refId && pKey.StartsWith("Reference"))
            {
                var control = new ComboBox()
                {
                    DropDownStyle = ComboBoxStyle.DropDownList
                };
                control.SelectedIndexChanged += (sender, args) =>
                {
                    toolParameter[pKey] = ((control.SelectedItem)as BaseOperation)?.Id??NoOrigin.Instance.Id;
                    panel2.Enabled = true;
                    TriggerChanged();
                };
                var candidates = _workflowList.Operations.TakeWhile(x=> x.Id != operation.Id);
                control.Items.Add(NoOrigin.Instance);
                if (pKey.Contains("Edge"))
                {
                    control.Items.AddRange(candidates.Where(x => x is IHaveEdge).Select(x => x as IHaveEdge).ToArray());
                }
                else
                {
                    control.Items.AddRange(candidates.Where(x => x is IHaveOrigin ).Select(x => x as IHaveOrigin).ToArray());
                }
                
                
                control.DisplayMember = nameof(BaseOperation.Label);
                control.SelectedItem = candidates.FirstOrDefault(x=>x.Id==refId)?? NoOrigin.Instance;
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);
            }
            else if(value is Action a)
            {
                var control = new Button()
                {
                    Text = "Execute"
                };

                control.Click += (sender, args) => a();
                var row = new ToolConfigurationRow(BaseOperation.MakeOperationName(pKey), control);
                row.Width = flowLayoutPanel1.Width - 50;
                this.flowLayoutPanel1.Controls.Add(row);

            }
           
            
           
        }

        private static string MakeRelative(string path, string basePath)
        {
            var uri = new Uri(path);
            var baseUri = new Uri(basePath);

            // if path can't be made relative
            if (baseUri.Scheme != uri.Scheme)
            {
                return path;
            }

            var res= baseUri.MakeRelativeUri(uri).ToString();
            // ensure windows path separator
            return "..\\"+res.Replace("/", "\\");
            

        }

        private void Cc_NeedSugestions(string input, List<string> suggestions)
        {
            
            var start = input.IndexOf(".");
            if (start == -1)
            {
                
                suggestions.AddRange(_workflowList.Operations.Select(x => x.Label).Where(x=>x.StartsWith(input)));
                suggestions.Add("config");
                return;
            }
            var word = input.Substring(0, start);
            if (word == "config")
            {
                GenerateSugestions(input.Substring(start + 1), _workflowList.Configuration, suggestions);
                return;
            }

            var o = _workflowList.Operations.FirstOrDefault(x => x.Label == word);
            if (o!=null)
            {
                GenerateSugestions(input.Substring(start + 1), o, suggestions);
            }
        }

        private IEnumerable<PropertyInfo> GetAllowedProperties(Type type)
        {
            return type.GetProperties().Where(x => x.GetCustomAttribute<NotForToolAttribute>() == null);
        }
        private void GenerateSugestions(string input, object o, List<string> suggestions)
        {
            var start = input.IndexOf(".");
            if (start == -1)
            {
                var props = GetAllowedProperties(o.GetType()).Where(x => x.Name.StartsWith(input)).ToList();
                props.ForEach(x => suggestions.Add(x.Name));
            }
            else
            {
                var word = input.Substring(0, start);
                var prop = GetAllowedProperties(o.GetType()).FirstOrDefault(x => x.Name == word);
                if (prop != null)
                {
                    GenerateSugestions(input.Substring(start + 1), prop.GetValue(o), suggestions);
                }
            }
        }

        private void TriggerChanged()
        {
            if(cbAutoApply.Checked)
                btnOk_Click(null,null);
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            _selectedOperation.SetParameters(_parameters);
            SetOperation(_selectedOperation);
            _workflowList.ExecuteTill(_selectedOperation);
            _workflowList.ExecuteOne(_selectedOperation);
            _visionStepsPanel.UpdateView();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            SetOperation(_selectedOperation);
        }
    }
}
