using System;
using System.Collections.Generic;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Diagnostics;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using EZGL2D;

namespace EZGL2Dexample2Dwith3D
{
    public class GL2Dexample : GameWindow  // inherit the GameWindow class
    {
        const int SCREEN_WIDTH = 800;       // screen dimension
        const int SCREEN_HEIGHT = 600;

        int cubeDisplayList;                // out display list 
        
        Stopwatch timer = new Stopwatch();  // timer used for animation
        int frame;                          // frame counter
        GL2D.SpriteGL magicSprite;          // our sprite


        // Init screen
        public GL2Dexample()
            : base(SCREEN_WIDTH, SCREEN_HEIGHT)
        {

        }

        // this is where you would initialize everything
        // like loading textures, setting up OpenGL screen, etc.
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            timer.Start();

            VSync = VSyncMode.On;                           // force vertical synching
            Title = "EZGL2D is too easy to use it hurts!";  // set the title
            //WindowBorder = WindowBorder.Hidden;           // hide the window

            // Load our sprite
            magicSprite = GL2D.LoadSprite("data/EZGL2Dexample2Dwith3D/magic.png", (int)TextureMinFilter.Linear);

            initCubeDisplayList();      // initialize our display list

        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            GL2D.Screen2D(Width, Height);  // reset the screen every resize
        }

        // this is where all your game logic
        // should be done. (Input, Entity movement, collisions, etc)
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            base.OnUpdateFrame(e);
            
            
            // get input
            if (Keyboard[OpenTK.Input.Key.Escape])
            {
                GL.DeleteLists(cubeDisplayList, 1);
                this.Exit();
                //Console.WriteLine("FPS = {0}", frame / (timer.ElapsedMilliseconds / 1000));
                return;
            }
        }

        // This is where all rendering/drawing should be done
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            base.OnRenderFrame(e);

            frame++;
            
            double time = timer.ElapsedMilliseconds * 0.001;   // get timer in seconds
                
            GL2D.ClearScreen();   // clear the screen

            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);  // draw only lines 

            GL.Disable(EnableCap.Texture2D);                // disable texturing so we see lines
            GL.Enable(EnableCap.Blend);                     // blend 'em
            GL2D.BlendMode(GL2D.BLEND_MODE.GL2D_GLOW);      // glow it

            // 3D mode is active when we don't call GL2D.Begin2D()

            // Draw the single moving cube
            GL.Color4(Color.Silver);
            GL.PushMatrix();
                GL.Translate(Math.Sin(time * 0.2), Math.Cos(time * 0.7), Math.Cos(time) + Math.Sin(time));        
                GL.Rotate(time * 100, 1, 0, 0);
                GL.Rotate(time * 100, 0, 1, 0);
                GL.Rotate(time * 100, 0, 0, 1);
                DrawCube();
            GL.PopMatrix();
 

            // do some crazy math in preparation for drawing multicubes
            double ti = time * 0.002208;
            double t = ti * 180.5 * (Math.Sin(time / 1150.0) + 360 * Math.Cos(time / 2250.0) + 90 * Math.Sin(time / 1540.0));
            double dt = 0.80130 + 2.0124 * (Math.Sin(time / 30.0) * Math.Sin(time / 140.0) * Math.Cos(time / 340.0));
		
            // draw the multicube
            GL.PushMatrix();
                GL.Scale(10, 10, 10);	// make the next linecube bigger
                // Do some pseudo Inverse Kinematics
                float color = 0.0001f;
                for (int i = 0; i < 512; i++)
                {
                    t += dt;
                    GL.Color4(color, color * 0.7, color * 0.3, color);
                    // Rotate and draw
                    GL.PushMatrix();
                        GL.Rotate(t * 1, 1, 0, 0);
                        GL.Rotate(t * 2, 0, 1, 0);
                        GL.Rotate(t * 3, 0, 0, 1);
                        DrawCube();
                    GL.PopMatrix();

                    // Increase color
                    color += (0.0005232151f * ((i * i * i) * 0.00000012f));
                    // decrease the size of each succeedinng linecube
                    GL.Scale(0.98899999, 0.98899999, 0.98899999);
                }
            GL.PopMatrix();

            // End of 3D mode
            
            
            GL2D.Begin2D();         // start 2D drawing mode

                // draw four timer animated sprites
                GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);
                GL2D.BlendMode(GL2D.BLEND_MODE.GL2D_ALPHA);
                float dx = ((float)Math.Cos(time * 2.0d) + (float)Math.Cos(time * 4.0d)) * 180;
                float dy = ((float)Math.Sin(time * 3.0d) + (float)Math.Cos(time * 1.0d)) * 120;
                float cx = Width / 2;
                float cy = Height / 2;
                GL2D.SpriteRotateScale((int)(cx + dx), (int)(cy + dy), (float)time * 500, 1, 1, GL2D.SpriteGL.FLIP_NONE, magicSprite);
                GL2D.SpriteRotateScale((int)(cx - dx), (int)(cy - dy), (float)time * -500, 1, 1, GL2D.SpriteGL.FLIP_NONE, magicSprite);
                GL2D.SpriteRotateScale((int)(cx + dx), (int)(cy - dy), (float)time * 500, 1, 1, GL2D.SpriteGL.FLIP_NONE, magicSprite);
                GL2D.SpriteRotateScale((int)(cx - dx), (int)(cy + dy), (float)time * -500, 1, 1, GL2D.SpriteGL.FLIP_NONE, magicSprite);

            GL2D.End2D();

            
            this.SwapBuffers();     // copy what we draw to screen

            Thread.Sleep(1);        // sleep so that we don't hog CPU

        }

        // Draws a single cube
        private void DrawCube( )
        {
            GL.CallList(cubeDisplayList);
        }

        // initializes our display list
        private void initCubeDisplayList()
        {
            cubeDisplayList = GL.GenLists(1);
            GL.NewList(cubeDisplayList, ListMode.Compile);
        
            GL.Begin(PrimitiveType.Quads);

            GL.Vertex3(-1.0f, -1.0f, -1.0f);
            GL.Vertex3(-1.0f, 1.0f, -1.0f);
            GL.Vertex3(1.0f, 1.0f, -1.0f);
            GL.Vertex3(1.0f, -1.0f, -1.0f);

            GL.Vertex3(-1.0f, -1.0f, -1.0f);
            GL.Vertex3(1.0f, -1.0f, -1.0f);
            GL.Vertex3(1.0f, -1.0f, 1.0f);
            GL.Vertex3(-1.0f, -1.0f, 1.0f);

            GL.Vertex3(-1.0f, -1.0f, -1.0f);
            GL.Vertex3(-1.0f, -1.0f, 1.0f);
            GL.Vertex3(-1.0f, 1.0f, 1.0f);
            GL.Vertex3(-1.0f, 1.0f, -1.0f);

            GL.Vertex3(-1.0f, -1.0f, 1.0f);
            GL.Vertex3(1.0f, -1.0f, 1.0f);
            GL.Vertex3(1.0f, 1.0f, 1.0f);
            GL.Vertex3(-1.0f, 1.0f, 1.0f);

            GL.Vertex3(-1.0f, 1.0f, -1.0f);
            GL.Vertex3(-1.0f, 1.0f, 1.0f);
            GL.Vertex3(1.0f, 1.0f, 1.0f);
            GL.Vertex3(1.0f, 1.0f, -1.0f);

            GL.Vertex3(1.0f, -1.0f, -1.0f);
            GL.Vertex3(1.0f, 1.0f, -1.0f);
            GL.Vertex3(1.0f, 1.0f, 1.0f);
            GL.Vertex3(1.0f, -1.0f, 1.0f);

            GL.End();

            GL.EndList();

        }

        [STAThread]
        public static void Main()
        {
            using (GL2Dexample example = new GL2Dexample())
            {
                example.Run(60);    // run the game at 60 frames per second\
            }
        }

    }
}
