using System.Drawing;
using System;
using System.Runtime.Serialization;

namespace OOPLS {

    public delegate void BounceHandler();

    [Serializable]
    public class BouncingBall {
        float x, y, dx, dy, ddx, ddy;
        int radius = 10;
        Rectangle rect;
        Color color;
        public BounceHandler bounce;
        Random rnd;
        const int MAXGRAVITY = 100, MAXSPEED = 150;
        void initialize(int seed)
        {
            rnd = new Random(seed);
            x = y = 2 * radius;
            dx = (float)rnd.NextDouble() * MAXSPEED;
            dy = (float)rnd.NextDouble() * MAXSPEED;
            ddx = MAXGRAVITY - (float)rnd.NextDouble() * 2 * MAXGRAVITY;
            ddy = MAXGRAVITY - (float)rnd.NextDouble() * 2 * MAXGRAVITY;
        }
        public BouncingBall(int seed) {
            initialize(seed);
        }
        public BouncingBall()
        {
            initialize(DateTime.Now.Millisecond);
        }
        public event BounceHandler Bounce {
            add {
                if (bounce == null) bounce = value;
                else bounce += value;
            }
            remove {
                if (bounce != null) bounce -= value;
            }
        }
        public Color BallColor {
            set {
                color = value;
            }
            get {
                return color;
            }
        }
        public int Radius {
            set {
                radius = value;
            }
            get {
                return radius;
            }
        }
        public Rectangle Rect {
            set {
                rect = value;
            }
            get {
                return rect;
            }
        }
        public float X
        {
            get
            {
                return x;
            }
        }
        public float Y
        {
            get
            {
                return y;
            }
        }
        public Point Center
        {
            get
            {
                return new Point((int)x, (int)y);
            }
        }
        void BounceX(int newx) {
            dx = -dx;
            ddx = MAXGRAVITY - (float)rnd.NextDouble() * 2 * MAXGRAVITY;
            ddy = MAXGRAVITY - (float)rnd.NextDouble() * 2 * MAXGRAVITY;
            x = newx;
            if (bounce != null) bounce();
        }
        void BounceY(int newy) {
            dy = -dy;
            ddy = MAXGRAVITY - (float)rnd.NextDouble() * 2 * MAXGRAVITY;
            ddx = MAXGRAVITY - (float)rnd.NextDouble() * 2 * MAXGRAVITY;
            y = newy;
            if (bounce != null) bounce();
        }
        public void Cycle(int msecs)
        {
            x += dx * msecs / 1000.0f;
            y += dy * msecs / 1000.0f;
            if (x - radius < rect.Left) BounceX(rect.Left + radius);
            if (x + radius > rect.Right) BounceX(rect.Right - radius);
            if (y - radius < rect.Top) BounceY(rect.Top + radius);
            if (y + radius > rect.Bottom) BounceY(rect.Bottom - radius);
            dx += ddx * msecs / 1000.0f;
            dy += ddy * msecs / 1000.0f;
        }
        public void Draw(Graphics g) {
            using (Brush fillBrush = new SolidBrush(color)) {
                g.FillEllipse(fillBrush, (int)x - radius, (int)y - radius, 2*radius, 2*radius);
                g.DrawEllipse(Pens.Black, (int)x - radius, (int)y - radius, 2*radius, 2*radius);
            }
        }
    }
}