using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshFilter))]

public class ExtrudedMesh : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {

    }

    // Converted from UnityScript to C# at http://www.M2H.nl/files/js_to_c.php - by Mike Hergaarden
    // Do test the code! You usually need to change a few small bits.

    // Generates an extrusion trail from the attached mesh
    // Uses the MeshExtrusion algorithm in MeshExtrusion.cs to generate and preprocess the mesh.
    public double time = 1.0f;
    double prevtime;
    public bool leaveworldline = true;
    bool autoCalculateOrientation = true;
    double minDistance = 0.05f;
    bool invertFaces = false;
    private Mesh srcMesh;
    private MeshExtrusion.Edge[] precomputedEdges;
    int cnt = 0;

    FlatlandMovement myMovement;

    public Hash128 hash;

    static int hashNum = 0;

    public class ExtrudedTrailSection
    {
        public Vector3 point;
        public Quaternion orientation;
        public Matrix4x4 matrix;
        public float time;
    }

    void Start()
    {
        srcMesh = GetComponent<MeshFilter>().sharedMesh;
        precomputedEdges = MeshExtrusion.BuildManifoldEdges(srcMesh);
        GetComponent<MeshCollider>().sharedMesh = GetComponent<MeshFilter>().mesh;
        prevtime = Time.time;
        transform.SetAsFirstSibling();

        myMovement = transform.parent.GetComponent<FlatlandMovement>();

        hash = Hash128.Compute(gameObject.name + hashNum++);
    }

    List<ExtrudedTrailSection> sections = new List<ExtrudedTrailSection>();

    void FixedUpdate()
    {
        transform.SetAsFirstSibling();
        var position = transform.position;
        var rotation = transform.rotation;
        var now = Time.time + 0.5f * (float)time;

        // Remove old sections
        while (sections.Count > 0 && now > sections[sections.Count - 1].time + time)
        {
            if(sections[sections.Count - 1].time - prevtime > time * 0.5f && leaveworldline)
            {
                var clone = Instantiate(transform.parent.gameObject, transform.parent.position, transform.parent.rotation);
                clone.GetComponentInChildren<MeshFilter>().sharedMesh = clone.GetComponentInChildren<MeshFilter>().mesh;

                clone.GetComponentInChildren<ExtrudedMesh>().hash = hash;

                var x = clone.GetComponents(typeof(Component)); 
                foreach(var t in x)
                {
                    if ((t is Transform))
                    {
                        continue;
                    }
                    else
                        DestroyImmediate(t);
;               }
                if (clone.transform.childCount > 1)
                {
                    var size = clone.transform.childCount;
                    for (int i = 1; i < size; ++i)
                    {
                        DestroyImmediate(clone.transform.GetChild(1).gameObject);
                    }
                }
                DestroyImmediate(clone.GetComponentInChildren<ExtrudedMesh>());
                clone.GetComponentInChildren<MeshCollider>().convex = false;
                clone.GetComponentInChildren<MeshCollider>().sharedMesh = clone.GetComponentInChildren<MeshFilter>().mesh;
                clone.transform.GetChild(0).gameObject.AddComponent<RuntimeCSGFlatlandObject>();
                clone.transform.GetChild(0).gameObject.GetComponent<RuntimeCSGFlatlandObject>().original = transform.parent.gameObject.GetComponent<FlatlandMovement>();
                clone.transform.GetChild(0).gameObject.GetComponent<RuntimeCSGFlatlandObject>().clock = transform.parent.gameObject.GetComponent<FlatlandMovement>().time;
                //clone.transform.GetChild(0).gameObject.GetComponent<RuntimeCSGFlatlandObject>().player = transform.parent.gameObject.GetComponent<FlatlandMovement>().levelManager.player;
                clone.layer = 8;
                clone.transform.GetChild(0).gameObject.layer = 8;
                Physics.IgnoreCollision(clone.GetComponentInChildren<MeshCollider>(), GetComponent<MeshCollider>());
                Destroy(clone, 20);
                prevtime = sections[sections.Count - 1].time;
            }
         
            sections.RemoveAt(sections.Count - 1);
        }

        // Add a new trail section to beginning of array
        if (sections.Count == 0 || (sections[0].point - position).sqrMagnitude > minDistance * minDistance)
        {
            var section = new ExtrudedTrailSection();
            section.point = position;
            section.matrix = transform.localToWorldMatrix;
            section.time = now;
            section.orientation = rotation;
            sections.Insert(0,section);
        }

        // We need at least 2 sections to create the line
        if (sections.Count < 2)
            return;

        var worldToLocal = transform.worldToLocalMatrix;
        var finalSections = new Matrix4x4[sections.Count];
        Quaternion previousRotation = Quaternion.LookRotation(sections[0].point - sections[1].point, Vector3.up);

        for (var i = 0; i < sections.Count; i++)
        {
            /*if (autoCalculateOrientation)
            {
                if (i == 0)
                {
                    var direction = sections[0].point - sections[1].point;
                    var rotation = Quaternion.LookRotation(direction, Vector3.up);
                    previousRotation = rotation;
                    finalSections[i] = worldToLocal * Matrix4x4.TRS(position, rotation, Vector3.one);
                }
                // all elements get the direction by looking up the next section
                else if (i != sections.Count - 1)
                {
                    var direction = sections[i].point - sections[i + 1].point;
                    var rotation = Quaternion.LookRotation(direction, Vector3.up);

                    // When the angle of the rotation compared to the last segment is too high
                    // smooth the rotation a little bit. Optimally we would smooth the entire sections array.
                    if (Quaternion.Angle(previousRotation, rotation) > 20)
                        rotation = Quaternion.Slerp(previousRotation, rotation, 0.5f);

                    previousRotation = rotation;
                    finalSections[i] = worldToLocal * Matrix4x4.TRS(sections[i].point, rotation, Vector3.one);
                }
                // except the last one, which just copies the previous one
                else
                {
                    finalSections[i] = finalSections[i - 1];
                }
            }
            else
            {*/
                if (i == 0)
                {
                    finalSections[i] = Matrix4x4.identity;
                }
                else
                {
                    finalSections[i] = worldToLocal * sections[i].matrix;
                }
            //}
        }

        // Rebuild the extrusion mesh	
        MeshExtrusion.ExtrudeMesh(srcMesh, GetComponent<MeshFilter>().mesh, finalSections, precomputedEdges, invertFaces);
        /*Mesh m = new Mesh();
        MeshExtrusion.ExtrudeMesh(srcMesh, m, finalSections, precomputedEdges, !invertFaces);

        CombineInstance[] combine = new CombineInstance[2];

        combine[0].mesh = GetComponent<MeshFilter>().mesh;
        //combine[0].transform = transform.worldToLocalMatrix;
        combine[1].mesh = m;
        //combine[0].transform = transform.worldToLocalMatrix;

        transform.GetComponent<MeshFilter>().mesh = new Mesh();
        transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);*/

    }

    public void OnCollisionStay(Collision collision)
    {
        if (myMovement == null)
        {
            return;
        }
        else
        {
            myMovement.OnCollisionStaychild(collision);
        }
    }

    public void OnCollisionEnter(Collision collision)
    {
        if (myMovement == null)
        {
            return;
        }
        else
        {
            myMovement.OnCollisionEnterchild(collision);
        }

    }

    public void OnCollisionExit(Collision collision)
    {
        if (myMovement == null)
        {
            return;
        }
        else
        {
            myMovement.OnCollisionExitchild(collision);
        }

    }

}
