using System;
using System.Drawing;
using System.Collections.Generic; // we need aditional classes eg List
using System.Windows.Forms;
using AgkSharp;

namespace _2DParticleEditor
{
    public partial class Editor : Form
    {
        // create a timer for continuous updating
        Timer agkSyncTimer = new Timer();

        // store created emitters
        public List<CEmitter> Effect = new List<CEmitter>();
        public bool ShowZone = true;
        public bool PlaySelected = false;
        public uint ImageId = 0;
        public CEmitter WorkingEmitter = null;

        // store center of screen.
        private float centerX = 0.0f;
        private float centerY = 0.0f;

        Point currentMousePos = new Point() { X = 0, Y = 0 };   // stor mouseposition for displaying and set emitter positions
        uint currentButtonState = 0x00000000;   // store mousebutton states for set emitter positions
        bool effectMove = false;    // effect move action is activated
        bool emitterMove = false;   // emitter move action is activated
        string action = "(none)";   // display which action currently active.

        public Editor()
        {
            // WindowsForms stuff
            InitializeComponent();

            // CurrentEmiiter is also image id
            ImageId = 1;

            // init OpenGL with window handle.
            Agk.InitGL(AGKScreen.Handle);

            // set virtua resolution to the size of window
            Agk.SetVirtualResolution(AGKScreen.Width, AGKScreen.Height);

            centerX = Agk.GetDeviceWidth() / 2.0f;
            centerY = Agk.GetDeviceHeight() / 2.0f;

            // create defaut emitter
            WorkingEmitter = new CEmitter(centerX, centerY);
            Effect.Add(WorkingEmitter);

            // change print font
            Agk.LoadFont(1, "Arial");
            Agk.SetPrintFont(1);
            Agk.SetPrintSize(14);

            // connect timer event for continuous updating. and start the timer.
            agkSyncTimer.Tick += new EventHandler(SyncTimer);
            agkSyncTimer.Interval = 33;     // ~ 30 fps
            agkSyncTimer.Start();

            // update controls to default values
            CheckBox_ShowZone.Checked = ShowZone;
            CheckBox_PlaySelected.Checked = PlaySelected;

            UpDown_Emitter.Minimum = 1;
            UpDown_Emitter.Maximum = Effect.Count;
            UpDown_Emitter.Value = 1;

            UpdateControls();
        }

        // timer event for updating.
        private void SyncTimer(Object sender, EventArgs e)
        {
            if (CheckBox_ShowZone.Checked)
                DrawingEmitterZones();

            Agk.Print(action);
            Agk.Print(string.Format("{0}, {1}", currentMousePos.X, currentMousePos.Y));
            Agk.Sync();
        }

        public bool GetValueFromString(string txt, out float v0, out float v1)
        {
            // result -> return true for success
            bool result = false;

            // set values to default values.
            v0 = 0;
            v1 = 0;

            // split the string with ';' as delimeter
            string[] values = txt.Split(';');

            // parse only the first two strings for the values
            if (values.Length > 0)
                result |= float.TryParse(values[0], out v0);
            if (values.Length > 1)
                result |= float.TryParse(values[1], out v1);

            return result;
        }

        private void UpdateControls()
        {
            int sizex = 0;
            int sizey = 0;

            // particle creation
            if (System.IO.File.Exists(WorkingEmitter.Image))
            {
                PictureBox_Particle.Load(WorkingEmitter.Image);
                sizex = PictureBox_Particle.Image.Width;
                sizey = PictureBox_Particle.Image.Height;
            }
            Label_ImageSize.Text = string.Format("{0} x {1}", sizex, sizey);

            TextBox_Direction.Text = string.Format("{0:0.00}; {1:0.00}",
                WorkingEmitter.DirectionVX,
                WorkingEmitter.DirectionVY);

            TextBox_Angle.Text = WorkingEmitter.Angle.ToString("0.00");
            TextBox_ParticleSize.Text = WorkingEmitter.Size.ToString("0.00");
            CheckBox_Transparency.Checked = WorkingEmitter.Transparency;
            CheckBox_Interpolation.Checked = WorkingEmitter.Interpolation;

            // particle count
            TextBox_Frequency.Text = WorkingEmitter.Frequency.ToString();
            TextBox_Life.Text = WorkingEmitter.Life.ToString("0.00");
            TextBox_MaxParticles.Text = WorkingEmitter.Max.ToString();
            CheckBox_Infinit.Checked = (WorkingEmitter.Max < 0);

            Label_Count.Text = (WorkingEmitter.Frequency * WorkingEmitter.Life).ToString("0.00");

            // particle zone
            TextBox_ZoneX1.Text = WorkingEmitter.X1.ToString("0.00");
            TextBox_ZoneX2.Text = WorkingEmitter.X2.ToString("0.00");
            TextBox_ZoneY1.Text = WorkingEmitter.Y1.ToString("0.00");
            TextBox_ZoneY2.Text = WorkingEmitter.Y2.ToString("0.00");

            // particle rotation
            TextBox_RotRangeMin.Text = WorkingEmitter.RotationMin.ToString("0.00");
            TextBox_RotRangeMax.Text = WorkingEmitter.RotationMax.ToString("0.00");
            CheckBox_FaceDirection.Checked = WorkingEmitter.FaceDirection;

            // particle velocity
            TextBox_VelocityMin.Text = WorkingEmitter.VelocityMin.ToString("0.00");
            TextBox_VelocityMax.Text = WorkingEmitter.VelocityMax.ToString("0.00");

            // particle colorkey
            if (WorkingEmitter.ColorKeys.Count > 0)
            {
                UpDown_ColorKey.Enabled = true;
                TextBox_ColorKeyTime.Enabled = true;
                Button_ColorKeySelect.Enabled = true;
                CheckBox_Interpolation.Enabled = true;

                UpDown_ColorKey.Minimum = 1;
                UpDown_ColorKey.Maximum = WorkingEmitter.ColorKeys.Count;
                UpDown_ColorKey.Value = 1;

                UpdateColorKeyControls(0);
            }
            else
            {
                UpDown_ColorKey.Enabled = false;
                TextBox_ColorKeyTime.Enabled = false;
                Button_ColorKeySelect.Enabled = false;
                CheckBox_Interpolation.Enabled = false;
            }

            // particle scalekey
            if (WorkingEmitter.ScaleKeys.Count > 0)
            {
                UpDown_ScaleKey.Enabled = true;
                TextBox_ScaleKey.Enabled = true;
                TextBox_ScaleKeyTime.Enabled = true;

                UpDown_ScaleKey.Minimum = 1;
                UpDown_ScaleKey.Maximum = WorkingEmitter.ScaleKeys.Count;
                UpDown_ScaleKey.Value = 1;

                UpdateScaleKeyControls(0);
            }
            else
            {
                UpDown_ScaleKey.Enabled = false;
                TextBox_ScaleKey.Enabled = false;
                TextBox_ScaleKeyTime.Enabled = false;
            }

            // particle forcekey
            if (WorkingEmitter.ForceKeys.Count > 0)
            {
                UpDown_ForceKey.Enabled = true;
                TextBox_ForceKeyTime.Enabled = true;
                TextBox_ForceKeyForce.Enabled = true;

                UpDown_ForceKey.Minimum = 1;
                UpDown_ForceKey.Maximum = WorkingEmitter.ForceKeys.Count;
                UpDown_ForceKey.Value = 1;

                UpdateForceKeyControls(0);
            }
            else
            {
                UpDown_ForceKey.Enabled = false;
                TextBox_ForceKeyTime.Enabled = false;
                TextBox_ForceKeyForce.Enabled = false;
            }
        }

        private void UpdateZone()
        {
            // particle zone
            TextBox_ZoneX1.Text = WorkingEmitter.X1.ToString("0.00");
            TextBox_ZoneX2.Text = WorkingEmitter.X2.ToString("0.00");
            TextBox_ZoneY1.Text = WorkingEmitter.Y1.ToString("0.00");
            TextBox_ZoneY2.Text = WorkingEmitter.Y2.ToString("0.00");
        }

        private void DrawingEmitterZones()
        {
            // drawing all zones of the effect (all emitters)
            uint idc = 1;
            foreach (CEmitter emitter in Effect)
            {
                emitter.DrawZone();
                idc++;
            }
        }

        // #region's splits your code in foldable parts (ide dependent)
        #region Particle Creation
        private void Button_LoadImage_Click(object sender, EventArgs e)
        {
            // build the read path from AGK-App base folder.
            string path = Agk.GetReadPath() + "media/";

            // set initial read path for the Dialog
            OpenFileDialog.InitialDirectory = path.Replace('/', '\\');

            // check if the path exists. Is not exists then set the path to 
            // application current directory.
            if (!OpenFileDialog.CheckPathExists)
                OpenFileDialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory() + "media\\";

            // let the dialog box do its job.
            DialogResult result = OpenFileDialog.ShowDialog(this);

            // the dialog box was closed with the OK button.
            if (result == DialogResult.OK)
            {
                // check if image '1' exists. if so then delete image '1'.
                if (Agk.GetImageExists(ImageId) > 0)
                    Agk.DeleteImage(ImageId);

                // build full pathname. build the full path name with prefixed
                // "raw:" for direct access to the selected file.
                string file = "raw:" + OpenFileDialog.FileName;

                // check if file exists and load image
                if (Agk.GetFileExists(file) > 0)
                {
                    // load image and set it as particle image if emitter exists
                    Agk.LoadImage(ImageId, file);
                    if (Agk.GetImageExists(ImageId) == 1 && Agk.GetParticlesExists(ImageId) == 1)
                        Agk.SetParticlesImage(ImageId, ImageId);

                    // set the label text
                    Label_ImageSize.Text = Agk.GetImageWidth(1).ToString() + " x " + Agk.GetImageHeight(1).ToString();

                    // load the image in the picture box.
                    PictureBox_Particle.Load(OpenFileDialog.FileName);

                    // store image filename in the emitter class
                    WorkingEmitter.Image = OpenFileDialog.FileName;
                }
            }
        }

        private void TrackBar_Angle_Scroll(object sender, EventArgs e)
        {
            // set TextBox to the value of TrackBar
            TextBox_Angle.Text = TrackBar_Angle.Value.ToString("0.00");
        }

        private void TextBox_Angle_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox. 
            float.TryParse(TextBox_Angle.Text, out float val);

            // crop the value between 0 and 360
            if (val < 0.0f) val = 0.0f;
            if (val > 360.0f) val = 360.0f;

            // set the new value to TrackBar and TextBox
            TrackBar_Angle.Value = (int)val;
            TextBox_Angle.Text = val.ToString("0.00");
        }


        private void TextBox_Angle_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store angle value in the emitter class
            WorkingEmitter.Angle = val;

            // set particles angle if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesAngle(ImageId, val);
        }

        private void TextBox_Direction_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            GetValueFromString(TextBox_Direction.Text, out float vx, out float vy);

            // set the parsed value to the TextBox
            TextBox_Direction.Text = string.Format("{0:0.00}; {1:0.00}", vx, vy);

            // store direction values in the emitter class
            WorkingEmitter.DirectionVX = vx;
            WorkingEmitter.DirectionVY = vy;
        }

        private void TextBox_Direction_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            GetValueFromString(TextBox_Direction.Text, out float vx, out float vy);

            // store particle direction value in the emitter class
            WorkingEmitter.DirectionVX = vx;
            WorkingEmitter.DirectionVY = vy;

            // set particles direction if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesDirection(ImageId, vx, vy);
        }

        private void TextBox_ParticleSize_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store particle size value in the emitter class
            WorkingEmitter.Size = val;

            // set particles angle if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesSize(ImageId, val);
        }

        private void TextBox_ParticleSize_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(((TextBox)sender).Text, out float val);

            ((TextBox)sender).Text = val.ToString("0.00");
        }


        private void CheckBox_Transparency_CheckedChanged(object sender, EventArgs e)
        {
            // store particle transparency value in the emitter class
            WorkingEmitter.Transparency = CheckBox_Transparency.Checked;

            // set particles angle if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesTransparency(ImageId, Convert.ToInt32(WorkingEmitter.Transparency));
        }

        private void CheckBox_Interpolation_CheckedChanged(object sender, EventArgs e)
        {
            // store particle color interpolation value in the emitter class
            WorkingEmitter.Interpolation = CheckBox_Interpolation.Checked;

            // set particles color interpolation if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesColorInterpolation(ImageId, Convert.ToInt32(WorkingEmitter.Interpolation));
        }

        #endregion

        #region Particle Count
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CheckBox_Infinit_CheckedChanged(object sender, EventArgs e)
        {
            // disable TextBox_MaxParticles if infinite checked. and set 
            // MaxParticles in the emitter class to -1.
            if (CheckBox_Infinit.Checked)
            {
                TextBox_MaxParticles.Enabled = false;
                WorkingEmitter.Max = -1;

                // set particles infinity if emitter exist
                if (Agk.GetParticlesExists(ImageId) == 1)
                    Agk.SetParticlesMax(ImageId, -1);
            }
            else
            {
                TextBox_MaxParticles.Enabled = true;

                int.TryParse(TextBox_MaxParticles.Text, out int val);
                WorkingEmitter.Max = val;

                // set particles infinity if emitter exist
                if (Agk.GetParticlesExists(ImageId) == 1)
                    Agk.SetParticlesAngle(ImageId, val);
            }
        }

        private void TextBox_Frequency_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            int.TryParse(TextBox_Frequency.Text, out int val);

            // crop the value between -360 and 360
            if (val < 1) val = 1;
            if (val > 10000) val = 10000;

            // set the new value to the TextBox
            TextBox_Frequency.Text = val.ToString();

            // calculate the theoretical count of particles
            float.TryParse(TextBox_Life.Text, out float val2);
            Label_Count.Text = (val * val2).ToString("0.00");
        }

        private void TextBox_Frequency_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            int.TryParse(((TextBox)sender).Text, out int val);

            // store frequency value in the emitter class
            WorkingEmitter.Frequency = val;

            // set particles frequency if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesFrequency(ImageId, val);
        }

        private void TextBox_Life_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_Life.Text, out float val);

            // min life is 0.1
            if (val < 0.1) val = 0.1f;

            // set the new value to the TextBox
            TextBox_Life.Text = val.ToString("0.00");

            // calculate the theoretical count of particles
            int.TryParse(TextBox_Frequency.Text, out int val2);
            Label_Count.Text = (val * val2).ToString("0.00");
        }

        private void TextBox_Life_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store life value in the emitter class
            WorkingEmitter.Life = val;

            // set particles live if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesLife(ImageId, val);
        }

        private void TextBox_MaxParticles_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            int.TryParse(TextBox_MaxParticles.Text, out int val);

            // min particles is 1
            if (val < 1) val = 1;

            // set the new value to the TextBox
            TextBox_MaxParticles.Text = val.ToString();
        }

        private void TextBox_MaxParticles_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            int.TryParse(((TextBox)sender).Text, out int val);

            // store max particles value in the emitter class
            WorkingEmitter.Max = val;

            // set particles max if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesMax(ImageId, val);
        }

        #endregion

        #region Particle Zone
        private void TextBox_ZoneX1_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_ZoneX1.Text, out float val);

            // set the new value to the TextBox
            TextBox_ZoneX1.Text = val.ToString("0.00");
        }

        private void TextBox_ZoneX1_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store max particles value in the emitter class
            WorkingEmitter.X1 = val;

            // set particles zone if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesStartZone(ImageId, WorkingEmitter.X1, WorkingEmitter.Y1, WorkingEmitter.X2, WorkingEmitter.Y2);
        }

        private void TextBox_ZoneX2_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_ZoneX2.Text, out float val);

            // set the new value to the TextBox
            TextBox_ZoneX2.Text = val.ToString("0.00");
        }

        private void TextBox_ZoneX2_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store max particles value in the emitter class
            WorkingEmitter.X2 = val;

            // set particles zone if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesStartZone(ImageId, WorkingEmitter.X1, WorkingEmitter.Y1, WorkingEmitter.X2, WorkingEmitter.Y2);
        }

        private void TextBox_ZoneY1_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_ZoneY1.Text, out float val);

            // set the new value to the TextBox
            TextBox_ZoneY1.Text = val.ToString("0.00");
        }

        private void TextBox_ZoneY1_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store max particles value in the emitter class
            WorkingEmitter.Y1 = val;

            // set particles zone if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesStartZone(ImageId, WorkingEmitter.X1, WorkingEmitter.Y1, WorkingEmitter.X2, WorkingEmitter.Y2);
        }

        private void TextBox_ZoneY2_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_ZoneY2.Text, out float val);

            // set the new value to the TextBox
            TextBox_ZoneY2.Text = val.ToString("0.00");
        }

        private void TextBox_ZoneY2_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store max particles value in the emitter class
            WorkingEmitter.Y2 = val;

            // set particles zone if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesStartZone(ImageId, WorkingEmitter.X1, WorkingEmitter.Y1, WorkingEmitter.X2, WorkingEmitter.Y2);
        }
        #endregion

        #region Particle Rotation
        private void TextBox_RotRangeMin_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_RotRangeMin.Text, out float val);

            // set the new value to the TextBox
            TextBox_RotRangeMin.Text = val.ToString("0.00");
        }

        private void TextBox_RotRangeMin_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            int.TryParse(((TextBox)sender).Text, out int val);

            // store max particles value in the emitter class
            WorkingEmitter.RotationMin = val;

            // set particles rotation if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesRotationRange(ImageId, val, WorkingEmitter.RotationMax);
        }

        private void TextBox_RotRangeMax_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_RotRangeMax.Text, out float val);

            // set the new value to the TextBox
            TextBox_RotRangeMax.Text = val.ToString("0.00");
        }

        private void TextBox_RotRangeMax_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            int.TryParse(((TextBox)sender).Text, out int val);

            // store max particles value in the emitter class
            WorkingEmitter.RotationMax = val;

            // set particles rotation if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesRotationRange(ImageId, WorkingEmitter.RotationMin, val);
        }

        private void CheckBox_FaceDirection_CheckedChanged(object sender, EventArgs e)
        {
            WorkingEmitter.FaceDirection = CheckBox_FaceDirection.Checked;
            TextBox_RotRangeMin.Enabled = !CheckBox_FaceDirection.Checked;
            TextBox_RotRangeMax.Enabled = !CheckBox_FaceDirection.Checked;

            // set particles face direction if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesFaceDirection(ImageId, Convert.ToInt32(WorkingEmitter.FaceDirection));
        }
        #endregion

        #region Particle Velocity
        private void TextBox_VelocityMin_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_VelocityMin.Text, out float val);

            // set the new value to the TextBox
            TextBox_VelocityMin.Text = val.ToString("0.00");
        }

        private void TextBox_VelocityMin_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store max particles value in the emitter class
            WorkingEmitter.VelocityMin = val;

            // set particles velocity if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesVelocityRange(ImageId, val, WorkingEmitter.VelocityMax);
        }

        private void TextBox_VelocityMax_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(TextBox_VelocityMax.Text, out float val);

            // set the new value to the TextBox
            TextBox_VelocityMax.Text = val.ToString("0.00");
        }

        private void TextBox_VelocityMax_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // store max particles value in the emitter class
            WorkingEmitter.VelocityMax = val;

            // set particles velocity if emitter exist
            if (Agk.GetParticlesExists(ImageId) == 1)
                Agk.SetParticlesVelocityRange(ImageId, WorkingEmitter.VelocityMin, val);
        }
        #endregion

        #region Particle ColorKey
        private void Button_ColorKeySelect_Click(object sender, EventArgs e)
        {
            // show the ColorDialog to select a color
            DialogResult result = ColorDialog.ShowDialog(this);
            if (result == DialogResult.OK)
            {
                // if the dialog end with click on ok. set the button to the selected color.
                Button_ColorKeySelect.BackColor = ColorDialog.Color;
                Button_ColorKeySelect.Text = string.Format("{0:X}", ColorDialog.Color.ToArgb());
                float b = ColorDialog.Color.GetBrightness();
                if (b < 0.4)
                    Button_ColorKeySelect.ForeColor = Color.White;
                else
                    Button_ColorKeySelect.ForeColor = Color.Black;

                // set ColorKey-Color to emitter class. struct in c# are 
                // immutable. so we must copy the current ColorKey(struct).
                // change the value and copy back the struct.
                int index = (int)UpDown_ColorKey.Value - 1;
                SColorKey key = WorkingEmitter.ColorKeys[index];
                key.color = (uint)ColorDialog.Color.ToArgb();
                WorkingEmitter.ColorKeys[index] = key;
            }
        }

        private void Button_AddColorKey_Click(object sender, EventArgs e)
        {
            //get last index
            int index = WorkingEmitter.ColorKeys.Count - 1;

            // set defaut values for ColorKey
            uint default_color = 0xffcfcfcf;
            float default_time = 1.0f;

            // enable alle colorkey fields
            UpDown_ColorKey.Enabled = true;
            TextBox_ColorKeyTime.Enabled = true;
            Button_ColorKeySelect.Enabled = true;
            CheckBox_Interpolation.Enabled = true;
            Button_ColorKeySet.Enabled = true;

            // if exists a colorkey then copy last colorkey values 
            // to default values,
            if (index >= 0)
            {
                default_color = WorkingEmitter.ColorKeys[index].color;
                default_time = WorkingEmitter.ColorKeys[index].time + 1.0f;
            }

            // add the deffault colorkey to the colorkey-list
            WorkingEmitter.ColorKeys.Add(new SColorKey()
            {
                color = default_color,
                time = default_time
            });

            // update NumericUpDown-control with new bounds.
            UpDown_ColorKey.Minimum = 1;
            UpDown_ColorKey.Maximum = WorkingEmitter.ColorKeys.Count;
            UpDown_ColorKey.Value = WorkingEmitter.ColorKeys.Count;

            // update other controls with default values
            Color color = Color.FromArgb((int)default_color);
            Button_ColorKeySelect.BackColor = color;
            Button_ColorKeySelect.Text = string.Format("{0:X}", color.ToArgb());
            if (color.GetBrightness() < 0.4)
                Button_ColorKeySelect.ForeColor = Color.White;
            else
                Button_ColorKeySelect.ForeColor = Color.Black;

            TextBox_ColorKeyTime.Text = default_time.ToString("0.00");
        }

        private void TextBox_ColorKeyTime_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(((TextBox)sender).Text, out float val);

            // set the new value to the TextBox
            TextBox_ColorKeyTime.Text = val.ToString("0.00");
        }

        private void TextBox_ColorKeyTime_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // set ColorKey-time to emitter class. struct in c# are 
            // immutable. so we must copy the current ColorKey(struct).
            // change the value and copy back the struct.
            int index = (int)UpDown_ColorKey.Value - 1;
            SColorKey key = WorkingEmitter.ColorKeys[index];
            key.time = val;
            WorkingEmitter.ColorKeys[index] = key;
        }

        private void UpDown_ColorKey_ValueChanged(object sender, EventArgs e)
        {
            // abort if the emitter does not have a color key.
            if (WorkingEmitter.ColorKeys.Count == 0)
                return;

            // get all necessary variables for a better readability
            int index = (int)UpDown_ColorKey.Value - 1;

            // update colorky controls if possible
            if (index < WorkingEmitter.ColorKeys.Count)
                UpdateColorKeyControls(index);
        }

        private void UpdateColorKeyControls(int index)
        {
            SColorKey key = WorkingEmitter.ColorKeys[index];
            Color color = Color.FromArgb((int)key.color);

            // set KeyColor-color to Button.
            Button_ColorKeySelect.BackColor = color;
            Button_ColorKeySelect.Text = string.Format("{0:X}", color.ToArgb());
            if (color.GetBrightness() < 0.4)
                Button_ColorKeySelect.ForeColor = Color.White;
            else
                Button_ColorKeySelect.ForeColor = Color.Black;

            // set KeyColor-time to TextBox
            TextBox_ColorKeyTime.Text = key.time.ToString("0.00");
        }


        private void Button_ColorKeyClear_Click(object sender, EventArgs e)
        {
            // disable all colorkey fields
            UpDown_ColorKey.Enabled = false;
            TextBox_ColorKeyTime.Enabled = false;
            Button_ColorKeySelect.Enabled = false;
            CheckBox_Interpolation.Enabled = false;
            Button_ColorKeySet.Enabled = false;

            // clear the keys by agk and in your own list
            WorkingEmitter.ColorKeys.Clear();
            Agk.ClearParticlesColors(ImageId);

            UpDown_ColorKey.Minimum = -1;
            UpDown_ColorKey.Maximum = -1;
            UpDown_ColorKey.Value = -1;
        }

        private void Button_ColorKeySet_Click(object sender, EventArgs e)
        {
            WorkingEmitter.SetColorKeys(ImageId);
        }
        #endregion

        #region Particle ScaleKey
        private void Button_AddScaleKey_Click(object sender, EventArgs e)
        {
            //get last index
            int index = WorkingEmitter.ScaleKeys.Count - 1;

            // set defaut values for ScaleKey
            float default_time = 1.0f;
            float default_scale = 1.0f;

            // enable alle scalekey fields
            UpDown_ScaleKey.Enabled = true;
            TextBox_ScaleKey.Enabled = true;
            TextBox_ScaleKeyTime.Enabled = true;
            Button_ScaleKeySet.Enabled = true;

            // if exists a scalekey then copy last scalekey values 
            // to default values,
            if (index >= 0)
            {
                default_time = WorkingEmitter.ScaleKeys[index].time + 1.0f;
                default_scale = WorkingEmitter.ScaleKeys[index].scale;
            }

            // add the default scalekey to the scalekey-list
            WorkingEmitter.ScaleKeys.Add(new SScaleKey()
            {
                time = default_time,
                scale = default_scale
            });

            // update NumericUpDown-control with new bounds.
            UpDown_ScaleKey.Minimum = 1;
            UpDown_ScaleKey.Maximum = WorkingEmitter.ScaleKeys.Count;
            UpDown_ScaleKey.Value = WorkingEmitter.ScaleKeys.Count;

            // update other controls with default values
            TextBox_ScaleKeyTime.Text = default_time.ToString("0.00");
            TextBox_ScaleKey.Text = default_scale.ToString("0.00");
        }

        private void TextBox_ScaleKeyTime_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(((TextBox)sender).Text, out float val);

            // set the new value to the TextBox
            TextBox_ScaleKeyTime.Text = val.ToString("0.00");
        }

        private void TextBox_ScaleKeyTime_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // set ScaleKey-time to emitter class. struct in c# are 
            // immutable. so we must copy the current ColorKey(struct).
            // change the value and copy back the struct.
            int index = (int)UpDown_ScaleKey.Value - 1;
            SScaleKey key = WorkingEmitter.ScaleKeys[index];
            key.time = val;
            WorkingEmitter.ScaleKeys[index] = key;
        }

        private void TextBox_ScaleKey_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            float.TryParse(((TextBox)sender).Text, out float val);

            // set the new value to the TextBox
            TextBox_ScaleKey.Text = val.ToString("0.00");
        }

        private void TextBox_ScaleKey_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to int value
            float.TryParse(((TextBox)sender).Text, out float val);

            // set ScaleKey-scale to emitter class. struct in c# are 
            // immutable. so we must copy the current ColorKey(struct).
            // change the value and copy back the struct.
            int index = (int)UpDown_ScaleKey.Value - 1;
            SScaleKey key = WorkingEmitter.ScaleKeys[index];
            key.scale = val;
            WorkingEmitter.ScaleKeys[index] = key;
        }

        private void UpDown_ScaleKey_ValueChanged(object sender, EventArgs e)
        {
            // abort if the emitter does not have a color key.
            if (WorkingEmitter.ScaleKeys.Count == 0)
                return;

            // get all necessary variables for a better readability
            int index = (int)UpDown_ScaleKey.Value - 1;

            // update scalekey controls if possible
            if (index < WorkingEmitter.ScaleKeys.Count)
                UpdateScaleKeyControls(index);
        }

        private void UpdateScaleKeyControls(int index)
        {
            SScaleKey key = WorkingEmitter.ScaleKeys[index];

            // set KeyScale-time to TextBox
            TextBox_ScaleKeyTime.Text = key.time.ToString("0.00");

            // set KeyScale-scale to TextBox
            TextBox_ScaleKey.Text = key.scale.ToString("0.00");
        }

        private void Button_ScaleKeyClear_Click(object sender, EventArgs e)
        {
            // enable alle colorkey fields
            UpDown_ScaleKey.Enabled = false;
            TextBox_ScaleKey.Enabled = false;
            TextBox_ScaleKeyTime.Enabled = false;
            Button_ScaleKeySet.Enabled = false;

            // clear the keys by agk and in your own list
            WorkingEmitter.ScaleKeys.Clear();
            Agk.ClearParticlesScales(ImageId);

            UpDown_ScaleKey.Minimum = -1;
            UpDown_ScaleKey.Maximum = -1;
            UpDown_ScaleKey.Value = -1;
        }

        private void Button_ScaleKeySet_Click(object sender, EventArgs e)
        {
            WorkingEmitter.SetScaleKeys(ImageId);
        }
        #endregion

        #region Particle ForceKey
        private void Button_AddForceKey_Click(object sender, EventArgs e)
        {
            //get last index
            int index = WorkingEmitter.ForceKeys.Count - 1;

            // set defaut values for ForceKey
            float default_start = 0.0f;
            float default_end = 1.0f;
            float default_x = 0.0f;
            float default_y = 1.0f;

            // enable alle forcekey fields
            UpDown_ForceKey.Enabled = true;
            TextBox_ForceKeyTime.Enabled = true;
            TextBox_ForceKeyForce.Enabled = true;
            Button_ForceKeySet.Enabled = true;

            // if exists a forcekey then copy last forcekey values 
            // to default values,
            if (index >= 0)
            {
                default_start = WorkingEmitter.ForceKeys[index].end_time;
                default_end = WorkingEmitter.ForceKeys[index].end_time + 1.0f;
                default_x = WorkingEmitter.ForceKeys[index].force_x;
                default_y = WorkingEmitter.ForceKeys[index].force_y;
            }

            // add the default forcekey to the forcekey-list
            WorkingEmitter.ForceKeys.Add(new SForceKey()
            {
                start_time = default_start,
                end_time = default_end,
                force_x = default_x,
                force_y = default_y
            });

            // update NumericUpDown-control with new bounds.
            UpDown_ForceKey.Minimum = 1;
            UpDown_ForceKey.Maximum = WorkingEmitter.ForceKeys.Count;
            UpDown_ForceKey.Value = WorkingEmitter.ForceKeys.Count;

            // update other controls with default values
            TextBox_ForceKeyTime.Text = string.Format("{0:0.00}; {1:0.00}", default_start, default_end);
            TextBox_ForceKeyForce.Text = string.Format("{0:0.00}; {1:0.00}", default_x, default_y);
        }

        private void TextBox_ForceKeyStart_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            GetValueFromString(((TextBox)sender).Text, out float v0, out float v1);
            //float.TryParse(((TextBox)sender).Text, out float val);

            // set the new value to the TextBox
            TextBox_ForceKeyTime.Text = string.Format("{0:0.00}; {1:0.00}", v0, v1);
            //TextBox_ForceKeyTime.Text = val.ToString("0.00");
        }

        private void TextBox_ForceKeyTime_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to float value
            GetValueFromString(((TextBox)sender).Text, out float v0, out float v1);

            // set ForceKey-start_time to emitter class. struct in c# are 
            // immutable. so we must copy the current ColorKey(struct).
            // change the value and copy back the struct.
            int index = (int)UpDown_ForceKey.Value - 1;
            SForceKey key = WorkingEmitter.ForceKeys[index];
            key.start_time = v0;
            key.end_time = v1;
            WorkingEmitter.ForceKeys[index] = key;
        }

        private void TextBox_ForceKeyX_Leave(object sender, EventArgs e)
        {
            // Try to parse the TextBox.
            GetValueFromString(((TextBox)sender).Text, out float v0, out float v1);

            // set the new value to the TextBox
            TextBox_ForceKeyForce.Text = string.Format("{0:0.00}; {1:0.00}", v0, v1);
        }

        private void TextBox_ForceKeyX_TextChanged(object sender, EventArgs e)
        {
            // convert TextBox text to float value
            GetValueFromString(((TextBox)sender).Text, out float v0, out float v1);

            // set ForceKey-end_time to emitter class. struct in c# are 
            // immutable. so we must copy the current ColorKey(struct).
            // change the value and copy back the struct.
            int index = (int)UpDown_ForceKey.Value - 1;
            SForceKey key = WorkingEmitter.ForceKeys[index];
            key.force_x = v0;
            key.force_y = v1;
            WorkingEmitter.ForceKeys[index] = key;
        }

        private void UpDown_ForceKey_ValueChanged(object sender, EventArgs e)
        {
            // abort if the emitter does not have a forcekey.
            if (WorkingEmitter.ForceKeys.Count == 0)
                return;

            // get all necessary variables for a better readability
            int index = (int)UpDown_ForceKey.Value - 1;

            // update forcekey controls if possible
            if (index < WorkingEmitter.ForceKeys.Count)
                UpdateForceKeyControls(index);
        }

        private void UpdateForceKeyControls(int index)
        {
            SForceKey key = WorkingEmitter.ForceKeys[index];

            // set class values to controls
            TextBox_ForceKeyTime.Text = string.Format("{0:0.00}; {1:0.00}", key.start_time, key.end_time);
            TextBox_ForceKeyForce.Text = string.Format("{0:0.00}; {1:0.00}", key.force_y, key.force_x);
        }

        private void Button_ForceKeyClear_Click(object sender, EventArgs e)
        {
            // enable alle colorkey fields
            UpDown_ForceKey.Enabled = false;
            TextBox_ForceKeyTime.Enabled = false;
            TextBox_ForceKeyForce.Enabled = false;
            Button_ForceKeySet.Enabled = false;

            // clear the keys by agk and in your own list
            WorkingEmitter.ForceKeys.Clear();
            Agk.ClearParticlesForces(ImageId);

            UpDown_ForceKey.Minimum = -1;
            UpDown_ForceKey.Maximum = -1;
            UpDown_ForceKey.Value = -1;
        }

        private void Button_ForceKeySet_Click(object sender, EventArgs e)
        {
            WorkingEmitter.SetForceKeys(ImageId);
        }
        #endregion

        #region Handle Emitter Controls
        private void Button_CreateEmitter_Click(object sender, EventArgs e)
        {
            //WorkingEmitter = new CEmitter();
            WorkingEmitter = CEmitter.Clone(WorkingEmitter);
            Effect.Add(WorkingEmitter);

            UpDown_Emitter.Maximum = Effect.Count;
            UpDown_Emitter.Value = Effect.Count;

            UpdateControls();
        }

        private void Button_Play_Click(object sender, EventArgs e)
        {
            if (CheckBox_PlaySelected.Checked)
            {
                if (Agk.GetParticlesExists(ImageId) == 1)
                {
                    if (Agk.GetParticlesActive(ImageId) == 0)
                    {
                        Agk.SetParticlesActive(ImageId, 1);
                    }
                }
                else
                    WorkingEmitter.Create(ImageId, WorkingEmitter.PositionX, WorkingEmitter.PositionY);
            }
            else
            {
                uint idc = 1;
                foreach (CEmitter emitter in Effect)
                {
                    if (Agk.GetParticlesExists(idc) == 1)
                    {
                        if (Agk.GetParticlesActive(idc) == 0)
                        {
                            Agk.SetParticlesActive(idc, 1);
                        }
                    }
                    else
                        emitter.Create(idc, WorkingEmitter.PositionX, WorkingEmitter.PositionY);

                    idc++;
                }
            }

            CheckBox_PlaySelected.Enabled = false;
        }

        private void Button_Pause_Click(object sender, EventArgs e)
        {
            if (CheckBox_PlaySelected.Checked)
            {
                if (Agk.GetParticlesExists(ImageId) == 1)
                    Agk.SetParticlesActive(ImageId, 0);
            }
            else
            {
                uint idc = 1;
                foreach (CEmitter emitter in Effect)
                {
                    if (Agk.GetParticlesExists(idc) == 1)
                        Agk.SetParticlesActive(idc, 0);
                    idc++;
                }
            }
        }

        private void Button_Stop_Click(object sender, EventArgs e)
        {
            uint idc = 1;
            foreach (CEmitter emitter in Effect)
            {
                if (Agk.GetParticlesExists(idc) == 1)
                    Agk.DeleteParticles(idc);
                idc++;
            }

            CheckBox_PlaySelected.Enabled = true;
        }

        private void UpDown_Emitter_ValueChanged(object sender, EventArgs e)
        {
            ImageId = (uint)UpDown_Emitter.Value;

            WorkingEmitter = Effect[(int)ImageId - 1];
            UpdateControls();
        }

        private void AGKScreen_MouseMove(object sender, MouseEventArgs e)
        {
            currentMousePos = e.Location;
            if ((currentButtonState & (uint)MouseButtons.Left) != 0)
            {
                if (effectMove)
                    WorkingEmitter.SetPosition(ImageId, e.Location);
                else if (emitterMove)
                    WorkingEmitter.SetEmitterAbsolut(ImageId, e.Location);

                UpdateZone();
            }
        }

        private void AGKScreen_MouseDown(object sender, MouseEventArgs e)
        {
            AGKScreen.Focus();
            currentButtonState |= (uint)e.Button;

            if (effectMove)
                WorkingEmitter.SetPosition(ImageId, new Point(currentMousePos.X, currentMousePos.Y));
            else if(emitterMove)
                WorkingEmitter.SetEmitterAbsolut(ImageId, e.Location);

            UpdateZone();
        }

        private void AGKScreen_MouseUp(object sender, MouseEventArgs e)
        {
            currentButtonState ^= (uint)e.Button;
        }

        private void AGKScreen_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            effectMove = false;
            emitterMove = false;

            switch (e.KeyCode)
            {
                case Keys.M:
                    effectMove = true;
                    action = "(move effect)";
                    break;

                case Keys.G:
                    emitterMove = true;
                    action = "(move emitter)";
                    break;

                case Keys.Escape:
                   action = "(none)";
                    break;
            }
        }
        #endregion
    }
}
