using System.Collections.Generic;
using UnityEngine;

namespace Intrepid.Gameplay.Prototyping
{
    public static class MeshUtils
    {
        public static void AddGridFace(List<Vector3> vertices,
            List<int> triangles,
            List<Vector2> uvs,
            Vector3 origin,
            Vector3 axisU,
            Vector3 axisV,
            int countU,
            int countV,
            float metersPerTextureRepeat = 1f)
        {
            var baseIndex = vertices.Count;
            for (var y = 0; y <= countV; y++)
            {
                for (var x = 0; x <= countU; x++)
                {
                    var p = origin + axisU * x + axisV * y;
                    vertices.Add(p);
                    uvs.Add(new Vector2(
                        x / metersPerTextureRepeat,
                        y / metersPerTextureRepeat
                    ));
                }
            }
            var rowSize = countU + 1;
            for (var y = 0; y < countV; y++)
            {
                for (var x = 0; x < countU; x++)
                {
                    var i00 = baseIndex + y * rowSize + x;
                    var i10 = baseIndex + y * rowSize + x + 1;
                    var i01 = baseIndex + (y + 1) * rowSize + x;
                    var i11 = baseIndex + (y + 1) * rowSize + x + 1;

                    triangles.Add(i00);
                    triangles.Add(i10);
                    triangles.Add(i11);

                    triangles.Add(i00);
                    triangles.Add(i11);
                    triangles.Add(i01);
                }
            }
        }
        
        public static void AddTriangle(
            List<Vector3> vertices,
            List<int> triangles,
            List<Vector2> uvs,
            Vector3 a,
            Vector3 b,
            Vector3 c,
            Vector2 uvA,
            Vector2 uvB,
            Vector2 uvC)
        {
            var index = vertices.Count;

            vertices.Add(a);
            vertices.Add(b);
            vertices.Add(c);

            triangles.Add(index);
            triangles.Add(index + 1);
            triangles.Add(index + 2);

            uvs.Add(uvA);
            uvs.Add(uvB);
            uvs.Add(uvC);
        }

        public static void AddCubeFaces(
            List<Vector3> vertices,
            List<int> triangles,
            List<Vector2> uvs,
            int sizeX,
            int sizeY,
            int sizeZ,
            float halfX,
            float halfY,
            float halfZ,
            bool halfLateral = false)
        {
            // Top
            AddGridFace(vertices, triangles, uvs,
                new Vector3(-halfX, halfY, halfZ),
                Vector3.right,
                Vector3.back,
                sizeX,
                sizeZ);
            
            // Bottom (face up)
            AddGridFace(vertices, triangles, uvs,
                new Vector3(-halfX, -halfY, halfZ),
                Vector3.right,
                Vector3.back,
                sizeX,
                sizeZ);

            // Back
            AddGridFace(vertices, triangles, uvs,
                new Vector3(halfX, -halfY, -halfZ),
                Vector3.left,
                Vector3.up * (halfLateral ? 0.5f : 1.0f),
                sizeX,
                sizeY);

            // // Front
            // AddGridFace(vertices, triangles, uvs,
            //     new Vector3(-halfX, -halfY, halfZ),
            //     Vector3.right,
            //     Vector3.up * (halfLateral ? 0.5f : 1.0f),
            //     sizeX,
            //     sizeY);

            // Right
            AddGridFace(vertices, triangles, uvs,
                new Vector3(halfX, -halfY, halfZ),
                Vector3.back,
                Vector3.up * (halfLateral ? 0.5f : 1.0f),
                sizeZ,
                sizeY);

            // Left
            AddGridFace(vertices, triangles, uvs,
                new Vector3(-halfX, -halfY, -halfZ),
                Vector3.forward,
                Vector3.up * (halfLateral ? 0.5f : 1.0f),
                sizeZ,
                sizeY);
        }
        
        public static void AddQuadFace(
            List<Vector3> vertices,
            List<int> triangles,
            List<Vector2> uvs,
            Vector3 origin,
            Vector3 axisU,
            Vector3 axisV,
            Vector2 uv00,
            Vector2 uv10,
            Vector2 uv11,
            Vector2 uv01)
        {
            var index = vertices.Count;

            var p00 = origin;
            var p10 = origin + axisU;
            var p11 = origin + axisU + axisV;
            var p01 = origin + axisV;

            vertices.Add(p00);
            vertices.Add(p10);
            vertices.Add(p11);
            vertices.Add(p01);

            triangles.Add(index + 0);
            triangles.Add(index + 1);
            triangles.Add(index + 2);
            triangles.Add(index + 0);
            triangles.Add(index + 2);
            triangles.Add(index + 3);

            uvs.Add(uv00);
            uvs.Add(uv10);
            uvs.Add(uv11);
            uvs.Add(uv01);
        }

        public static void AddHorizontalCapFace(
            List<Vector3> vertices,
            List<int> triangles,
            List<Vector2> uvs,
            float y,
            float halfX,
            float halfZ,
            float uvScaleX,
            float uvScaleZ)
        {
            AddQuadFace(
                vertices,
                triangles,
                uvs,
                new Vector3(-halfX, y, halfZ),
                Vector3.right * (halfX * 2f),
                Vector3.back * (halfZ * 2f),
                new Vector2(0f, 0f),
                new Vector2(uvScaleX, 0f),
                new Vector2(uvScaleX, uvScaleZ),
                new Vector2(0f, uvScaleZ)
            );
        }

        public static void AddInternalHorizontalCapFaces(
            List<Vector3> vertices,
            List<int> triangles,
            List<Vector2> uvs,
            int sizeX,
            int sizeY,
            int sizeZ,
            float halfX,
            float halfY,
            float halfZ,
            int capStep = 1)
        {
            capStep = Mathf.Max(1, capStep);

            for (var height = capStep; height < sizeY; height += capStep)
            {
                var y = -halfY + height;

                AddHorizontalCapFace(
                    vertices,
                    triangles,
                    uvs,
                    y,
                    halfX,
                    halfZ,
                    sizeX,
                    sizeZ
                );
            }
        }
    }
}