


#include <iostream.h>
#include "analogck.h"

#include "ui/dsinmem.h"
#include "ui/dsplsurf.h"
#include "ui/shadorec.h"
#include "ui/font.h"


#include "base/date.h"
#include "base/timeofda.h"
#include "base/clntset.h"

#include <math.h>

#ifndef M_PI // Some !#$ compilers don't define M_PI!! :-(
const float M_PI = 3.14159265;
#endif

#if defined(__GNUC__)
template class CL_Binding0<AnalogClock>;
#endif


AnalogClock::AnalogClock (UI_VisualObject* parent, const UI_Rectangle&
                          shape, UI_ViewID id)
    : UI_Canvas (parent, shape, id),
      _bind  (this, &AnalogClock::Update),
      _timer (_bind)
{
    _bitmap = NULL;
}


AnalogClock::~AnalogClock ()
{
    if (_bitmap)
        delete _bitmap;
}


void AnalogClock::SetAlarm (const CL_TimeOfDay& time,
                            const CL_AbstractBinding&  binding)
{
    CL_ClientSet* set = (CL_ClientSet*) _alarmMap [(CL_Object*) &time];
    if (!set) {
        set = new CL_ClientSet;
        _alarmMap.Add (time.Clone(), set);
    }
    set->Add (binding);
}


void AnalogClock::Initialize()
{
    _timer.Start (10000); // Call Update every 10 seconds
}

void AnalogClock::Finalize ()
{
    _timer.Stop();
}



bool AnalogClock::Paint (const UI_Rectangle&)
{
    if (!_bitmap) {
        // First ever paint event
        _bitmap = new UI_Bitmap;
        Update ();
    }
    UI_DisplaySurface& sfc = CreateDisplaySurface ();
    _bitmap->DrawOn (sfc);
    DestroyDisplaySurface ();

    return TRUE;
}


bool AnalogClock::Update ()
{
    if (!_bitmap)
        _bitmap = new UI_Bitmap;
    UI_Rectangle r = Shape();
    CL_TimeOfDay now = CL_TimeOfDay::Now();

    long w = r.Width(), h = r.Height();
    UI_DwgSurfaceInMemory ds (w, h);
    _DoDraw (ds, now);
    _bitmap->CopyFrom (ds, UI_Rectangle (0, 0, w, h));

    CL_GenericMapIterator itr (_alarmMap);
    while (itr.More()) {
        CL_GenericAssoc assoc = itr.Next();
        CL_ClientSet* set  = (CL_ClientSet*) assoc.value;
        CL_TimeOfDay* time = (CL_TimeOfDay*) assoc.key;
        if (time && *time - now <= 15 && set)
            set->NotifyAll (*this);
    }

    Invalidate ();

    return FALSE;
}


bool AnalogClock::_ShapeRectChanged ()
{
    Update ();
    return TRUE;
}


inline float Radians (long degrees)
{
    return M_PI * degrees / 180.0;
}


#if defined(__X_YACL__) || defined(__X_MOTIF__)
const int DateFontSize = 14;// Unix has very small fonts
#else
const int DateFontSize = 9;
#endif

UI_ColorScheme ClockFaceColor  = UIColor_Black,
               ClockMarksColor = UIColor_White;
void AnalogClock::_DoDraw (UI_DrawingSurface& sfc, const CL_TimeOfDay& time)
{
    // Fill the background
    UI_Rectangle outerRect = sfc.DrawingArea();
    sfc.ColorRectangle (UI_Rectangle (0, 0, outerRect.Width(),
                                      outerRect.Height()),
                        ClockFaceColor);

    short dateRectHt = 24;
    short clockFaceWidth = outerRect.Width(),
        clockFaceHeight = outerRect.Height() - dateRectHt;


    // Draw the five-minute marks
    long hAxis = clockFaceWidth/2;
    long vAxis = clockFaceHeight/2;
    long x, y;
    double radians;
    sfc.Brush().Color (ClockMarksColor);
    long biggerAxis = maxl (hAxis, vAxis);
    short bigSide = maxl (4, biggerAxis/20),
        smallSide = maxl(3, biggerAxis/25);

    for (short i = 0; i < 360; i += 30) {
        radians = Radians (i);
        x = hAxis + (hAxis-7) * cos (radians);
        y = vAxis + (vAxis-7) * sin (radians);
        short rectSize = (i % 90) ? smallSide : bigSide;
        sfc.DrawRectangle (UI_Rectangle (x-rectSize/2, y-rectSize/2,
                                         rectSize, rectSize), UID_Fill);
    }


    // Draw the hour hand
    short hour   = time.Hour();
    short minute = time.Minute ();
    float hourAngleDeg = (30 * hour + 270 + minute/2) % 360;
    radians = Radians (hourAngleDeg);
    x = hAxis + hAxis * 0.45 * cos (radians); // Make it a 55%-wide hand
    y = vAxis + vAxis * 0.45 * sin (radians); 
    sfc.Pen().Thickness (3);
    sfc.Pen().Color (ClockMarksColor);
    sfc.DrawLine (UI_Point (hAxis, vAxis), UI_Point (x, y));

    // Draw the minute hand
    float minuteAngleDeg = 270 + 6 * time.Minute();
    radians = Radians (minuteAngleDeg);
    x = hAxis + hAxis * 0.75 * cos (radians); // Make it a 75%-wide hand
    y = vAxis + vAxis * 0.75 * sin (radians); 
    sfc.DrawLine (UI_Point (hAxis, vAxis), UI_Point (x, y));

    // Draw the date
    UI_Rectangle dateRect (0, clockFaceHeight, clockFaceWidth,
                           dateRectHt);
    sfc.Pen().Thickness (1);
    sfc.DrawLine (dateRect.TopLeft(), dateRect.TopRight());
    CL_Date today = CL_Date::Today();
    CL_String shortWkDayName = today.WeekdayName()(0,3);
    CL_String shortMonthName = CL_Date::ShortMonthName (today.Month());
    CL_String s;
    s.AssignWithFormat ("%s, %s %d, %d", shortWkDayName.AsPtr(),
                        shortMonthName.AsPtr(), today.Day(), today.Year());
    sfc.Font() = UI_FontDesc (UIFont_Helvetica, DateFontSize, UIFont_BoldFace);

    dateRect += UI_Vector (0, 2); // Move it down a little
    sfc.WriteString (s.AsPtr(), dateRect, UIText_Center);
}


