using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MojaPierwszaGraMonoGame
{
    class Kwadryka
    {
        private GraphicsDevice gd;
        private VertexBuffer buforWerteksów = null;
        private IndexBuffer buforIndeksów = null;

        private int liczbaWerteksówWPaśmie;
        private int liczbaTrójkątówWPaśmie;
        private int liczbaPasm = 1;

        private bool czySferaZBuforemIndeksów = false;

        private Kwadryka(GraphicsDevice gd, int liczbaSekcji)
        {
            this.gd = gd;
            liczbaTrójkątówWPaśmie = 2 * liczbaSekcji;
            liczbaWerteksówWPaśmie = 2 + liczbaTrójkątówWPaśmie;
        }

        #region Metody statyczny
        static private VertexPositionColor[] BudujTablicęWerteksówŚciętegoStożka(
            float promieńDolny, float promieńGórny, float położeniePodstawy, float wysokość, int liczbaSekcji, 
            Color kolor)
        {
            int liczbaWerteksów = 2 * (liczbaSekcji + 1);

            VertexPositionColor[] tablicaWerteksów = new VertexPositionColor[liczbaWerteksów];
            float przyrostKąta = MathHelper.TwoPi / liczbaSekcji;

            for (int i = 0; i <= liczbaSekcji; i++)
            {
                float kąt = i * przyrostKąta;
                Vector3 kierunek = new Vector3((float)Math.Cos(kąt), (float)Math.Sin(kąt), 0);
                tablicaWerteksów[2 * i].Position = promieńDolny * kierunek + położeniePodstawy * Vector3.UnitZ;                
                tablicaWerteksów[2 * i].Color = kolor;
                tablicaWerteksów[2 * i + 1].Position = promieńGórny * kierunek + (położeniePodstawy + wysokość) * Vector3.UnitZ;
                tablicaWerteksów[2 * i + 1].Color = kolor;
            }

            return tablicaWerteksów;
        }

        static private Kwadryka StwórzKwadrykę( //pseudokonstruktor
            GraphicsDevice gd,
            float promieńDolny, float promieńGórny, float położeniePodstawy, float wysokość, int liczbaSekcji, 
            Color kolor)
        {
            Kwadryka kwadryka = new Kwadryka(gd, liczbaSekcji);

            VertexPositionColor[] tablicaWerteksów = Kwadryka.BudujTablicęWerteksówŚciętegoStożka(
                promieńDolny, promieńGórny, położeniePodstawy, wysokość, liczbaSekcji, kolor);
            kwadryka.ZbudujBuforWerteksów(tablicaWerteksów);
            return kwadryka;
        }

        static public Kwadryka StwórzŚciętyStożek(
            GraphicsDevice gd,
            float promieńDolny, float promieńGórny, float położeniePodstawy, float wysokość, int liczbaSekcji,
            Color kolor)
        {
            return StwórzKwadrykę(gd, promieńDolny, promieńGórny, położeniePodstawy, wysokość, liczbaSekcji, kolor);
        }

        static public Kwadryka StwórzWalec(
            GraphicsDevice gd,
            float promień, float położeniePodstawy, float wysokość, int liczbaSekcji,
            Color kolor)
        {
            return StwórzKwadrykę(gd, promień, promień, położeniePodstawy, wysokość, liczbaSekcji, kolor);
        }

        static public Kwadryka StwórzStożek(
            GraphicsDevice gd,
            float promieńDolny, float położeniePodstawy, float wysokość, int liczbaSekcji,
            Color kolor)
        {
            return Kwadryka.StwórzKwadrykę(gd, 0, promieńDolny, położeniePodstawy, wysokość, liczbaSekcji, kolor);
        }

        static public Kwadryka StwórzDysk(
            GraphicsDevice gd,
            float promieńWewnętrzny, float promieńZewnętrzny, float położeniePodstawy, int liczbaSekcji,
            Color kolor)
        {
            return Kwadryka.StwórzKwadrykę(gd, promieńZewnętrzny, promieńWewnętrzny, położeniePodstawy, 0, liczbaSekcji, kolor);
        }

        static public Kwadryka StwórzKoło(
            GraphicsDevice gd,
            float promień, float położeniePodstawy, int liczbaSekcji,
            Color kolor)
        {
            return StwórzKwadrykę(gd, promień, 0, położeniePodstawy, 0, liczbaSekcji, kolor);
        }

        static public Kwadryka StwórzSferę(
            GraphicsDevice gd,
            float promień, int liczbaSekcjiNaRównoleżnikach, int liczbaSekcjiNaPołudnikach,
            Color kolor)
        {
            Kwadryka kwadryka = new Kwadryka(gd, liczbaSekcjiNaRównoleżnikach);
            kwadryka.liczbaPasm = liczbaSekcjiNaPołudnikach;

            List<VertexPositionColor> listaWerteksów = new List<VertexPositionColor>(kwadryka.liczbaPasm * kwadryka.liczbaWerteksówWPaśmie);

            double przyrostKątaTheta = Math.PI / liczbaSekcjiNaPołudnikach;
            for (int i = 0; i <= liczbaSekcjiNaPołudnikach; i++)
            {
                double kątThetaGórny = i * przyrostKątaTheta;
                double kątThetaDolny = (i + 1) * przyrostKątaTheta;
                float wysokośćGórna = (float)(promień * Math.Cos(kątThetaGórny));
                float wysokośćDolna = (float)(promień * Math.Cos(kątThetaDolny));
                float promieńGórny = (float)(promień * Math.Sin(kątThetaGórny));
                float promieńDolny = (float)(promień * Math.Sin(kątThetaDolny));

                VertexPositionColor[] werteksyPasma = BudujTablicęWerteksówŚciętegoStożka(
                    promieńDolny, promieńGórny, wysokośćDolna, wysokośćGórna - wysokośćDolna, liczbaSekcjiNaRównoleżnikach, 
                    kolor);

                listaWerteksów.AddRange(werteksyPasma);
            }

            //return listaWerteksów.ToArray(); //tu był błąd z końca zajęć!!!!!!!!
            kwadryka.ZbudujBuforWerteksów(listaWerteksów.ToArray());
            return kwadryka;
        }

        static public Kwadryka StwórzSferęZBuforemIndeksów(
            GraphicsDevice gd,
            float promień, int liczbaSekcjiNaRównoleżnikach, int liczbaSekcjiNaPołudnikach,
            Color kolor)
        {            
            Kwadryka kwadryka = new Kwadryka(gd, liczbaSekcjiNaRównoleżnikach);
            kwadryka.czySferaZBuforemIndeksów = true;
            kwadryka.liczbaPasm = liczbaSekcjiNaPołudnikach;

            List<VertexPositionColor> listaWerteksów = new List<VertexPositionColor>(kwadryka.liczbaPasm / 2 * kwadryka.liczbaWerteksówWPaśmie);
            double przyrostKątaTheta = Math.PI / liczbaSekcjiNaPołudnikach;
            for (int i = 0; i <= liczbaSekcjiNaPołudnikach; i+=2)
            {
                double kątThetaGórny = i * przyrostKątaTheta;
                double kątThetaDolny = (i + 1) * przyrostKątaTheta;
                float wysokośćGórna = (float)(promień * Math.Cos(kątThetaGórny));
                float wysokośćDolna = (float)(promień * Math.Cos(kątThetaDolny));
                float promieńGórny = (float)(promień * Math.Sin(kątThetaGórny));
                float promieńDolny = (float)(promień * Math.Sin(kątThetaDolny));

                switch (i)
                {
                    case 0: kolor = Color.Red; break;
                    case 1: kolor = Color.Green; break;
                    case 2: kolor = Color.Blue; break;
                }

                VertexPositionColor[] werteksyPasma = BudujTablicęWerteksówŚciętegoStożka(
                    promieńDolny, promieńGórny, wysokośćDolna, wysokośćGórna - wysokośćDolna, liczbaSekcjiNaRównoleżnikach,
                    kolor);

                listaWerteksów.AddRange(werteksyPasma);            
            }
            kwadryka.ZbudujBuforWerteksów(listaWerteksów.ToArray());
            
            List<int> listaIndeksów = new List<int>(kwadryka.liczbaPasm * kwadryka.liczbaWerteksówWPaśmie);
            for (int j = 0; j <= liczbaSekcjiNaPołudnikach; j+=2)
            {                
                for (int i = 0; i <= liczbaSekcjiNaRównoleżnikach; i++)
                {
                    int indeks = 2 * i + j * (liczbaSekcjiNaRównoleżnikach + 1);
                    listaIndeksów.Add(indeks);
                    listaIndeksów.Add(indeks + 1);                    
                }
                if (j+1<kwadryka.liczbaPasm)
                {
                    for (int i = 0; i <= liczbaSekcjiNaRównoleżnikach; i++)
                    {
                        int indeks = 2 * i + j * (liczbaSekcjiNaRównoleżnikach + 1);
                        listaIndeksów.Add(indeks + 1);
                        listaIndeksów.Add(indeks + 2 * (liczbaSekcjiNaRównoleżnikach + 1));
                    }
                }
            }
            kwadryka.ZbudujBuforIndeksów(listaIndeksów.ToArray());

            return kwadryka;
        }
        #endregion

        #region Metody
        private void ZbudujBuforWerteksów(VertexPositionColor[] tablicaWerteksów)
        {
            buforWerteksów = new VertexBuffer(gd, VertexPositionColor.VertexDeclaration, tablicaWerteksów.Length, BufferUsage.WriteOnly);
            buforWerteksów.SetData<VertexPositionColor>(tablicaWerteksów);
        }

        private void ZbudujBuforIndeksów(int[] tablicaIndeksów)
        {
            buforIndeksów = new IndexBuffer(gd, IndexElementSize.ThirtyTwoBits, tablicaIndeksów.Length, BufferUsage.WriteOnly);
            buforIndeksów.SetData<int>(tablicaIndeksów);
        }

        public void RysujBezBuforaIndeksów(Effect efekt)
        {
            foreach (EffectPass przebieg in efekt.CurrentTechnique.Passes)
            {
                przebieg.Apply();
                gd.SetVertexBuffer(buforWerteksów);
                for (int i = 0; i < liczbaPasm; i++)
                {
                    gd.DrawPrimitives(PrimitiveType.TriangleStrip, i * liczbaWerteksówWPaśmie, liczbaTrójkątówWPaśmie);
                }
            }
        }

        public void RysujZBuforemIndeksów(Effect efekt)
        {
            foreach (EffectPass przebieg in efekt.CurrentTechnique.Passes)
            {
                przebieg.Apply();
                gd.Indices = buforIndeksów;
                gd.SetVertexBuffer(buforWerteksów);                
                for (int i = 0; i < liczbaPasm; i++)
                {                    
                    //int i = 1;
                    gd.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, liczbaWerteksówWPaśmie, i * liczbaWerteksówWPaśmie, liczbaTrójkątówWPaśmie);                    
                }
            }
        }

        public void Rysuj(Effect efekt)
        {
            //czySferaZBuforemIndeksów = false;
            if (!czySferaZBuforemIndeksów) RysujBezBuforaIndeksów(efekt);
            else RysujZBuforemIndeksów(efekt);
        }
        #endregion        
    }
}
