using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Align;
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Edge;
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Origin;
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.RotatedRectangle;
using Hawkeye.VisionBuilder.Workflow.Links;
using Hawkeye.VisionBuilder.Workflow.Operations.Attributes;

using OpenCvSharp;

namespace Hawkeye.VisionBuilder.Workflow.Operations.PositionDetection.Edges;
[Category("Simple/Edges")]
public class FindEdgeOperation : BaseOperation, IHaveEdge, IHaveOrigin
{
    private readonly WorkflowList _workflowList;

    public Guid ReferenceId { get; set; } = new Guid();
    public EdgeOrigin EdgeOrigin { get; set; }
    public FindEdgeOperation(WorkflowList workflowList)
    {
        _workflowList = workflowList;
        SearchArea = new RotatedRectangleElement()
        {
            Location = new Vector2(100, 100),
            Editable = true,
            HalfWidth = 50,
            Height = new Vector2(0, 100),
            IsGood = true
        };
    }
    private string _status;
    public override string ToString()
    {
        return _status;
    }

    protected override void InterpretInternal(Context context)
    {
        if (context.ActiveImage == null)
        {
            Result = false;
            _status = "No image provided";
            return;
        }

        SearchArea.MovePivot(_workflowList.GetOriginById(ReferenceId).Origin.Location);
        context.GraphicsElements.Add(SearchArea);


        var img = context.ActiveImage.ImageData;
        var mask = new Mat(img.Size(), img.Type(), Scalar.Black);

        SearchArea.FillMat(mask);

        var edges = img.Canny(100, 200);
        var mat = edges.BitwiseAnd(mask).ToMat();


        mat.MinMaxLoc(out _, out var maxVal, out _, out var maxLoc);
        if (maxVal < 0.01)
        {
            _status = "No edges found";
            SearchArea.IsGood = false;
            context.GraphicsElements.Add(SearchArea);
            Result = false;
            return;
        }
        

        Cv2.FindContours(mat, out var contours, out var hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);
        //var closest = contours.OrderBy(c =>
        //{

        //    return (c.Select(x => new Vector2(x.X, x.Y)).Aggregate((v1, v2) => v1 + v2) / c.Length - SearchArea.Location).Length();

        //}).First();

        var closest = contours.SelectMany(x => x);
        var line = Cv2.FitLine(closest, DistanceTypes.L2, 0, 0.01, 0.01);



        context.GraphicsElements.Add(new EdgeElement()
        {
            Location = new Vector2((float)line.X1, (float)line.Y1),
            Rotation = (float)line.GetVectorRadian()
        });

        EdgeOrigin = new EdgeOrigin()
        {
            Found = true,
            Location = new Vector2((float)line.X1, (float)line.Y1),
            Rotation = (float)line.GetVectorRadian()
        };
        Origin = new OriginElement()
        {
            Location = new Vector2((float)line.X1, (float)line.Y1)
        };
        context.GraphicsElements.Add(new AlignElement() { Location = new Vector2((float)line.X1, (float)line.Y1) });
        _status = EdgeOrigin.ToString();
        SearchArea.IsGood = true;
        Result = true;

    }
    public override void SetParameters(Dictionary<string, object> parameters)
    {
        base.SetParameters(parameters);

        SearchArea.SetPivot(_workflowList.GetOriginById(ReferenceId).Origin.Location);
    }
    public override void Save(BinaryWriter bw)
    {
        bw.Write(Id.ToString());
        bw.Write(Label);
        bw.Write(ReferenceId.ToString());
        SearchArea.Save(bw);
    }

    public override void Load(BinaryReader br)
    {
        Id = new Guid(br.ReadString());
        Label = br.ReadString();
        ReferenceId = new Guid(br.ReadString());
        SearchArea = new RotatedRectangleElement();
        SearchArea.Load(br);
    }

    public override void Save(Dictionary<string, object> dict)
    {
        base.Save(dict);
        dict[nameof(ReferenceId)] = ReferenceId.ToString();
        
        // Save RotatedRectangleElement SearchArea properties manually
        dict["SearchArea_Editable"] = SearchArea.Editable;
        dict["SearchArea_IsGood"] = SearchArea.IsGood;
        dict["SearchArea_LocationX"] = SearchArea.Location.X;
        dict["SearchArea_LocationY"] = SearchArea.Location.Y;
        dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
        dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
        dict["SearchArea_HeightX"] = SearchArea.Height.X;
        dict["SearchArea_HeightY"] = SearchArea.Height.Y;
        dict["SearchArea_HalfWidth"] = SearchArea.HalfWidth;
    }

    public override void Load(Dictionary<string, object> dict)
    {
        base.Load(dict);
        if (dict.ContainsKey(nameof(ReferenceId)))
            ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
        
        // Load RotatedRectangleElement SearchArea properties manually
        if (dict.ContainsKey("SearchArea_Editable") || dict.ContainsKey("SearchArea_LocationX") || dict.ContainsKey("SearchArea_HeightX"))
        {
            SearchArea = new RotatedRectangleElement();
            if (dict.ContainsKey("SearchArea_Editable"))
                SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
            if (dict.ContainsKey("SearchArea_IsGood"))
                SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
            if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
                SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
            if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
                SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
            if (dict.ContainsKey("SearchArea_HeightX") && dict.ContainsKey("SearchArea_HeightY"))
                SearchArea.Height = new Vector2(Convert.ToSingle(dict["SearchArea_HeightX"]), Convert.ToSingle(dict["SearchArea_HeightY"]));
            if (dict.ContainsKey("SearchArea_HalfWidth"))
                SearchArea.HalfWidth = Convert.ToSingle(dict["SearchArea_HalfWidth"]);
        }
    }


    public RotatedRectangleElement SearchArea { get; set; }
    public OriginElement Origin { get; set; }
}