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

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

//Uwaga! Mozna uogolnic kwadryki wprowadzajac rozne promienie w kierunku x i y przez skalowanie wspolrzednych z tablicy werteksow w metodzie ZbudujBuforWerteksow

namespace ITA106
{
    enum TypKwadryki { ScietyStozek, Walec, Stozek, Dysk, Kolo, Sfera };

    class Kwadryka
    {
        private TypKwadryki typKwadryki;
        private GraphicsDevice gd;
        private VertexBuffer buforWerteksow = null;
        
        private int iloscTrojkatowWPasmie;
        private int iloscPasm = 1;

        #region Metody statyczne
        static private VertexPositionColorNormalTexture[] BudujTabliceWerteksowScietegoStozka(float promienDolny, float promienGorny, float polozeniePodstawy, float wysokosc,int iloscSekcji,Color kolor)
        {
            int iloscWerteksow = 2 * iloscSekcji;
            
            VertexPositionColorNormalTexture[] tablicaWerteksow = new VertexPositionColorNormalTexture[iloscWerteksow];
            float przyrostKata = MathHelper.TwoPi / (iloscSekcji - 1.0f);

            for (int i = 0; i < iloscSekcji; i++)
            {
                float kat = i * przyrostKata;
                Vector3 kierunek = new Vector3((float)Math.Cos(kat), (float)Math.Sin(kat), 0);
                tablicaWerteksow[2 * i].Position = promienDolny * kierunek + polozeniePodstawy * Vector3.UnitZ;
                tablicaWerteksow[2 * i + 1].Position = promienGorny * kierunek + (polozeniePodstawy+wysokosc) * Vector3.UnitZ;
                tablicaWerteksow[2 * i].Color = kolor;
                tablicaWerteksow[2 * i + 1].Color = kolor;
                
                //obliczanie normalnej
                float a=promienDolny-promienGorny;
                float c=(float)Math.Sqrt(a * a + wysokosc * wysokosc);
                float sinTheta = a / c;
                float cosTheta = wysokosc / c;                
                Vector3 normalna = new Vector3((float)Math.Cos(kat)*cosTheta,(float)Math.Sin(kat)*cosTheta,sinTheta);                
                tablicaWerteksow[2 * i].Normal = normalna;
                tablicaWerteksow[2 * i + 1].Normal = normalna;

                //wspolrzedne teksturowania (dla bryl jednopasmowych)
                tablicaWerteksow[2 * i].TextureCoordinate = new Vector2(kat / MathHelper.TwoPi, 1);
                tablicaWerteksow[2 * i + 1].TextureCoordinate = new Vector2(kat / MathHelper.TwoPi, 0);
            }

            return tablicaWerteksow;
        }

        static private Kwadryka StworzKwadryke(GraphicsDevice gd, float promienDolny, float promienGorny, float polozeniePodstawy, float wysokosc, int iloscSekcji, Color kolor)
        {
            Kwadryka kwadryka = new Kwadryka();            
            kwadryka.gd = gd;
            kwadryka.iloscTrojkatowWPasmie = 2 * iloscSekcji;

            VertexPositionColorNormalTexture[] tablicaWerteksow = Kwadryka.BudujTabliceWerteksowScietegoStozka(promienDolny, promienGorny, polozeniePodstawy, wysokosc, iloscSekcji, kolor);
            kwadryka.ZbudujBuforWerteksow(tablicaWerteksow);
            return kwadryka;            
        }

        static public Kwadryka StworzScietyStozek(GraphicsDevice gd, float promienDolny, float promienGorny, float wysokosc, int iloscSekcji, Color kolor)
        {
            Kwadryka kwadryka = Kwadryka.StworzKwadryke(gd, promienDolny, promienGorny, 0, wysokosc, iloscSekcji, kolor);
            kwadryka.typKwadryki = TypKwadryki.ScietyStozek;
            return kwadryka;
        }
        

        static public Kwadryka StworzWalec(GraphicsDevice gd, float promien, float wysokosc, int iloscSekcji, Color kolor)
        {
            Kwadryka kwadryka = Kwadryka.StworzKwadryke(gd, promien, promien, 0, wysokosc, iloscSekcji, kolor);
            kwadryka.typKwadryki = TypKwadryki.Walec;
            return kwadryka;
        }

        static public Kwadryka StworzStozek(GraphicsDevice gd, float promien, float wysokosc, int iloscSekcji, Color kolor)
        {
            Kwadryka kwadryka = Kwadryka.StworzKwadryke(gd, promien, 0, 0, wysokosc, iloscSekcji, kolor);
            kwadryka.typKwadryki = TypKwadryki.Stozek;
            return kwadryka;
        }

        static public Kwadryka StworzDysk(GraphicsDevice gd, float promienWewnetrzny, float promienZewnetrzny, int iloscSekcji,Color kolor)
        {
            Kwadryka kwadryka = Kwadryka.StworzKwadryke(gd, promienZewnetrzny, promienWewnetrzny, 0, 0, iloscSekcji, kolor);
            kwadryka.typKwadryki = TypKwadryki.Dysk;
            return kwadryka;
        }

        static public Kwadryka StworzKolo(GraphicsDevice gd, float promien, int iloscSekcji, Color kolor)
        {
            Kwadryka kwadryka = Kwadryka.StworzKwadryke(gd, promien, 0, 0, 0, iloscSekcji, kolor);
            kwadryka.typKwadryki = TypKwadryki.Kolo;
            return kwadryka;
        }

        static public Kwadryka StworzSfere(GraphicsDevice gd, float promien, int iloscSekcjiNaRownoleznikach, int iloscSekcjiNaPoludnikach, Color kolor)
        {
            Kwadryka kwadryka = new Kwadryka();
            kwadryka.gd = gd;
            kwadryka.typKwadryki = TypKwadryki.Sfera;
            kwadryka.iloscTrojkatowWPasmie = 2 * iloscSekcjiNaRownoleznikach;
            kwadryka.iloscPasm = iloscSekcjiNaPoludnikach;
            
            List<VertexPositionColorNormalTexture> listaWerteksow = new List<VertexPositionColorNormalTexture>(kwadryka.iloscPasm * kwadryka.iloscTrojkatowWPasmie * 2);

            double przyrostKataTheta = Math.PI / iloscSekcjiNaPoludnikach;
            for (int i = 0; i < iloscSekcjiNaPoludnikach; i++)
            {
                double katThetaGorny = i * przyrostKataTheta;
                double katThetaDolny = (i+1) * przyrostKataTheta;
                float wysokoscGorna = (float)(promien * Math.Cos(katThetaGorny));
                float wysokoscDolna = (float)(promien * Math.Cos(katThetaDolny));
                float promienGorny = (float)(promien * Math.Sin(katThetaGorny));
                float promienDolny = (float)(promien * Math.Sin(katThetaDolny));
                //listaWerteksow.AddRange(Kwadryka.BudujTabliceWerteksowScietegoStozka(promienDolny, promienGorny, wysokoscDolna, wysokoscGorna - wysokoscDolna, iloscSekcjiNaRownoleznikach, kolor));

                VertexPositionColorNormalTexture[] werteksyPasma = Kwadryka.BudujTabliceWerteksowScietegoStozka(promienDolny, promienGorny, wysokoscDolna, wysokoscGorna - wysokoscDolna, iloscSekcjiNaRownoleznikach, kolor);
                double przyrostKataPhi=2*Math.PI/iloscSekcjiNaRownoleznikach;
                for (int j = 0; j < iloscSekcjiNaRownoleznikach; j++)
                {
                    double katPhi=j*przyrostKataPhi;
                    werteksyPasma[2 * j].Normal.X = (float)(Math.Cos(katPhi) * Math.Sin(katThetaDolny));
                    werteksyPasma[2 * j].Normal.Y = (float)(Math.Sin(katPhi) * Math.Sin(katThetaDolny));
                    werteksyPasma[2 * j].Normal.Z = (float)(Math.Cos(katThetaDolny));
                    werteksyPasma[2 * j + 1].Normal.X = (float)(Math.Cos(katPhi) * Math.Sin(katThetaGorny));
                    werteksyPasma[2 * j + 1].Normal.Y = (float)(Math.Sin(katPhi) * Math.Sin(katThetaGorny));
                    werteksyPasma[2 * j + 1].Normal.Z = (float)(Math.Cos(katThetaGorny));
                }

                przyrostKataPhi = 2 * Math.PI / (iloscSekcjiNaRownoleznikach - 1);
                for (int j = 0; j < iloscSekcjiNaRownoleznikach; j++)
                {
                    double katPhi = j * przyrostKataPhi;
                    werteksyPasma[2 * j].TextureCoordinate = new Vector2((float)katPhi / MathHelper.TwoPi, (float)katThetaDolny / MathHelper.Pi);
                    werteksyPasma[2 * j + 1].TextureCoordinate = new Vector2((float)katPhi / MathHelper.TwoPi, (float)katThetaGorny / MathHelper.Pi);
                }
                listaWerteksow.AddRange(werteksyPasma);
            }            

            kwadryka.ZbudujBuforWerteksow(listaWerteksow.ToArray());
            return kwadryka;
        }
        #endregion

        private void ZbudujBuforWerteksow(VertexPositionColorNormalTexture[] tablicaWerteksow)
        {
            buforWerteksow = new VertexBuffer(gd, VertexPositionColorNormalTexture.VertexDeclaration, tablicaWerteksow.Count(), BufferUsage.WriteOnly);
            buforWerteksow.SetData<VertexPositionColorNormalTexture>(tablicaWerteksow);
        }

        #region Własności
        public TypKwadryki TypKwadryki
        {
            get
            {
                return typKwadryki;
            }
        }

        public VertexBuffer BuforWerteksow
        {
            get
            {
                return buforWerteksow;
            }
        }

        public int IloscTrojkatowWPasmie
        {
            get
            {
                return iloscTrojkatowWPasmie;
            }
        }

        public int IloscPasm
        {
            get
            {
                return iloscPasm;
            }
        }
        #endregion

        public void Rysuj(Effect efekt)
        {
            foreach (EffectPass pass in efekt.CurrentTechnique.Passes)
            {
                pass.Apply();
                gd.SetVertexBuffer(buforWerteksow);
                for (int i = 0; i < iloscPasm; i++)
                {
                    gd.DrawPrimitives(PrimitiveType.TriangleStrip, i * iloscTrojkatowWPasmie, iloscTrojkatowWPasmie);
                }
            }
        }
    }

    class Lampa
    {
        private GraphicsDevice gd;
        private Kwadryka zbiornik, abazurBok, abazurGora, podstawkaZarowki, zarowka;

        public Lampa(GraphicsDevice gd)
        {
            this.gd = gd;
            zbiornik = Kwadryka.StworzSfere(gd, 0.4f, 100, 100, Color.Green);            
            podstawkaZarowki = Kwadryka.StworzWalec(gd, 0.05f, 0.1f, 100, Color.LimeGreen);
            zarowka = Kwadryka.StworzSfere(gd, 0.1f, 100, 100, Color.Yellow);

            Color kolorAbazura = Color.Lime;
            kolorAbazura.A = 230;
            abazurBok = Kwadryka.StworzScietyStozek(gd, 0.45f, 0.25f, 0.55f, 100, kolorAbazura);
            abazurGora = Kwadryka.StworzDysk(gd, 0.25f, 0.15f, 100, kolorAbazura);          
        }

        public void Rysuj(BasicEffect efekt)
        {
            Matrix kopiaOryginalnejMacierzySwiata = efekt.World;

            efekt.World *= Matrix.CreateTranslation(0, -0.25f, 0);
            zbiornik.Rysuj(efekt);
            efekt.World = kopiaOryginalnejMacierzySwiata * Matrix.CreateRotationX(-MathHelper.PiOver2)*Matrix.CreateTranslation(0,0.15f,0);
            podstawkaZarowki.Rysuj(efekt);
            efekt.World *= Matrix.CreateTranslation(0, 0.15f, 0);
            zarowka.Rysuj(efekt);

            //polprzezroczysty abazur            
            //gd.RenderState.AlphaBlendEnable = true;
            //gd.RenderState.SourceBlend = Blend.SourceAlpha;
            //gd.RenderState.DestinationBlend = Blend.DestinationAlpha;
            //gd.RenderState.BlendFunction = BlendFunction.Add;
            gd.BlendState = BlendState.NonPremultiplied;
            efekt.World = kopiaOryginalnejMacierzySwiata * Matrix.CreateRotationX(-MathHelper.PiOver2);
            abazurBok.Rysuj(efekt);
            efekt.World *= Matrix.CreateTranslation(0, 0.55f, 0);
            abazurGora.Rysuj(efekt);
            //gd.RenderState.AlphaBlendEnable = false;
            gd.BlendState = BlendState.Opaque;
                     
            efekt.World = kopiaOryginalnejMacierzySwiata;      
        }
    }
}
