using System;
using System.Collections.Generic; // we need aditional classes eg List
using System.Drawing;
using System.Runtime.Serialization.Formatters.Binary; // need for serialization.
using System.IO;
using AgkSharp;

namespace _2DParticleEditor
{
    // we need some structs to hold all property data of the emitter.
    // [Serializable] is needed for the method CEmitter.Clone 
    // to copy the whole class .
    [Serializable]
    // for storing a list of colorkeys
    public struct SColorKey
    {
        public float time;
        public uint color;
    }

    [Serializable]
    // for storing a list of scalekeys
    public struct SScaleKey
    {
        public float time;
        public float scale;
    }

    [Serializable]
    // for storing a list of forcekeys
    public struct SForceKey
    {
        public float start_time;
        public float end_time;
        public float force_x;
        public float force_y;
    }

    [Serializable]
    // the emitter class store all property data for a particleemitter.
    public class CEmitter
    {
        // creation
        public string Image { get; set; } = "";
        public float DirectionVX { get; set; } = 0.0f;
        public float DirectionVY { get; set; } = -10.0f;
        public float Angle { get; set; } = 360.0f;
        public float Size { get; set; } = 1.0f;
        public bool Transparency { get; set; } = true;
        public bool Interpolation { get; set; } = false;

        // count
        public int Frequency { get; set; } = 10;
        public float Life { get; set; } = 3.0f;
        public int Max { get; set; } = -1;

        // zone
        public float X1 { get; set; } = 0.0f;
        public float Y1 { get; set; } = 0.0f;
        public float X2 { get; set; } = 1.0f;
        public float Y2 { get; set; } = 1.0f;

        // rotation
        public float RotationMin { get; set; } = 0.0f;
        public float RotationMax { get; set; } = 0.0f;
        public bool FaceDirection { get; set; } = false;

        // velocity
        public float VelocityMin { get; set; } = 1.0f;
        public float VelocityMax { get; set; } = 1.0f;

        // frame keys
        public List<SColorKey> ColorKeys = new List<SColorKey>();
        public List<SScaleKey> ScaleKeys = new List<SScaleKey>();
        public List<SForceKey> ForceKeys = new List<SForceKey>();

        // position data of the emitter. only for the editor
        public float PositionX { get; set; } = 0.0f;
        public float PositionY { get; set; } = 0.0f;

        // clone / copy the whole emitter class.
        public static T Clone<T>(T other)
        {
            // clone all data of class
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, other);
                ms.Position = 0;

                return (T)formatter.Deserialize(ms);
            }
        }

        // emitter constructor. set the emitter position to x, y
        public CEmitter(float x, float y)
        {
            PositionX = x;
            PositionY = y;
        }

        // create a real agk particle emitter with given id and 
        // at the given position.
        public void Create(uint id, float centerx, float centery)
        {
            // check whether an image is set. if so, load the image into 
            // the same id as the particle id.
            if (Image.Length > 0)
                Agk.LoadImage(id, "raw:" + Image);

            // create an emitter
            Agk.CreateParticles(id, centerx, centery);

            // set the image for the particles.
            if (Agk.GetImageExists(id) == 1)
                Agk.SetParticlesImage(id, id);

            // set all stored data from CEmitter to agk-emitter 
            Agk.SetParticlesSize(id, Size);
            Agk.SetParticlesAngle(id, Angle);
            Agk.SetParticlesColorInterpolation(id, Convert.ToInt32(Interpolation));
            Agk.SetParticlesDirection(id, DirectionVX, DirectionVY);
            Agk.SetParticlesFaceDirection(id, Convert.ToInt32(FaceDirection));
            Agk.SetParticlesFrequency(id, Frequency);
            Agk.SetParticlesLife(id, Life);
            Agk.SetParticlesMax(id, Max);
            Agk.SetParticlesRotationRange(id, RotationMin, RotationMax);
            Agk.SetParticlesStartZone(id, X1, Y1, X2, Y2);
            Agk.SetParticlesTransparency(id, Convert.ToInt32(Transparency));
            Agk.SetParticlesVelocityRange(id, VelocityMin, VelocityMax);

            // set frame keys.
            SetColorKeys(id);
            SetScaleKeys(id);
            SetForceKeys(id);
        }

        // set stored color keys.
        public void SetColorKeys(uint id)
        {
            Agk.ClearParticlesColors(id);

            if (ColorKeys.Count > 0)
            {
                foreach (SColorKey key in ColorKeys)
                {
                    Agk.AddParticlesColorKeyFrame(id, key.time,
                        (key.color & 0x00ff0000) >> 16,
                        (key.color & 0x0000ff00) >> 8,
                        (key.color & 0x000000ff),
                        (key.color & 0xff000000) >> 24);
                }
            }

        }

        // set strored scale keys.
        public void SetScaleKeys(uint id)
        {
            Agk.ClearParticlesScales(id);

            if (ScaleKeys.Count > 0)
            {
                foreach (SScaleKey key in ScaleKeys)
                {
                    Agk.AddParticlesScaleKeyFrame(id, key.time, key.scale);
                }
            }
        }

        // set stored force keys
        public void SetForceKeys(uint id)
        {
            Agk.ClearParticlesForces(id);

            if (ForceKeys.Count > 0)
            {
                foreach (SForceKey key in ForceKeys)
                {
                    Agk.AddParticlesForce(id, key.start_time, key.end_time, key.force_x, key.force_y);
                }
            }
        }

        // draw the particle creation zone as a green box.
        public void DrawZone()
        {
            Agk.DrawBox(X1 + PositionX, Y1 + PositionY, X2 + PositionX, Y2 + PositionY,
                0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0);
        }

        // set new position. if particles currently playing, update 
        // agk-emitter position.
        public void SetPosition(uint id, Point pos)
        {
            if (Agk.GetParticlesExists(id) > 0)
                Agk.SetParticlesPosition(id, pos.X, pos.Y);

            PositionX = pos.X;
            PositionY = pos.Y;
        }

        // set the position of the particle creation zone. pos are 
        // absolute coordinates.
        public void SetEmitterAbsolut(uint id, Point pos)
        {
            Point offset = new Point() { X = pos.X - (int)PositionX, Y = pos.Y - (int)PositionY };
            SetOffset(id, offset);
        }

        // set the position of the particle creation zone. pos are 
        // relative coordinates.
        public void SetOffset(uint id, Point offset)
        {
            float hw = Math.Abs(X2 - X1) / 2.0f;
            float hh = Math.Abs(Y2 - Y1) / 2.0f;

            X1 = offset.X - hw;
            Y1 = offset.Y - hh;
            X2 = offset.X + hw;
            Y2 = offset.Y + hh;

            if (Agk.GetParticlesExists(id) > 0)
            {
                Agk.SetParticlesStartZone(id, X1, Y1, X2, Y2);
            }
        }
    }
}
