using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;


namespace OpenTKTest2
{
    class Program : GameWindow
    {
        public struct Vertex
        {
            public float X, Y, Z;
            public Vertex(float x, float y, float z)
            {
                X = x; Y = y; Z = z;
            }

            //The stride of this vertex
            //3 floats 4 bytes each
            //if you dont like counting bytes you can get this value a few different ways
            //System.Runtime.InteropServices.Marshal.SizeOf(new Vertex());
            //OpenTK.BlittableValueType.StrideOf(new Vertex());
            public const int Stride = sizeof(float)*3;
        }

        float[] triangles2 = 
        {
            -1.0f,-1.0f,-1.0f, // triangle 1 : begin
            -1.0f,-1.0f, 1.0f,
            -1.0f, 1.0f, 1.0f, // triangle 1 : end
    1.0f, 1.0f,-1.0f, // triangle 2 : begin
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f,-1.0f, // triangle 2 : end
    1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f,-1.0f,
    1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f,-1.0f, 1.0f,
    1.0f,-1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f,-1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f, 1.0f,-1.0f,
    -1.0f, 1.0f,-1.0f,
    1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f, 1.0f,
    1.0f,-1.0f, 1.0f
        };

        ushort[] triangles3 = 
        {
            0,0,0, // triangle 1 : begin
            0,0,2,
            0,2,2, // triangle 1 : end
    2,2,0, // triangle 2 : begin
   0,0,0,
    0,2,0, // triangle 2 : end
    2,0,2,
   0,0,0,
    2,0,0,
    2,2,0,
    2,0,0,
    0,0,0,
    0,0,0,
   0,2,2,
   0,2,0,
    2,0,2,
    0,0,2,
    0,0,0,
   0,2,2,
    0,0,2,
   2,0,2,
    2,2,2,
   2,0,0,
   2,2,0,
   2,0,0,
   2,2,2,
   2,0,2,
    2,2,2,
  2,2,0,
   0,2,0,
   2,2,2,
   0,2,0,
    0,2,2,
   2,2,2,
    0,2,2,
   2,0,2
        };

        Matrix4 matrixProjection, matrixModelview;
        float cameraRotation = 0f;
        int x, y;
        byte[,] mybits;
      //  float[] verts;
      //  int counts;
        Color[,] mycolors;
        float camerax, cameray, cameraz;
        System.Timers.Timer timer1;
        private int _vBO; //vertex buffer objects
        private int _IBO; //vertex buffer objects
        private int _cBO; //vertex buffer objects
        private Vertex[] vertices;
        private uint[] indices;
        private byte[] colors;

        protected override void OnLoad(EventArgs e)
        {
            GL.ClearColor(Color.CornflowerBlue);
            GL.Enable(EnableCap.DepthTest);
           // GL.Enable(EnableCap.CullFace);
          

            Bitmap bitsource = Properties.Resources.Test2;
            x = bitsource.Width;
            y = bitsource.Height;
            // System.Windows.Forms.MessageBox.Show(x.ToString() + "," + y.ToString());

            int a, b, c, d, g, f,skipped,h;
            Color t;

            mybits = new byte[x, y];
            mycolors = new Color[x, y];
           // verts = new float[(x * y * 12 * 3 * 3) + 1]; // X*Y for each cube
            vertices = new Vertex[(x * y * 36)];
            indices = new uint[(x * y * 36)];
            colors = new byte[(x * y * 36 * 3 * 4)];
            // counts = new int[x * y];


            for (a = 0; a < x; a++)
            {
                for (b = 0; b < y; b++)
                {
                    t = bitsource.GetPixel(a, b);
                    if (t != Color.FromArgb(255, 255, 255)) { mybits[a, b] = 1; } else { mybits[a, b] = 0; }
                    mycolors[a, b] = t; //mybits[a, b] = 1;
                    //if (a == b) { mybits[a, b] = 1; } else { mybits[a, b] = 0; }
                } //for b
            } //for a

            f = 0; g = 0; skipped = 0;
            for (a = 0; a < x; a++)
            {                
                for (b = 0; b < y; b++)
                {
                    if (mybits[a, b] > 0)
                    {
                        d = 0;
                        for (c = 0; c < 36; c++)
                        {
                            //  GL.Vertex3(triangles2[d] + (a * 2), triangles2[d + 1] + (b * 2), triangles2[d + 2]);
                            // colors[f] =  (mycolors[a, b].ToArgb());
                            colors[g] = mycolors[a, b].R; colors[g + 1] = mycolors[a, b].G; colors[g + 2] = mycolors[a, b].B;
                            colors[g + 3] = mycolors[a, b].A;
                            vertices[f] = new Vertex((triangles3[d] + (a * 2)), (triangles3[d + 1] + (b * 2)), (triangles3[d + 2]));
                            indices[f] = (uint)(f);
                            d = d + 3; g = g + 4;
                            f++;
                        }
                    }
                    else { skipped++; }
                } //for b
            } //for a */

            a = f / (12*3); b = a + skipped; c = x * y; d = indices.Length; h = vertices.Length;
            Console.WriteLine(f.ToString() + "," + h.ToString() + " vertices, " + a.ToString() + " cubes, " + skipped.ToString() + " skipped, total: " + b.ToString() + " expected: " +c.ToString());
            Console.WriteLine("Dimensions: " + x.ToString() + "," + y.ToString() + " indices " + d.ToString());

            GL.GenBuffers(1, out _vBO);
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO);      
            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * Vertex.Stride), vertices, BufferUsageHint.StaticDraw);
       
            GL.GenBuffers(1, out _IBO);            
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _IBO);
            GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indices.Length * sizeof(uint)), indices, BufferUsageHint.StaticDraw);

            GL.GenBuffers(1, out _cBO);
            GL.BindBuffer(BufferTarget.ArrayBuffer, _cBO);
            GL.BufferData(BufferTarget.ArrayBuffer,(IntPtr)(colors.Length * sizeof(byte)), colors, BufferUsageHint.StaticDraw); 
            
            camerax = 1; cameray = (float)(y * 4); cameraz = -4 * x;
            timer1 = new System.Timers.Timer(50); //50 ms
            //main.AddHandler(timer1.Elapsed, Timer1_Tick, false);
            timer1.Elapsed += Timer1_Tick;
            timer1.Enabled = true;
            //  GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        }

        protected void Timer1_Tick(Object source, System.Timers.ElapsedEventArgs e)
        {
            // Console.WriteLine("!");
            var state = OpenTK.Input.Keyboard.GetState();

            if (state[Key.Up])
            {
                //Console.WriteLine("Up");
                cameray = cameray + 1;
                Console.WriteLine(camerax.ToString() + " " + cameray.ToString() + " " + cameraz.ToString());
            } //Key up

            if (state[Key.Down])
            {
                cameray = cameray - 1;
                Console.WriteLine(camerax.ToString() + " " + cameray.ToString() + " " + cameraz.ToString());
            }

            if (state[Key.Left])
            {
                //Console.WriteLine("Left");
                camerax = camerax + 1;
                Console.WriteLine(camerax.ToString() + " " + cameray.ToString() + " " + cameraz.ToString());
            }

            if (state[Key.Right])
            {
                //Console.WriteLine("Left");
                camerax = camerax - 1;
                Console.WriteLine(camerax.ToString() + " " + cameray.ToString() + " " + cameraz.ToString());
            }

            if (state[Key.PageUp])
            {
                //Console.WriteLine("Left");
                cameraz = cameraz + 1;
                Console.WriteLine(camerax.ToString() + " " + cameray.ToString() + " " + cameraz.ToString());
            }

            if (state[Key.PageDown])
            {
                //Console.WriteLine("Left");
                cameraz = cameraz - 1;
                Console.WriteLine(camerax.ToString() + " " + cameray.ToString() + " " + cameraz.ToString());
            }
            if (state[Key.Space])
            {
                Console.WriteLine("Space");
            }
        }

        protected override void OnResize(EventArgs e)
        {
            GL.Viewport(0, 0, Width, Height);
            matrixProjection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1f, 10000f);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref matrixProjection);
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {           
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            #region Camera

            cameraRotation = (cameraRotation < 360f) ? (cameraRotation + 1f * (float)e.Time) : 0f;
            // if (Rotation < 360) then rotate, otherwise zero

            Matrix4.CreateRotationY(cameraRotation, out matrixModelview);
            // Matrix4.CreateRotationZ(cameraRotation, out matrixModelview);
            //matrixModelview *= Matrix4.LookAt(0f, 0f, -5f, 0f, 0f, 0f, 0f, 1f, 0f);
            //Camera at 0,0,-5 looking at 0,0,0 up is 0,1,0
            // matrixModelview *= Matrix4.LookAt(x/2, 0, -3*x, 0f, x/2, 0f, 0f, 1, 0f);

            matrixModelview *= Matrix4.LookAt(camerax, cameray, cameraz, 0f, 0, 0f, 0, 0, -1f);

            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref matrixModelview);

            #endregion

            #region Draw cubes

            GL.BindBuffer(BufferTarget.ArrayBuffer, _vBO); //Vertex
            GL.EnableClientState(ArrayCap.VertexArray);
            GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
        
           GL.BindBuffer(BufferTarget.ArrayBuffer, _cBO); //Colors
            GL.EnableClientState(ArrayCap.ColorArray);
            GL.ColorPointer(4, ColorPointerType.UnsignedByte, 0, IntPtr.Zero); 
                     
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _IBO); //Indices
            
            GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedInt, 0);

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
            GL.DisableClientState(ArrayCap.VertexArray);
            GL.DisableClientState(ArrayCap.ColorArray);

            #endregion

            SwapBuffers();
        }
        [STAThread]
        private static void Main(string[] args)
        {
            using (Program p = new Program())
            {
                p.Run(60d);
            }
        }
        /*
        win.KeyDown += (sender, e) =>
{
    switch (e.Key)
    {
        case Key.Left:
            // move left
            break;
        ...
        case Key.Space:
            // start charging weapon
            break;
    }
}; */
    
    }
}