using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using VoxelIsometricRenderer.Properties;

namespace VoxelIsometricRenderer
{
    public partial class Terrain4 : Form
    {
        public static bool running = true;
        public static List<Voxel> Voxels = new List<Voxel>();
        public static List<int> ChunksToRender = new List<int>();
        public static Thread MainThread;
        public static float scale = 2f;
        public static int FPS = 0;
        public static int Frames = 0;
        public static double TotalRamUsage = 0;
        public static double RamUsage = 0;
        public static int width;
        public static int height;
        public Terrain4()
        {
            InitializeComponent();
            TextureManager.AddBitmap(Resources.grasside);
            TextureManager.AddBitmap(Resources.grasstop);
            TextureManager.AddBitmap(Resources.dirt);
            TextureManager.AddBitmap(Resources.log);
            TextureManager.AddBitmap(Resources.sprucelog);
            TextureManager.AddBitmap(Resources.logtop);
            TextureManager.AddBitmap(Resources.leaves);
            TextureManager.AddBitmap(Resources.spruceleaves);
            TextureManager.AddBitmap(Resources.stone1);
            TextureManager.AddBitmap(Resources.stone2);
            TextureManager.AddBitmap(Resources.stone3);
            TextureManager.AddBitmap(Resources.ore1);
            TextureManager.AddBitmap(Resources.flower);
            TextureManager.AddBitmap(Resources.vine);
            TextureManager.AddBitmap(Resources.grasstall);
            TextureManager.AddBitmap(Resources.grassmed);
            TextureManager.AddBitmap(Resources.BlueSquare);
            TextureManager.AddBitmap(Resources.greenSquare);
            TextureManager.AddBitmap(Resources.RedSquare);
            TextureManager.AddBitmap(Resources.YellowSquare);
            TextureManager.AddBitmap(Resources.PinkSquare);
            TextureManager.AddBitmap(Resources.OrangeSquare);
            TextureManager.AddBitmap(Resources.ColourEdges);
            TextureManager.GenerateVoxelFaces();
            PlayerObject.CreatePlayer(new int[] { 0, 0, 0, 0, 0, 0 }, 0, new BlockState(false, BlockType.Ground, false));
            Chunk TestChunk = ChunkRegistry.IndexChunk(0, 0, 0);
            Chunk TestChunk2 = ChunkRegistry.IndexChunk(0, 1, 0);
            Chunk TestChunk3 = ChunkRegistry.IndexChunk(0, 0, -1);
            Chunk TestChunk4 = ChunkRegistry.IndexChunk(-1, 0, -1);
            Chunk TestChunk5 = ChunkRegistry.IndexChunk(-1, 0, 0);
            Random rnd = new Random();
            for (int i = 0; i < 16; i++) {
                for (int j = 0; j < 16; j++) {
                    for (int k = 0; k < 16; k++) {
                        if(LightSource.IsWithinSphere(3, 4, 7, 4, i, j, k))
                        {
                            TestChunk2.CreateBlock(new int[] { 8,8,8,8,8,8 }, 0, new BlockState(false, BlockType.Ground, true), i, j, k);
                        } else if(i == 4 && k == 4 && j < 6){
                            TestChunk2.CreateBlock(new int[] { 5,5,5,5,6,6 }, 0, new BlockState(false, BlockType.Ground, true), i, j, k);
                        }
                        else TestChunk2.CreateAir(i, j, k);
                        if (LightSource.IsWithinSphere(8, 4, 8, 4, i, j, k)) TestChunk.CreateBlock(new int[] { rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13) }, 0, new BlockState(false, BlockType.Ground, true), i, j, k);
                        else TestChunk.CreateAir(i, j, k);
                        if (LightSource.IsWithinSphere(8, 4, 8, 20, i, j, k)) TestChunk3.CreateBlock(new int[] { rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13) }, 0, new BlockState(false, BlockType.Ground, true), i, j, k);
                        else TestChunk3.CreateAir(i, j, k);
                        if (LightSource.IsWithinSphere(8, 20, 8, 20, i, j, k)) TestChunk4.CreateBlock(new int[] { rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13) }, 0, new BlockState(false, BlockType.Ground, true), i, j, k);
                        else TestChunk4.CreateAir(i, j, k);
                        if (LightSource.IsWithinSphere(8, 20, 8, 4, i, j, k)) TestChunk5.CreateBlock(new int[] { rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13), rnd.Next(0, 13) }, 0, new BlockState(false, BlockType.Ground, true), i, j, k);
                        else TestChunk5.CreateAir(i, j, k);
                    }
                }
            }
            LightingManager.Lights.Add(new LightSource(LightShape.Sphere,9,9,9, 12, 18, 12, new float[] {0.0f,0.0f,1.8f}, 255));
            LightingManager.Lights.Add(new LightSource(LightShape.Sphere, 8, 8, 8, 4,18,4, new float[] {1.8f,0.0f,0.0f},255));
            TestChunk2.GenerateLightMap();
            TestChunk4.GenerateLightMap();
            TestChunk5.GenerateLightMap();
            TestChunk3.GenerateLightMap();
            TestChunk.GenerateLightMap();
            TestChunk.CalculateCull();
            TestChunk3.CalculateCull();
            TestChunk4.CalculateCull();
            TestChunk5.CalculateCull();
            ChunksToRender.Add(2);
            ChunksToRender.Add(3);
            ChunksToRender.Add(4);
            ChunksToRender.Add(0);
            ChunksToRender.Add(1);
            MainThread = new Thread(Runtime);
            MainThread.SetApartmentState(ApartmentState.STA);
            MainThread.Start();
            ResizeDisplay();
        }

        private void Runtime()
        {
            bool up = true;
            bool Down = true;
            double LightingFrameTime = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds;
            double SecondFrameTime = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds;
            double _64ms = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds;
            while (running)
            {
                Display.Refresh();
                if (Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds - LightingFrameTime > 16)
                {
                    PlayerObject.UpdateCamera();
                    LightingFrameTime = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds;
                    LightingManager.UpdateLighting();
                    //LightingManager.Lights[0].SetPosition(PlayerObject.GetPos());
                    //LightingManager.sunAngles[0] += 5f;
                    //LightingManager.sunAngles[1] += 1f;
                }
                if (Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds - _64ms > 64)
                {
                    _64ms = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds;
                    int[] Pos = LightingManager.Lights[0].GetPosition();
                    if (up && Pos[1] < 25)
                    {
                        Pos[1]++;
                    } else if (up) up = false;
                    else if (Pos[1] > -5)
                    {
                        Pos[1]--;
                    }
                    else
                    {
                        up = true;
                    }
                    LightingManager.Lights[0].SetPosition(Pos);
                    //LightingManager.Lights[1].SetPosition(PlayerObject.GetPos());
                }
                if (Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds - SecondFrameTime > 1000)
                {
                    TotalRamUsage = Math.Round(System.Diagnostics.Process.GetCurrentProcess().WorkingSet64 / 1000000.0);
                    RamUsage = Math.Round(GC.GetTotalMemory(false) / 1000000.0);
                    SecondFrameTime = Process.GetCurrentProcess().TotalProcessorTime.TotalMilliseconds;
                    FPS = Frames;
                    Frames = 0;
                }
            }
        }
        private void Draw(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.PixelOffsetMode = PixelOffsetMode.Half;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            g.ScaleTransform(TextureManager.RenderScale, TextureManager.RenderScale);
            //ChunkRegistry.RenderChunk(g, 1);
            ChunksToRender.ForEach(chunk =>
            {
                ChunkRegistry.RenderChunk(g, chunk);
            });
            PlayerObject.Draw(g);
            int[] Positions = BlockRegistry.GetDrawLocation(LightingManager.Lights[0].GetPosition()[0], LightingManager.Lights[0].GetPosition()[1], LightingManager.Lights[0].GetPosition()[2], PlayerObject.GetCameraPos()[0], PlayerObject.GetCameraPos()[1], PlayerObject.GetCameraPos()[2]);
            g.FillEllipse(Brushes.Blue, new Rectangle(Positions[0] + (int)(Terrain4.width / (2 * TextureManager.RenderScale)), Positions[1] + (int)(Terrain4.height / (2 * TextureManager.RenderScale)), TextureManager.VoxelSize/2, TextureManager.VoxelSize/2));
            g.ResetTransform();
            g.DrawString(String.Format("FPS: {0} Ram Usage: {9}MB Ram Allocated: {8}MB\nSunValue: {1}\nMoonValue:{3}\nLightDirection:{4}\nAmbientColourGrading: R{5} G{6} B{7}\nSunAngles:{2}", FPS, LightingManager.SkyLightMetrics[0], String.Format(" x={0}° y={1}°", LightingManager.sunAngles[0], LightingManager.sunAngles[1]), LightingManager.SkyLightMetrics[2], LightingManager.SunAngle, LightingManager.SkyLightMetrics[3], LightingManager.SkyLightMetrics[4], LightingManager.SkyLightMetrics[5], TotalRamUsage, RamUsage), SystemFonts.DefaultFont, Brushes.Red, new PointF(0, 0));
            Frames++;
        }
        public void ResizeDisplay()
        {
            Display.Left = 0;
            Display.Top = 0;
            Display.Width = Width;
            Display.Height = Height -32;
            width = Width;
            height = Height -32;
            PlayerObject.UpdateCameraCentre(Width, Height - 32);
        }
        private void ResizeAspectRatio(object sender, EventArgs e)
        {
            ResizeDisplay();
        }

        private void KillThreads(object sender, FormClosingEventArgs e)
        {
            running = false;
        }

        private void KeyDownEvent(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Add) TextureManager.RenderScale += 0.25f;
            if (e.KeyCode == Keys.Subtract) TextureManager.RenderScale -= 0.25f;
            if (e.KeyCode == Keys.Up)
            {
                LightingManager.sunAngles[1] += 10f;
            }
            if (e.KeyCode == Keys.Down)
            {
                LightingManager.sunAngles[1] -= 10f;
            }
            if (e.KeyCode == Keys.Left)
            {
                LightingManager.sunAngles[0] += 10f;
            }
            if (e.KeyCode == Keys.Right)
            {
                LightingManager.sunAngles[0] += 10f;
            }
            if (e.KeyCode == Keys.W)
            {
                PlayerObject.IncrementPos(2, -1);
            }
            if (e.KeyCode == Keys.S)
            {
                PlayerObject.IncrementPos(2, 1);
            }
            if (e.KeyCode == Keys.A)
            {
                PlayerObject.IncrementPos(0, -1);
            }
            if (e.KeyCode == Keys.D)
            {
                PlayerObject.IncrementPos(0, 1);
            }
            if (e.KeyCode == Keys.Space)
            {
                PlayerObject.IncrementPos(1, 1);
            }
            if (e.KeyCode == Keys.ShiftKey)
            {
                PlayerObject.IncrementPos(1, -1);
            }
        }

        private void KeyUpEvent(object sender, KeyEventArgs e)
        {

        }

        private void Terrain4_Load(object sender, EventArgs e)
        {

        }
    }
}
