using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AgkSharp;

namespace Workaround
{
    public struct SCamera
    {
        public float AngleX;
        public float AngleY;
        public float AngleZ;
        public float FOV;
        public float OrthoWidth;
    }

    public partial class AgkFormTemplate : Form
    {
        bool init = false;
        System.Windows.Forms.Timer agkSyncTimer = new System.Windows.Forms.Timer();
        private bool move_cam;
        private System.Drawing.Point down_pos;

        SCamera[] Cameras { get; set; } = new SCamera[4];
        int CurrentCamera = 0;
        PictureBox CurrentPicbox = null;

        public AgkFormTemplate()
        {
            InitializeComponent();
        }

        private void ButtonUp_Click(object sender, EventArgs e)
        {
            Agk.SetSpritePosition(1, Agk.GetSpriteX(1), Agk.GetSpriteY(1) - 10);
        }

        private void ButtonDown_Click(object sender, EventArgs e)
        {
            Agk.SetSpritePosition(1, Agk.GetSpriteX(1), Agk.GetSpriteY(1) + 10);
        }

        private void ButtonLeft_Click(object sender, EventArgs e)
        {
            Agk.SetSpritePosition(1, Agk.GetSpriteX(1) - 10, Agk.GetSpriteY(1));
        }

        private void ButtonRight_Click(object sender, EventArgs e)
        {
            Agk.SetSpritePosition(1, Agk.GetSpriteX(1) + 10, Agk.GetSpriteY(1));
        }

        private void AgkFormTemplate_Shown(object sender, EventArgs e)
        {
            if (!init)
            {
                Agk.InitGL(AGKScreen.Handle);
                init = true;

                Agk.SetVirtualResolution(AGKScreen.Width, AGKScreen.Height);

                Agk.CreateObjectBox(1, 10, 10, 10);

                // create four cameras for the four windows.
                Cameras[0] = new SCamera()
                {
                    AngleX = Agk.GetCameraAngleX(1),
                    AngleY = Agk.GetCameraAngleY(1),
                    AngleZ = Agk.GetCameraAngleZ(1),
                    FOV = Agk.GetCameraFOV(1),
                    OrthoWidth = 40.0f
                };

                Cameras[1] = new SCamera()
                {
                    AngleX = -90,
                    AngleY = 0,
                    AngleZ = 0,
                    FOV = 0,
                    OrthoWidth = 40.0f
                };

                Cameras[2] = new SCamera()
                {
                    AngleX = 0,
                    AngleY = -90,
                    AngleZ = 0,
                    FOV = 0,
                    OrthoWidth = 40.0f
                };

                Cameras[3] = new SCamera()
                {
                    AngleX = 0,
                    AngleY = 0,
                    AngleZ = 0,
                    FOV = 0,
                    OrthoWidth = 40.0f
                };

                // set current camera and window
                CurrentCamera = 0;
                CurrentPicbox = pictureBoxBR;

                agkSyncTimer.Tick += new EventHandler(SyncTimer);
                agkSyncTimer.Interval = 33;     // ~ 30 fps
                agkSyncTimer.Start();

                this.MouseWheel += new MouseEventHandler(this.OnMouseWheel);

                // put the render window at the top.
                AGKScreen.BringToFront();

                // initial rendering of all windows
                Render(pictureBoxTL);
                Render(pictureBoxTR);
                Render(pictureBoxBL);
                Render(pictureBoxBR);
                Render(pictureBoxBR);
            }
        }

        private void Render(PictureBox picbox)
        {
            CameraUpdate();
            Agk.Sync();

            SwapAgkScreen(picbox);
        }

        private void CameraUpdate()
        {
            // preventing flickering when changing windows
            if (!AGKScreen.Visible)
                AGKScreen.Show();

            Agk.SetCameraRotation(1, Cameras[CurrentCamera].AngleX, Cameras[CurrentCamera].AngleY, Cameras[CurrentCamera].AngleZ);
            Agk.SetCameraFOV(1, Cameras[CurrentCamera].FOV);

            Agk.SetCameraPosition(1, 0, 0, 0);
            Agk.MoveCameraLocalZ(1, -25);

            if (Cameras[CurrentCamera].FOV == 0.0f)
                Agk.SetCameraOrthoWidth(1, Cameras[CurrentCamera].OrthoWidth);
        }

        private void OnMouseWheel(object sender, MouseEventArgs e)
        {
            Cameras[CurrentCamera].FOV += (e.Delta / 10.0f);

            if (Cameras[CurrentCamera].FOV < 7.5f)
                Cameras[CurrentCamera].FOV = 7.5f;
            else if (Cameras[CurrentCamera].FOV > 170.0f)
                Cameras[CurrentCamera].FOV = 170.0f;
        }

        private void SyncTimer(Object sender, EventArgs e)
        {
            CameraUpdate();
            Agk.Sync();
        }

        private void AGKScreen_MouseDown(object sender, MouseEventArgs e)
        {
            move_cam = true;
            down_pos = e.Location;
        }

        private void AGKScreen_MouseMove(object sender, MouseEventArgs e)
        {
            if (!move_cam)
                return;

            Cameras[CurrentCamera].AngleY += (e.X - down_pos.X);
            Cameras[CurrentCamera].AngleX += (e.Y - down_pos.Y);

            down_pos = e.Location;
        }

        private void AGKScreen_MouseUp(object sender, MouseEventArgs e)
        {
            move_cam = false;
        }

        private void pictureBox_MouseEnter(object sender, EventArgs e)
        {
            SwapAgkScreen((PictureBox)sender);
        }

        private void SwapAgkScreen(PictureBox picbox)
        {
            // get the position and boundary of the AGK render winow
            Rectangle bounds = AGKScreen.ClientRectangle;
            Point topleft = AGKScreen.PointToScreen(new Point(bounds.Left, bounds.Top));

            // take a screenshot of this area
            Bitmap bmp = new Bitmap(bounds.Width, bounds.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.CopyFromScreen(topleft, Point.Empty, bounds.Size);

            // for debugging only 
            //bmp.Save("g://debug_img.jpg", ImageFormat.Jpeg);

            // put the screenshot as an image in the PictureBox.
            CurrentPicbox.Image = bmp;

            // prevent flickering when changing windows. Will be set to visible
            // again during Camera update.
            AGKScreen.Hide();

            // get the position and size of the new window
            Point panelL = picbox.Parent.Location;
            Size panelS = picbox.Parent.Size;

            // set the position and size of the render-window to the new 
            // position and cover the PictureBox with the render window.
            AGKScreen.Location = panelL;
            AGKScreen.Size = panelS;

            // set the current camera using the given PictureBox
            CurrentPicbox = picbox;
            if (picbox == pictureBoxBR)
                CurrentCamera = 0;
            if (picbox == pictureBoxTL)
                CurrentCamera = 1;
            if (picbox == pictureBoxTR)
                CurrentCamera = 2;
            if (picbox == pictureBoxBL)
                CurrentCamera = 3;

            // update size and resolution of agk.
            Agk.UpdateDeviceSize((uint)AGKScreen.Size.Width, (uint)AGKScreen.Size.Height);
            Agk.SetVirtualResolution(AGKScreen.Size.Width, AGKScreen.Size.Height);
        }
    }
}
