using System;
using System.Collections.Generic;


namespace MyGeometry
{
	public class Point3
	{
		public Vector3d vertexCoord;
		public int index;
        public int[] neig = new int[4];
		
        public Point3(Vector3d coord)
        {
            vertexCoord = coord;
        }
        public Point3(Vector3d coord, int ind)
        {
            vertexCoord = coord;
            index = ind;
        }
        public void addNeig(int[] neighb)
        {
            for (int i = 0; i < 4; i++)
                neig[i] = neighb[i];
        }
	}

	public class Face
	{
		public List<Point3> vertices = new List<Point3>();
		public Vector3d normal;
		public int index;
        public int verticesNum;
        public Vector3d center;

		public Face(List<Point3> pnts, int indFace)
		{
            for (int i = 0; i < pnts.Count; ++i)
                this.vertices.Add(pnts[i]);

			this.index = indFace;
            this.verticesNum = pnts.Count;
            //center = the center of the line connecting frist and middle point
            int mid = (int)(pnts.Count / 2);
            this.center = (pnts[0].vertexCoord + pnts[mid].vertexCoord) / 2;
			this.ComputeFaceNormal();
		}

		public void ComputeFaceNormal()
		{
            Vector3d v1 = this.vertices[1].vertexCoord - this.vertices[0].vertexCoord;
            Vector3d v2 = this.vertices[verticesNum - 1].vertexCoord - this.vertices[0].vertexCoord;
			Vector3d fNormal = v1.Cross(v2).Normalize();
			this.normal = new Vector3d(fNormal.x, fNormal.y, fNormal.z);
		}
	}
	
	public class Polygon
	{
		public List<Face> faces = new List<Face>();
		public List<Point3> vertices = new List<Point3>();
		public int verticesNum;
		public int faceNum;
        public bool lift = false;

		public Polygon(List<Point3> vertex)
		{
            for (int i = 0; i < vertex.Count; i++)
                this.vertices.Add(vertex[i]);
            // Initialzae a plane polygon 
            this.faces.Add(new Face(vertex, 0));
            this.verticesNum = vertices.Count;
            this.faceNum = 1;
        }

        public void rePolygon(List<Point3> newVertices, double liftDist)
        {
            int num = this.verticesNum;

			this.verticesNum = num * 2;

            this.faces.Add(new Face(newVertices, 1));

            for (int i = 0; i < num; i++)
                this.vertices.Add(newVertices[i]);

			//Add the side faces
			for (int i = 0; i < num; i++)
			{
                int[] currIndex = { i, (i + 1) % num, num + (i + 1 + num) % num, num + (i + num) % num };
				List<Point3> currPoints = new List<Point3>();
				for (int j = 0; j < 4; j++)
                    currPoints.Add(this.vertices[currIndex[j]]);
				this.faces.Add(new Face(currPoints, i + 2));
			}
			this.faceNum = num + 2;
            lift = true;
		}

        //Move a surface
        public void moveFace(int indexFace, double liftDist)
        {
            for (int i = 0; i < this.faces[indexFace].verticesNum; i++)
                this.faces[indexFace].vertices[i].vertexCoord += this.faces[indexFace].normal * liftDist;
        }
    }
    
    public class PolygonMesh
    {
        public List<Polygon> polygons = new List<Polygon>();
        public List<Face> faces = new List<Face>();
        public List<Point3> vertices = new List<Point3>();
        public int polygonNum;
        public int faceNum;
        public int vertexNum;

        public PolygonMesh(List<Polygon> polygons, int pNum, int fNum, int vNum)
        {
            for (int i = 0; i < polygons.Count; i++)
                this.polygons.Add(polygons[i]);
            this.polygonNum = pNum;
            this.faceNum = fNum;
            this.vertexNum = vNum;
        }

        public void rePolygonMesh(int polyIndex, double liftDist)
        {
            int num = this.polygons[polyIndex].verticesNum;
            List<Point3> addVertices = new List<Point3>();

            for (int i = 0; i < num; i++)
            {
                Vector3d vertexCoord = this.polygons[polyIndex].faces[0].vertices[i].vertexCoord + liftDist * this.polygons[polyIndex].faces[0].normal;
                Point3 newVertex = new Point3(vertexCoord, i + this.vertexNum);
                this.vertices.Add(newVertex);
                addVertices.Add(newVertex);
            }
            this.polygons[polyIndex].rePolygon(addVertices, liftDist);
            this.vertexNum += this.polygons[polyIndex].verticesNum * 2;
        }

        public void movePolygon(int polyIndex, int faceIndex, double liftDist)
        {
            this.polygons[polyIndex].moveFace(faceIndex, liftDist);
        }

    }	
}
