// Analog Clock in GDI 
// Buffered to prevent flicker.

using System;
using System.Windows;
using System.Windows.Forms;

using Calculus;

using IPlusPlus;
using IPlusPlus.Windows;
using IPlusPlus.Graphics;

class AnalogClock : Form
{
    Timer timer;
    Brush whiteBrush;
    Brush blackBrush;
    Pen whitePen;
    Pen blackPen;
    XForm transform;
    int clientWidth;
    int clientHeight;

    [STAThread]
    public static void Main()
    {
        System.Windows.Forms.application.Run(new AnalogClock());
    }

    AnalogClock()
    {
        timer = new Timer();
        timer.Tick += new EventHandler(OnTimer);
        timer.Interval = 1000;
        timer.Start();

        Text = "Clock";

        whiteBrush = new Brush(StandardBrush.white);
        blackBrush = new Brush(StandardBrush.Black);

        whitePen = new Pen(StandardPen.white);
        blackPen = new Pen(StandardPen.Black);

        OnResize(this, null);
        OnTimer(this, null);

        Paint += OnPaint;
        Resize += OnResize;
    }

    private void OnTimer(Object myObject,
                         EventArgs myEventArgs)
    {
        Invalidate();
    }

    void OnPaint(object source, PaintEventArgs args)
    {
        DeviceContext deviceContext = new DeviceContext(this);
        DeviceContext memoryDeviceContext = new DeviceContext(deviceContext, DeviceContextType.Memory);
        Bitmap deviceBitmap = new Bitmap(deviceContext, new Point(clientWidth, clientHeight));
        memoryDeviceContext.Bitmap = deviceBitmap;
        memoryDeviceContext.Brush = whiteBrush;
        memoryDeviceContext.Pen = whitePen;
        memoryDeviceContext.DrawRectangle(0, 0, clientWidth, clientHeight);
        DrawFace(memoryDeviceContext);
        DrawHands(memoryDeviceContext);
        deviceContext.BitBlockTransfer(0, 0, clientWidth, clientHeight, memoryDeviceContext, 0, 0, (UInt32)RasterOperation.SourceCopy);
    }

    void OnResize(object source, EventArgs args)
    {
        clientWidth = ClientRectangle.Width;
        clientHeight = ClientRectangle.Height;
        int Minimum = clientWidth < clientHeight ? clientWidth : clientHeight;
        transform = new XForm((double)Minimum / (double)2000, 0, 0, -(double)Minimum / (double)2000, clientWidth / 2, clientHeight / 2);
        Invalidate();
    }

    IPlusPlus.Point RotatePoint(IPlusPlus.Point point,
                                int angle)
    {
        double Radians = 2 * Maths.Pi * angle / 360;

        double Sine = Math.Sin(Radians);
        double Cosine = Math.Cos(Radians);

        XForm Rotation = new XForm((double)Cosine, (double)Sine, (double)-Sine, (double)Cosine);

        return Rotation * point;
    }

    Array<Point> RotatePoints(Array<Point> points,
                                     int angle)
    {
        Array<Point> Out = new Array<Point>();
        int Length = (int)points.Length;
        for (int i = 0; i < Length; i++)
            Out[i] = RotatePoint(points[i], angle);
        return Out;
    }

    void DrawFace(DeviceContext deviceContext)
    {
        deviceContext.Brush = blackBrush;

        for (int Angle = 0; Angle < 360; Angle += 6)
        {
            Point Dot = new Point(0, 900);
            Dot = RotatePoint(Dot, Angle);

            int Size = Angle % 5 == 0 ? 100 : 33;
            Point Box = new Point(Size, Size);

            Point LowerLeft = Dot - Box / 2;
            Point UpperRight = LowerLeft + Box;

            Rectangle Out = transform * new Rectangle(LowerLeft, UpperRight);

            deviceContext.DrawEllipse(Out.A.X, Out.A.Y, Out.B.X, Out.B.Y);
        }
    }

    void DrawHands(DeviceContext deviceContext)
    {
        Array<Point> Hour = new Array<Point>() { new Point(0, -150), new Point(100, 0), new Point(0, 600), new Point(-100, 0), new Point(0, -150) };
        Array<Point> Minute = new Array<Point>() { new Point(0, -200), new Point(50, 0), new Point(0, 800), new Point(-50, 0), new Point(0, -200) };
        Array<Point> Second = new Array<Point>() { new Point(0, 0), new Point(0, 800) };

        DateTime Time = DateTime.Now;

        double AngleHour = (Time.Hour * 30) % 360 + (double)Time.Minute / 2 + (double)Time.Second / 120;
        double AngleMinute = Time.Minute * 6 + (double)Time.Second / 10;
        double AngleSecond = Time.Second * 6;

        deviceContext.Pen = blackPen;

        Hour   = transform * RotatePoints(Hour,   (int)AngleHour);
        Minute = transform * RotatePoints(Minute, (int)AngleMinute);
        Second = transform * RotatePoints(Second, (int)AngleSecond);

        deviceContext.DrawPolygon(Hour);
        deviceContext.DrawPolygon(Minute);
        deviceContext.DrawLines(Second);
    }

    protected override void OnPaintBackground(PaintEventArgs e) { }
}