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

public class Flag : MonoBehaviour
{
    public int Width;
    public int Height;

    public static float Distance = 1;

    public GameObject Particle;
    public GameObject[,] ParticleMatrix;
    public Mesh FlagMesh;

    private static Vector3[] vertices;
    private static int[] triangles;

    private void Awake()
    {
        FlagMesh = GetComponent<MeshFilter>().mesh = new Mesh();
        vertices = new Vector3[Width * Height];
        triangles = new int[(Width - 1) * (Height - 1) * 6];
        
        SetupMatrix();
        ConstructMesh();
    }

    public void Reset()
    {
        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            {
                Destroy(ParticleMatrix[x, y]);
            }
        }
        ParticleMatrix = null;
        Awake();
    }

    private void FixedUpdate()
    {
        ConstructMesh();
    }

    private void ConstructMesh()
    {
        for (int x = 0, v = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++, v++)
            {
                vertices[v] = ParticleMatrix[x, y].transform.position;
            }
        }
        for (int x = 0, v = 0, t = 0; x < Width - 1; x++, v++)
        {
            for (int y = 0; y < Height - 1; y++, v++, t += 6)
            {
                triangles[t] = v;
                triangles[t + 4] = triangles[t + 1] = v + 1;
                triangles[t + 3] = triangles[t + 2] = v + Height;
                
                triangles[t + 5] = v + Height + 1;
            }
        }
        FlagMesh.vertices = vertices;
        FlagMesh.triangles = triangles;
        FlagMesh.RecalculateNormals();
    }

    void SetupMatrix()
    {
        ParticleMatrix = new GameObject[Width, Height];
        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            {
                ParticleMatrix[x, y] = Instantiate(Particle, new Vector3(x * Distance, y * Distance), new Quaternion());
            }
        }

        for (int y = 0; y < Height; y++)
        {
            ParticleMatrix[0, y].GetComponent<Particle>().IsMovable = false;
        }

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            {
                ParticleMatrix[x, y].GetComponent<Particle>().StructuralNeighbors = GetStructuralNeighbors(x, y);
                ParticleMatrix[x, y].GetComponent<Particle>().ShearNeighbors = GetShearNeighbors(x, y);
                ParticleMatrix[x, y].GetComponent<Particle>().BendPerpendicularNeighbors = GetBendPerpendicularNeighbors(x, y);
                ParticleMatrix[x, y].GetComponent<Particle>().BendDiagonalNeighbors = GetBendDiagonalNeighbors(x, y);
            }
        }
    }

    List<GameObject> GetStructuralNeighbors(int x, int y)
    {
        return new List<GameObject>
        {
            ParticleMatrix.TryGetElement(x, y + 1),
            ParticleMatrix.TryGetElement(x + 1, y),
            ParticleMatrix.TryGetElement(x, y - 1),
            ParticleMatrix.TryGetElement(x - 1, y)
        }
        .Where(element => element != null).ToList();
    }

    List<GameObject> GetShearNeighbors(int x, int y)
    {
        return new List<GameObject>
        {
            ParticleMatrix.TryGetElement(x - 1, y - 1),
            ParticleMatrix.TryGetElement(x - 1, y + 1),
            ParticleMatrix.TryGetElement(x + 1, y - 1),
            ParticleMatrix.TryGetElement(x + 1, y + 1)
        }
        .Where(element => element != null).ToList();
    }

    List<GameObject> GetBendPerpendicularNeighbors(int x, int y)
    {
        return new List<GameObject>
        {
            ParticleMatrix.TryGetElement(x, y - 2),
            ParticleMatrix.TryGetElement(x, y + 2),
            ParticleMatrix.TryGetElement(x - 2, y),
            ParticleMatrix.TryGetElement(x + 2, y)
        }
        .Where(element => element != null).ToList();
    }

    List<GameObject> GetBendDiagonalNeighbors(int x, int y)
    {
        return new List<GameObject>
        {
            ParticleMatrix.TryGetElement(x - 2, y - 2),
            ParticleMatrix.TryGetElement(x - 2, y + 2),
            ParticleMatrix.TryGetElement(x + 2, y - 2),
            ParticleMatrix.TryGetElement(x + 2, y + 2)
        }
        .Where(element => element != null).ToList();
    }
}
