//Created by Neodrop
//neodrop@unity3d.ru
using UnityEngine;
//#pragma warning disable

[RequireComponent(typeof(AntaresBezierController))]
public class CurvedRoad : MonoBehaviour 
{
    public GameObject road;
    public int raycastLayer = 0;
    public Material roadMaterial;
    public float roadWidth = 6f;
    public float scaleU = 1f, scaleV = 1f, yShift = .1f;

    protected AntaresBezierController bc = null;
    protected Transform marker, markerLeft, markerRight;
    protected Quaternion currentRotation;
	
    public void InitFromEditor()
    {
        if (bc == null) bc = gameObject.GetComponent<AntaresBezierController>();
    }

    public void CreateRoad()
    {
        if (road == null || road.renderer) return;
        InitFromEditor();
        if (!bc)
        {
            Debug.Log("No bc!");
            return;
        }
        MeshFilter mf = road.AddComponent<MeshFilter>();
        MeshRenderer mr = road.AddComponent<MeshRenderer>();
        Mesh myMesh = new Mesh();
        int count = bc.wayPoints.Length;
        int vertCount = (count - 1) * 6;
        Color[] colors = new Color[vertCount];
        for (int i = 0; i < vertCount; i++) colors[i] = Color.white;
        myMesh.vertices = new Vector3[vertCount];
        myMesh.triangles = new int[vertCount * 2];
        int[] triangles = new int[myMesh.triangles.Length];
        Vector3[] vertices = new Vector3[vertCount];
        myMesh.uv = new Vector2[vertCount];
        Vector2[] uv = new Vector2[vertCount];

        if (marker == null)
        {
            marker = new GameObject("Road Points Marker").transform;
            markerLeft = new GameObject("Road Points Marker Left").transform;
            markerLeft.position = Vector3.left * (roadWidth / 2f);
            markerRight = new GameObject("Road Points Marker Right").transform;
            markerRight.position = Vector3.right * (roadWidth / 2f);
            markerLeft.parent = markerRight.parent = marker;
            marker.parent = road.transform;
            marker.position = bc.wayPoints[0];
            marker.LookAt(bc.wayPoints[1]);
        }

        int vindex = 0, tindex = 0;
        for (int i = 1; i < count; i++)
        {
            //0
            marker.position = bc.wayPoints[i - 1];
            if(i == 1)marker.LookAt(bc.wayPoints[i]);
            vertices[vindex] = RayCast(markerLeft);
            uv[vindex++] = new Vector2(0, 0);
            uv[4] = new Vector2(0, 0);

            //1
            vertices[vindex] = RayCast(markerRight);
            uv[vindex++] = new Vector2(scaleV, 0);
            uv[5] = new Vector2(scaleV, 0);

            marker.position = bc.wayPoints[i];
            currentRotation = marker.rotation;
            if (i < count - 1)
            {
                marker.LookAt(bc.wayPoints[i + 1]);
                marker.rotation = Quaternion.Lerp(currentRotation,marker.rotation, .5f);
            }

            //2
            vertices[vindex] = RayCast(markerLeft);
            uv[vindex++] = new Vector2(0, scaleU);
            uv[6] = new Vector2(0, scaleU);
            
            //3
            vertices[vindex] = RayCast(markerRight);
            uv[vindex++] = new Vector2(scaleV, scaleU);
            uv[7] = new Vector2(scaleV, scaleU); 

            triangles[tindex++] = vindex - 4;
            triangles[tindex++] = vindex - 3;
            triangles[tindex++] = vindex - 2;

            triangles[tindex++] = vindex-1;
            triangles[tindex++] = vindex-2;
            triangles[tindex++] = vindex-3;
        }

        myMesh.vertices = vertices;
        myMesh.uv = uv;
        myMesh.triangles = triangles;
        myMesh.colors = colors;
        
        mf.sharedMesh = myMesh;
        mf.sharedMesh.RecalculateNormals();
        mf.sharedMesh.RecalculateBounds();
        mf.sharedMesh.name = road.name + "_mesh";
        mr.material = roadMaterial;
        road.renderer.castShadows = false;
        ReverseNormals(mf.sharedMesh);
        road.transform.localPosition = transform.position * (-1f) + Vector3.up * yShift;
        //DestroyImmediate(myMesh);
    }

    public static void ReverseNormals(Mesh mesh)
    {
        Vector3[] normals = mesh.normals;
        int l = normals.Length;
        for (int k = 0; k < l; k++)
        {
            normals[k] = -normals[k];
        }
        l = mesh.subMeshCount;
        for (int m = 0; m < l; m++)
        {
            int[] triangles = mesh.GetTriangles(m);
            int tl = triangles.Length;
            for (int t = 0; t < tl; t += 3)
            {
                int temp = triangles[t + 0];
                triangles[t + 0] = triangles[t + 1];
                triangles[t + 1] = temp;
            }
            mesh.SetTriangles(triangles, m);
        }
        mesh.normals = normals;
        mesh.RecalculateNormals();
    }

    Vector3 upShift = Vector3.up * 100;
    Vector3 RayCast(Transform pos)
    {
        RaycastHit hit;
        if(Physics.Raycast(pos.position + upShift, pos.up * -1f, out hit, 200f, 1 << raycastLayer))
        {
            return hit.point;
        }
        else { Debug.Log("No hit"); return Vector3.zero; }
    }
}
