using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using GraphiXNA.Materials;

namespace GraphiXNA.Graphics.Library
{
    public class Torus : Object3D
    {
        float HoleRadius;
        float TorusRadius;
        int TorusPrecision;

        public Torus(GraphicsDevice graphicsDevice, float holeRadius, float torusRadius, int torusPrecision)
            : base(graphicsDevice)
        {
            GraphicsDevice = graphicsDevice;
            HoleRadius = holeRadius;
            TorusRadius = torusRadius;
            TorusPrecision = torusPrecision;

            NumberOfVertices = (torusPrecision + 1) * (torusPrecision + 1);
            NumberOfIndices = 2 * torusPrecision * torusPrecision * 3;

            PrimitiveType = PrimitiveType.TriangleStrip;
            PrimitiveCount = NumberOfIndices - 2;

            VertexStride = VertexPositionNormalTexture.SizeInBytes;

            VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[NumberOfVertices];
            short[] indices = new short[NumberOfIndices];

            //calculate the first ring - inner radius = holeRadius+torusRadius, outer radius = torusRadius
            float innerRadius = holeRadius + torusRadius;
            Matrix rotation = Matrix.Identity;
            Vector3 transformed;
            for (int i = 0; i < torusPrecision + 1; i++)
            {
                rotation = Matrix.CreateRotationZ(MathHelper.ToRadians(i * 360.0f / torusPrecision));
                transformed = Vector3.Transform(new Vector3(torusRadius, 0.0f, 0.0f), rotation);
                vertices[i].Position = transformed + new Vector3(innerRadius, 0.0f, 0.0f); // translation
                vertices[i].TextureCoordinate.X = 0.0f;
                vertices[i].TextureCoordinate.Y = (float)i / torusPrecision;
                vertices[i].Normal = Vector3.Normalize(transformed);
            }

            //rotate the first ring to get the other rings
            for (int ring = 1; ring < torusPrecision + 1; ring++)
            {
                for (int i = 0; i < torusPrecision + 1; i++)
                {
                    rotation = Matrix.CreateRotationY(MathHelper.ToRadians(ring * 360.0f / torusPrecision));
                    transformed = Vector3.Transform(vertices[i].Position, rotation);

                    vertices[ring * (torusPrecision + 1) + i].Position = transformed;
                    vertices[ring * (torusPrecision + 1) + i].TextureCoordinate.X = 2.0f * ring / torusPrecision;
                    vertices[ring * (torusPrecision + 1) + i].TextureCoordinate.Y = vertices[i].TextureCoordinate.Y;

                    transformed = Vector3.Transform(vertices[i].Normal, rotation);
                    vertices[ring * (torusPrecision + 1) + i].Normal = transformed;
                }
            }


            //calculate the indices
            for (int ring = 0; ring < torusPrecision; ring++)
            {
                for (int i = 0; i < torusPrecision; i++)
                {
                    indices[((ring * torusPrecision + i) * 2) * 3 + 0] = (short)(ring * (torusPrecision + 1) + i);
                    indices[((ring * torusPrecision + i) * 2) * 3 + 1] = (short)((ring + 1) * (torusPrecision + 1) + i);
                    indices[((ring * torusPrecision + i) * 2) * 3 + 2] = (short)(ring * (torusPrecision + 1) + i + 1);
                    indices[((ring * torusPrecision + i) * 2 + 1) * 3 + 0] = (short)(ring * (torusPrecision + 1) + i + 1);
                    indices[((ring * torusPrecision + i) * 2 + 1) * 3 + 1] = (short)((ring + 1) * (torusPrecision + 1) + i);
                    indices[((ring * torusPrecision + i) * 2 + 1) * 3 + 2] = (short)((ring + 1) * (torusPrecision + 1) + i + 1);
                }
            }

            VertexBuffer = new VertexBuffer(graphicsDevice, VertexPositionNormalTexture.SizeInBytes * NumberOfVertices, BufferUsage.WriteOnly);
            IndexBuffer = new IndexBuffer(graphicsDevice, 2 * NumberOfIndices, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);

            VertexBuffer.SetData<VertexPositionNormalTexture>(vertices);
            IndexBuffer.SetData<short>(indices);

            VertexDeclaration = new VertexDeclaration(graphicsDevice, VertexPositionNormalTexture.VertexElements);
        }
    }
}
