using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using static StageStatic;

/// <summary>
/// Attached to prefab Def_Stage
/// </summary>
public class StageBehaviour : MonoBehaviour
{
    /// <summary>
    /// Re-loads <see cref="StageStatic.stage"/> to  display all <see cref="Fact">Facts</see>.
    /// </summary>
    void Start()
    {
        StageStatic.LoadInitStage(true /*StageStatic.stage.player_record.solved*/, gameObject);
        GenerateMMTCollider(gameObject);
    }

    public static List<TriangleFact> GenerateMMTCollider(GameObject obj = null)
    {
        bool debug = false;
        var t0 = System.DateTime.Now;
        List<TriangleFact> tris = new();

        foreach (BoxCollider coll in FindObjectsOfType<BoxCollider>())
        {
            if (!UseMe(coll))
                continue;

            Vector3[] minmax = new Vector3[]
            {
                coll.center - coll.size/2,
                coll.center + coll.size/2,
            };

            _QuadAdd(0, 1, 3, 2);
            _QuadAdd(1, 5, 7, 3);
            _QuadAdd(5, 4, 6, 7);
            _QuadAdd(4, 0, 2, 6);

            _QuadAdd(2, 3, 7, 6);
            _QuadAdd(0, 4, 5, 1);

            void _QuadAdd(uint i, uint j, uint k, uint l)
            {
                _TrisAdd(i, k, j);
                _TrisAdd(k, i, l);
            }

            void _TrisAdd(uint i, uint j, uint k)
            {
                tris.Add(new(
                    new[] {
                        _Transform(new Vector3(minmax[i>>0&1][0], minmax[i>>1&1][1], minmax[i>>2&1][2]), coll.transform.localToWorldMatrix),
                        _Transform(new Vector3(minmax[j>>0&1][0], minmax[j>>1&1][1], minmax[j>>2&1][2]), coll.transform.localToWorldMatrix),
                        _Transform(new Vector3(minmax[k>>0&1][0], minmax[k>>1&1][1], minmax[k>>2&1][2]), coll.transform.localToWorldMatrix),
                }));
            }
        }

        //foreach (MeshCollider coll in FindObjectsOfType<MeshCollider>())
        //{
        //    if (!UseMe(coll)
        //     || !coll.sharedMesh.isReadable)
        //        continue;

        //    for (int i = 0; i < coll.sharedMesh.triangles.Length; i += 3)
        //    {
        //        tris.Add(new(
        //            new[] {
        //                _Transform(coll.sharedMesh.vertices[coll.sharedMesh.triangles[i + 0]], coll.transform.localToWorldMatrix),
        //                _Transform(coll.sharedMesh.vertices[coll.sharedMesh.triangles[i + 1]], coll.transform.localToWorldMatrix),
        //                _Transform(coll.sharedMesh.vertices[coll.sharedMesh.triangles[i + 2]], coll.transform.localToWorldMatrix),
        //            })
        //        );
        //    }
        //}

        if (debug && obj != null)
        {
            var t1 = System.DateTime.Now - t0;
            Debug.Log($"{tris.Count} Tris in {t1.Milliseconds}ms");

            Mesh mesh = new()
            {
                indexFormat = UnityEngine.Rendering.IndexFormat.UInt32,
                vertices = tris.SelectMany(t => t.Verticies).ToArray(),
                triangles = Enumerable.Range(0, tris.Count * 3).ToArray(),
            };
            mesh.RecalculateNormals();
            mesh.Optimize();

            MeshFilter filter = obj.GetComponent<MeshFilter>();
            filter.sharedMesh = mesh;
        }

        return tris;

        Vector3 _Transform(Vector3 vert, Matrix4x4 toworld)
        {
            Vector4 h = toworld * new Vector4(vert.x, vert.y, vert.z, 1f);
            return new Vector3(h.x, h.y, h.z);
        }

        bool UseMe(Collider coll) => true; //TODO: filter for what we want
    }

    /// <summary>
    /// Resets changes made by <see cref="StageStatic.stage"/> and frees ressources.
    /// </summary>
    private void OnDestroy()
    {
        StageStatic.SetMode(Mode.Play); // no Mode.Create
        // StageStatic.stage.solution.hardreset(invoke_event: false); NO! keep in memory!
        // StageStatic.stage?.factState.hardreset(invoke_event: false); also keep?
    }

    /// <summary>
    /// Wrapps <see cref="SetMode(Mode, GameObject)"/>. Needed as endpoint for unity buttons.
    /// </summary>
    /// <param name="create"><c>SetMode(create ? Mode.Create : Mode.Play);</c></param>
    public void SetMode(bool create)
    {
        SetMode(create ? Mode.Create : Mode.Play);
    }

    /// <summary>
    /// Wrapps <see cref="StageStatic.SetMode(Mode, GameObject)"/>. Defaulting <paramref name="obj"/> to <see cref="this.gameObject"/>.
    /// </summary>
    /// \copydetails StageStatic.SetMode(Mode, GameObject)
    public void SetMode(Mode mode, GameObject obj = null)
    {
        obj ??= gameObject;
        StageStatic.SetMode(mode, obj);
    }
}