using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DGLE2;
using ComponentStyleProvider;

namespace SuperTetris
{
    public partial class Form1 : Form
    {
        //Object type
        public class TObject
        {
            public bool alive, movingX, movingY;
            public TPoint3[] map;
            public Point coord;
            public Point coordf;
            public float angle, frame;

            public TObject()
            {
                /*movingX = false;
                movingY = false;
                map = maptypes[0];
                coord = new Point(rand.Next(6), 0);
                coordf = new Point(coord.X * 40, coord.Y * 40);
                alive = true;
                frame = 0;
                angle = (float)rand.Next(360);*/
            }
            public TObject(int type)
            {
                /*movingX = false;
                movingY = false;
                map = maptypes[type];
                coord = new Point(rand.Next(6), 0);
                coordf = new Point(coord.X * 40, coord.Y * 40);
                alive = true;
                frame = 0;
                angle = (float)rand.Next(360);*/
            }
            public void Draw()
            {
                if (map == null)
                    return;
                TPoint3 p3_1, p3_2;
                p3_1 = new TPoint3(OBJ_SIZE, OBJ_SIZE, OBJ_SIZE);
                p3_2 = new TPoint3(1, 1, 1);

                TRectF rect = new TRectF(coordf.X, coordf.Y, OBJ_SIZE * 4, OBJ_SIZE * 4);
                TColor4 color = new TColor4(150, 150, 0, 150);
                TPoint2 point;
                //pRender2D.DrawRect(ref rect, ref color, E_PRIMITIVE2D_FLAGS.PF_FILL);
                for (int i = 0; i < map.Count(); i++)
                {
                    point = new TPoint2(coordf.X + (int)map[i].x * 40, coordf.Y + (int)map[i].y * 40);
                    rect = new TRectF(point.x, point.y, OBJ_SIZE, OBJ_SIZE);
                    pRender2D.DrawRect(ref rect, ref color, E_PRIMITIVE2D_FLAGS.PF_FILL);
                    //pRender2D.DrawMesh(pMeshAsteroid, pTexAsteroid, ref point, ref p3_1, ref p3_2, ActiveObj.angle, false, 90.0f, E_EFFECT2D_FLAGS.EF_DEFAULT);
                }
            }
            public void DoStep()
            {
                if (coordf.X > coord.X * 40)
                    coordf.X -= 10;
                else if (coordf.X < coord.X * 40)
                    coordf.X += 10;
                else
                    movingX = false;
                if (coordf.Y < coord.Y * 40)
                    coordf.Y += 5;
                else
                    movingY = false;
            }
            public bool GoLeft()
            {
                coord.X--;
                if (Collision(0))
                {
                    coord.X++;
                    return false;
                }
                movingX = true;
                return true;
            }
            public bool GoRight()
            {
                coord.X++;
                if (Collision(1))
                {
                    coord.X--;
                    return false;
                }
                movingX = true;
                return true;
            }
            public bool GoDown()
            {
                coord.Y++;
                if (GoStop())
                {
                    coord.Y--;
                    coordf = new Point(coord.X * 40, coord.Y * 40);
                    for (int i = 0; i < map.Count(); i++)
                        Field.field[(int)map[i].x, (int)map[i].y] = 1;
                    TObject newobj = new TObject();
                    newobj.coord = this.coord;
                    newobj.coordf = this.coordf;
                    newobj.map = this.map;
                    StaticObj.Add(newobj);
                    alive = false;
                    return false;
                }
                movingY = true;
                return true;
            }
            public bool RotateX()
            {
                TPoint3 newpoint;
                for (int i = 0; i < ActiveObj.map.Count(); i++)
                {
                    newpoint.x = (map[i].y * 3) % 4;
                    newpoint.y = map[i].x;
                    newpoint.z = map[i].z;
                    map[i] = newpoint;
                }
                for (int j = 0; j < StaticObj.Count(); j++)
                    for (int k = 0; k < StaticObj[j].map.Count(); k++)
                    {
                        newpoint.x = (StaticObj[j].map[k].y * 3) % 4;
                        newpoint.y = StaticObj[j].map[k].x;
                        newpoint.z = StaticObj[j].map[k].z;
                        StaticObj[j].map[k] = newpoint;
                    }
                return true;
            }
            public bool RotateZ()
            {
                return true;
            }
            public bool GoStop()
            {
                for (int i = 0; i < map.Count(); i++)
                {
                    if (coord.Y + map[i].y > 19)
                        return true;
                    for (int j = 0; j < StaticObj.Count(); j++)
                        for (int k = 0; k < StaticObj[j].map.Count(); k++)
                            if (coord.X + map[i].x == StaticObj[j].coord.X + StaticObj[j].map[k].x &&
                                coord.Y + map[i].y == StaticObj[j].coord.Y + StaticObj[j].map[k].y)
                                return true;
                }
                return false;
            }
            public bool Collision(int side)
            {
                for (int i = 0; i < map.Count(); i++)
                {
                    if (side == 0 && coord.X + map[i].x < 0
                        || side == 1 && coord.X + map[i].x > (SCREEN_WIDTH - OBJ_SIZE) / 40)
                        return true;
                    for (int j = 0; j < StaticObj.Count(); j++)
                        for (int k = 0; k < StaticObj[j].map.Count(); k++)
                            if (coord.Y + map[i].y == StaticObj[j].coord.Y + StaticObj[j].map[k].y &&
                                (side == 0 && coord.X + map[i].x == StaticObj[j].coord.X + StaticObj[j].map[k].x
                                || side == 1 && coord.X + map[i].x == StaticObj[j].coord.X + StaticObj[j].map[k].x))
                                return true;
                }
                return false;
            }

        };
        #region CONST
        static Random rand = new Random();
        
        const string APP_CAPTION = "SpriteEditor";
        const string DLL_PATH = @"..\DGLE2.dll";

        public const uint SCREEN_WIDTH = 600;
        public const uint SCREEN_HEIGHT = 800;

        static TPoint3[][] maptypes = new TPoint3[][]
        {
            new TPoint3[]{              //O
                new TPoint3(1,1,0),
                new TPoint3(2,1,0),
                new TPoint3(1,2,0),
                new TPoint3(2,2,0)
            },
            new TPoint3[]{              //I
                new TPoint3(0,2,0),
                new TPoint3(1,2,0),
                new TPoint3(2,2,0),
                new TPoint3(3,2,0)
            },
            new TPoint3[]{              //S
                new TPoint3(2,1,0),
                new TPoint3(3,1,0),
                new TPoint3(1,2,0),
                new TPoint3(2,2,0)
            },
            new TPoint3[]{              //Z
                new TPoint3(1,1,0),
                new TPoint3(2,1,0),
                new TPoint3(2,2,0),
                new TPoint3(3,2,0)
            },
            new TPoint3[]{              //J
                new TPoint3(1,1,0),
                new TPoint3(1,2,0),
                new TPoint3(2,2,0),
                new TPoint3(3,2,0)
            },
            new TPoint3[]{              //L
                new TPoint3(3,1,0),
                new TPoint3(1,2,0),
                new TPoint3(2,2,0),
                new TPoint3(3,2,0)
            },
            new TPoint3[]{              //T
                new TPoint3(2,1,0),
                new TPoint3(1,2,0),
                new TPoint3(2,2,0),
                new TPoint3(3,2,0)
            }
        };
        //Delegates
        static DSubscriber DInit = null;
        static DSubscriber DFree = null;
        static DSubscriber DProcess = null;
        static DSubscriber DRender = null;

        //Engine main interface
        static IEngineCore pEngineCore = null;

        //Subsystems interfaces
        static IResourceManager pResourceManager = null;
        static IRender pRender = null;
        static IInput pInput = null;

        public static IRender2D pRender2D = null;

        static uint uiCounter = 0;
        static bool bIsGame = false;

        const int OBJ_SIZE = 40;
        #endregion
        //Resources interfaces
        static IMesh pMeshAsteroid = null;
        static ITexture pTexAsteroid = null, pTexExplo = null,
                        pTexPlasma = null, pTexShip = null, pTexSpace = null;
        static ISoundSample pSndExplo = null, pSndPlasma = null;
        static IBitmapFont pFntMain = null;
        static IMusic pMusic = null;
        static IParticleEffect pFxFlame = null;

               
        //Asteroids


        static TField Field = new TField(0);
        static List<TObject> StaticObj = new List<TObject>();
        //public static CObject ActiveObj;
        static TObject ActiveObj = null; 
       

        
       
        public struct TField
        {
            public byte[,] field;
            public TField(int type)
            {
                field = new byte[10,20];
                ActiveObj.alive = false;
            }
            public void Draw()
            {
            }
            public void AddObj()
            {
            }
        }



        static void StartGame()
        {
        }
        
        static void ProcessTitle()
        {           
            pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_SPACE, out bIsGame);
        }

        static void RenderTitle()
        {
            pTexSpace.Draw2D(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);

            TColor4 c = TColor4.White;

            const string txt_1 = "SUPER TETRIS", 
                txt_2 = "AAAAARGHHH!!!",
                txt_3 = "Press \"Space\" to start!";
            uint w = 0, h = 0;

            pFntMain.SetScale((float)(2.0 + Math.Sin((float)uiCounter / 5.0) * 0.25));
            pFntMain.GetTextDimensions(txt_1, ref w, ref h);
            pFntMain.Draw2D((int)(SCREEN_WIDTH - w) / 2, 100, txt_1, ref c, 0, false);
                        
            pFntMain.SetScale(1);
            pFntMain.GetTextDimensions(txt_2, ref w, ref h);
            pFntMain.Draw2D((int)(SCREEN_WIDTH - w) / 2, (int)SCREEN_HEIGHT - 100, txt_2, ref c, 0, false);

            pFntMain.SetScale(1);
            pFntMain.GetTextDimensions(txt_3, ref w, ref h);
            pFntMain.Draw2D((int)(SCREEN_WIDTH - w) / 2, (int)SCREEN_HEIGHT - 200, txt_3, ref c, 0, false);
        }

        static void ProcessGame()
        {
            if (!ActiveObj.alive)
                ActiveObj = new TObject(0/*rand.Next(6)*/);

            bool key_pressed;
            if (uiCounter % 3 == 0)
            {

                pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_LEFT, out key_pressed);
                if (key_pressed)
                    ActiveObj.GoLeft();

                pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_RIGHT, out key_pressed);
                if (key_pressed)
                    ActiveObj.GoRight();

                pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_UP, out key_pressed);
                if (key_pressed)
                {
                    ActiveObj.RotateX();
                }

                pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_SPACE, out key_pressed);
                if (key_pressed)
                    ActiveObj.RotateZ();

                if (!ActiveObj.movingY)
                    ActiveObj.GoDown();
            }
            ActiveObj.DoStep();

            pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_DOWN, out key_pressed);
            if (key_pressed)
                ActiveObj.coordf.Y += 5;
            //Process asteroids

            //ActiveObj.GoDown();
            //


            //Process plasma shots
            /*pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_SPACE, out key_pressed);
            if (key_pressed && uiShotPause > 10)
            {
                shots.Add(new TObject(new TPoint2(stShipCoord.x, stShipCoord.y)));
                pSndPlasma.Play();
                uiShotPause = 0;
            }
            else
                uiShotPause++;*/

            //Collision check

            //if (Field.Intersect())
           // {
                //Create explosion
                //TObject explosion = new TObject(new TPoint2(asteroid_rect.x, asteroid_rect.y));
                //explosion.koeff = asteroids[k].koeff;
                //explos.Add(explosion);

                //pSndExplo.Play();
           // }


            //Process explosions
        }

        static void RenderGame()
        {
            TColor4 c = TColor4.White;

            pTexSpace.Draw2D(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);


            c = TColor4.White;
            pRender2D.SetColorMix(ref c);
            pRender2D.SetBlendMode(E_EFFECT2D_BLENDING_FLAGS.EBF_NORMAL);

            ActiveObj.Draw();
            foreach (TObject obj in StaticObj)
                obj.Draw();
        
        }



        ~Form1()
        {
            Engine.FreeEngine();
        }
        public Form1()
        {
            InitializeComponent();
            Engine.GetEngine(DLL_PATH, out pEngineCore);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            if (pEngineCore != null)
            {
                TEngWindow window = new TEngWindow((UInt32)dpPanel1.Width, (UInt32)dpPanel1.Height, false, false, 1, E_ENG_WINDOW_FLAGS.EWF_DEFAULT);
                pEngineCore.InitializeEngine(dpPanel1.Handle, APP_CAPTION, ref window, 50,
#if DEBUG
 E_ENGINE_INIT_FLAGS.EIF_DEFAULT
#else      
                    E_ENGINE_INIT_FLAGS.EIF_CATCH_UNHANDLED
#endif
);

                DInit = Init;
                DFree = Free;
                DProcess = Process;
                DRender = Render;

                pEngineCore.AddProcedure(E_ENGINE_PROCEDURE_TYPE.EPT_INIT, DInit, this.Handle);
                pEngineCore.AddProcedure(E_ENGINE_PROCEDURE_TYPE.EPT_FREE, DFree, this.Handle);
                pEngineCore.AddProcedure(E_ENGINE_PROCEDURE_TYPE.EPT_PROCESS, DProcess, this.Handle);
                pEngineCore.AddProcedure(E_ENGINE_PROCEDURE_TYPE.EPT_RENDER, DRender, this.Handle);

                pEngineCore.StartEngine(); //Entering engine loop

            }
        }
        static void Init(IntPtr pParam)
        {
            //Get subsystems
            IEngineSubSystem p_sub_sys = null;

            pEngineCore.GetSubSystem(E_ENGINE_SUB_SYSTEM.ESS_RESOURCE_MANAGER, out p_sub_sys);
            pResourceManager = (IResourceManager)p_sub_sys;

            pEngineCore.GetSubSystem(E_ENGINE_SUB_SYSTEM.ESS_RENDER, out p_sub_sys);
            pRender = (IRender)p_sub_sys;

            pEngineCore.GetSubSystem(E_ENGINE_SUB_SYSTEM.ESS_INPUT, out p_sub_sys);
            pInput = (IInput)p_sub_sys;

            pInput.Configure(E_INPUT_CONFIGURATION_FLAGS.ICF_HIDE_CURSOR);

            pRender.GetRender2D(out pRender2D);

            //Load resources
            const string RESOURCE_PATH = @"Data.zip|";

            IEngBaseObj p_base_obj = null;

            pResourceManager.Load(RESOURCE_PATH + "asteroid.dmd", out p_base_obj, 0);
            pMeshAsteroid = (IMesh)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "asteroid.jpg", out p_base_obj, 0);
            pTexAsteroid = (ITexture)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "explo.bmp", out p_base_obj, 0);
            pTexExplo = (ITexture)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "plasma.bmp", out p_base_obj, 0);
            pTexPlasma = (ITexture)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "ship.png", out p_base_obj, 0);
            pTexShip = (ITexture)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "space.bmp", out p_base_obj, 0);
            pTexSpace = (ITexture)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "expl.wav", out p_base_obj, 0);
            pSndExplo = (ISoundSample)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "plasma.wav", out p_base_obj, 0);
            pSndPlasma = (ISoundSample)p_base_obj;

            pResourceManager.Load("Bauhaus 93_24.dft", out p_base_obj, 0);
            pFntMain = (IBitmapFont)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "music.mid", out p_base_obj, 0);
            pMusic = (IMusic)p_base_obj;

            pResourceManager.Load(RESOURCE_PATH + "flame.dpt", out p_base_obj, 0);
            pFxFlame = (IParticleEffect)p_base_obj;

            StartGame();
            //Start playing music
            //pMusic.Play(true);
        }

        static void Free(IntPtr pParam)
        {
            //pFxFlame.KillAllEmitters();
        }

        static void Process(IntPtr pParam)
        {
            if (bIsGame)
                ProcessGame();
            else
                ProcessTitle();

            bool key_pressed;
            
            pInput.GetKey(E_KEYBOARD_KEY_CODES.KEY_ESCAPE, out key_pressed);
            if (key_pressed) 
                pEngineCore.QuitEngine();

            uiCounter++;
        }

        static void Render(IntPtr pParam)
        {
            pRender2D.Begin2D();

            if (bIsGame)
                RenderGame();
            else
                RenderTitle();

            pRender2D.End2D();
        }
    }
}
