using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using static CommunicationEvents;
using static UnityEditor.PlayerSettings;
using static CanonBallProblemCalculator;
using Unity.Mathematics;
using System;

public class GenerateDemoFiles
{
    static bool firstcall = true;

    public static void GenerateAll()
    {
        if (!firstcall) return;
        if (GameObject.FindObjectOfType<GadgetBehaviour>(true) == null)
        {
            Debug.LogError("Cannot GenerateDemoFiles without populated GadgetManager");
            return;
        }

        Debug.LogWarning("Generating and Overwriting Stage Files");
        firstcall = false;

        GenerateTreeStage();
        GenerateRiverStage();
        GenerateCanonBallStage();
    }

    public static void GenerateTreeStage()
    {
        // Params
        float minimalSolutionHight = 6;

        // Generate Stage
        Stage demo = new Stage
        (
            number: 1,
            category: "Demo Category",
            name: "TechDemo A",
            scene: "RiverWorld",
            description: "Tree Stage",
            local: false
        );

        // needed to generate facts
        StageStatic.StageOfficial = new Dictionary<string, Stage>
        {
            { demo.name, demo },
        };
        StageStatic.SetStage(demo.name, false);

        // Populate Solution
        PointFact
            buttom = new PointFact(Vector3.zero, Vector3.up, StageStatic.stage.solution),
            top = new PointFact(Vector3.zero + Vector3.up * minimalSolutionHight, Vector3.up, StageStatic.stage.solution);

        StageStatic.stage.solution.Add(buttom, out _, false, null, null);
        StageStatic.stage.solution.Add(top, out _, true, null, null);

        LineFact target = new LineFact(buttom.Id, top.Id, StageStatic.stage.solution);
        var target_Id = StageStatic.stage.solution.Add(target, out _, true, null, null);

        // Set Solution
        StageStatic.stage.solution.ValidationSet =
            new List<SolutionOrganizer.SubSolution>
            { new SolutionOrganizer.SubSolution(new HashSet<string> { target_Id }, null, null, new LineFactHightDirectionComparer()) };

        // Set Gadgets/ Scrolls
        StageStatic.stage.AllowedGadgets = null;
        StageStatic.stage.AllowedScrolls = null;

        // Save
        StageStatic.SetMode(StageStatic.Mode.Create);
        StageStatic.stage.store(false, true);
    }

    public static void GenerateRiverStage()
    {
        // Params
        float minimalSolutionHight = 6;

        // Generate Stage
        Stage demo = new Stage
        (
            number: 2,
            category: "Demo Category",
            name: "TechDemo B",
            scene: "RiverWorld",
            description: "River Stage",
            local: false
        );

        // needed to generate facts
        StageStatic.StageOfficial = new Dictionary<string, Stage>
        {
            { demo.name, demo },
        };
        StageStatic.SetStage(demo.name, false);

        // Populate Solution
        PointFact
            buttom = new PointFact(Vector3.zero, Vector3.up, StageStatic.stage.solution),
            top = new PointFact(Vector3.zero + Vector3.up * minimalSolutionHight, Vector3.up, StageStatic.stage.solution);

        StageStatic.stage.solution.Add(buttom, out _, false, null, null);
        StageStatic.stage.solution.Add(top, out _, true, null, null);

        LineFact target = new LineFact(buttom.Id, top.Id, StageStatic.stage.solution);
        var target_Id = StageStatic.stage.solution.Add(target, out _, true, null, null);

        // Set Solution
        StageStatic.stage.solution.ValidationSet =
            new List<SolutionOrganizer.SubSolution> {
                new SolutionOrganizer.SubSolution(new HashSet<string> { target_Id }, null, null, new LineFactHightDirectionComparer()),
                new SolutionOrganizer.SubSolution(new HashSet<string> { target_Id }, null, null, new LineSpanningOverRiverWorldComparer()),
                new SolutionOrganizer.SubSolution(null, new List<int> { 1 }, new List<int> { 0 }, new LineFactHightComparer()),
            };

        // Set Gadgets/ Scrolls
        StageStatic.stage.AllowedGadgets = new() { new Pointer(), new Tape(), new AngleTool(), new LineTool(), new LotTool(), new Pendulum(), new Remover() }; //, new EqualCircleGadget(), new TestMiddlePoint() };
        StageStatic.stage.AllowedScrolls = new() { "OppositeLen" };//, "AngleSum", "Pythagoras", "CircleScroll", "CircleAreaScroll", "ConeVolumeScroll", "TruncatedConeVolumeScroll", "CylinderVolumeScroll", "MidPoint", "CircleLineAngleScroll", "CircleLineAngleToAngle", "SupplementaryAngles" };

        // Save
        StageStatic.SetMode(StageStatic.Mode.Create);
        StageStatic.stage.store(false, true);
    }

    public static void GenerateCanonBallStage()
    {
        // Params //List<Wall> walls, T starPos, T starVec, T gravity, int dimension
        int
            dim_const = 0,
            dim_A = 2,
            dim_B = 1;

        float Py_factor = 0.04905f; // 2f / 100f;

        float2 StartPos_py = new(380, 300);
        float2 StartVec_py = new(-490, 150);
        float2 Gravity_py = new(0, -200);

        (int x, int y, int width, int angle_deg, double bounce)[] PythonParams = {
            (250, 0, 50, 0, 2.0 ),
            (0, 0, 400, 90, 0.9 ),
            (400, 0, 400, 90, 0.9),
            (0, 400, 400, 0, 0.9),
            (50, 200, 100, 45, 1),
            (150, 100, 50, 0, 0.9),
            (200, 200, 100, 120, 0.9),
            (300, 300, 80, 150, 0.9),
            (300, 100, 100, 30, 0.8),
            (50, 50, 100, 60, 0.9),
            (300, 0, 100, 0, 0.4),
            (0, 0, 250, 0, 0.1),
        };

        //Parse PythonParams
        Vector3
            StartPos = Vector3.zero,
            StartVec = Vector3.zero,
            Gravity = Vector3.zero;

        StartPos[dim_A] = StartPos_py[0] * Py_factor;
        StartPos[dim_B] = StartPos_py[1] * Py_factor;
        StartVec[dim_A] = StartVec_py[0] * Py_factor;
        StartVec[dim_B] = StartVec_py[1] * Py_factor;
        Gravity[dim_A] = Gravity_py[0] * Py_factor;
        Gravity[dim_B] = Gravity_py[1] * Py_factor;

        float[,,] Wall_parameter = new float[PythonParams.Length, 2, 2];
        float[] Bounce_parameter = new float[PythonParams.Length];
        for (uint i = 0; i < PythonParams.Length; i++)
        {
            Wall_parameter[i, 0, 0] = Py_factor * (PythonParams[i].x);
            Wall_parameter[i, 0, 1] = Py_factor * (PythonParams[i].y);

            Wall_parameter[i, 1, 0] = Py_factor * (PythonParams[i].x + PythonParams[i].width * Mathf.Cos(MathfExtensions.ToRadians(PythonParams[i].angle_deg)));
            Wall_parameter[i, 1, 1] = Py_factor * (PythonParams[i].y + PythonParams[i].width * Mathf.Sin(MathfExtensions.ToRadians(PythonParams[i].angle_deg)));

            Bounce_parameter[i] = (float)PythonParams[i].bounce;
        }

        // Generate Stage
        Stage demo = new Stage
        (
            number: 2,
            category: "Demo Category",
            name: "CanonBall A",
            scene: "RiverWorld",
            description: "CanonBall Test",
            local: false
        );

        // needed to generate facts
        StageStatic.StageOfficial = new Dictionary<string, Stage>
        {
            { demo.name, demo },
        };
        StageStatic.SetStage(demo.name, false);

        // Populate Solution
        List<Wall> Walls = new();
        for (int i = 0; i < PythonParams.Length; i++)
        {
            Vector3 tmpVec = Vector3.zero;

            tmpVec[dim_A] = Wall_parameter[i, 0, 0];
            tmpVec[dim_B] = Wall_parameter[i, 0, 1];
            PointFact topA = new(tmpVec, Vector3.up, StageStatic.stage.solution);
            string topAURI = StageStatic.stage.solution.Add(topA, out _, false, null, null);

            tmpVec[dim_A] = Wall_parameter[i, 1, 0];
            tmpVec[dim_B] = Wall_parameter[i, 1, 1];
            PointFact topB = new(tmpVec, Vector3.up, StageStatic.stage.solution);
            string topBURI = StageStatic.stage.solution.Add(topB, out _, true, null, null);

            LineFact topology = new LineFact(topAURI, topBURI, StageStatic.stage.solution);
            StageStatic.stage.solution.Add(topology, out _, true, null, null);

            Walls.Add(new(topology, Bounce_parameter[i]));
        }

        // Set Solution
        string BallURI = StageStatic.stage.solution.Add(
            new PointFact(StartPos, Vector3.up, StageStatic.stage.solution),
            out _, false, null, null);

        List<FunctionFact<float, Vector3>> targetfunc = new CanonBallProblemCalculator(Walls, StartPos, StartVec, Gravity, dim_const, dim_A, dim_B, StageStatic.stage.solution).Compute();
        foreach (var func in targetfunc)
            StageStatic.stage.solution.Add(func, out _, true, null, null);

        //TODO: save as omdoc
        //      ball + attach funcs + cheat + generate demo button?
        //StageStatic.stage.solution.ValidationSet =
        //        new List<SolutionOrganizer.SubSolution> {
        //        new SolutionOrganizer.SubSolution(new HashSet<string> { target_Id }, null, null, new LineFactHightDirectionComparer()),
        //        new SolutionOrganizer.SubSolution(new HashSet<string> { target_Id }, null, null, new LineSpanningOverRiverWorldComparer()),
        //        new SolutionOrganizer.SubSolution(null, new List<int> { 1 }, new List<int> { 0 }, new LineFactHightComparer()),
        //        };

        // Set Gadgets/ Scrolls
        StageStatic.stage.AllowedGadgets = null;
        StageStatic.stage.AllowedScrolls = null;

        // Save
        StageStatic.SetMode(StageStatic.Mode.Create);
        StageStatic.stage.store(false, true);
    }
}