#pragma once
#define PI 3.14159265

/*
class Analog_clock
It creates a GUI representing an analog clock
with three indicators: hour, minute, second
and a fancy dial.
*/
using namespace Graph_lib;

class Analog_clock: public Graph_lib::Window {
public:
    Analog_clock (Point xy, const string &label);

private:
    // background image
    Image clock_dial;

    // data representing second, minute and hour
    Line* second_indicator;
    Line* minute_indicator;
    Line* hour_indicator;

    // helper functions
    Point rotate (Point initial, Point pivot, double angle);
    void set_clock ();
    void run_clock ();

    // action functions
    void increment_second ();
    void increment_minute ();
    void increment_hour ();

    // callback functions
    /*
        typedef void* Address;

        template<class W> W& reference_to (Address pw) {
            return *static_cast<W*>(pw);
        }
    */

    static void cb_seconds (Address, Address pw) { reference_to<Analog_clock>(pw).increment_second (); }
    static void cb_minutes (Address, Address pw) { reference_to<Analog_clock>(pw).increment_minute (); }
    static void cb_hours (Address, Address pw) { reference_to<Analog_clock>(pw).increment_hour (); }
};


//------------------------------------------------------------------------------------------------------------------------
// class member implementations
/*
class constructor: Analog_clock()
It initializes a window containing
an image (dial) and three lines
(indicators), together with a
function that runs the clock utilizing
the machine clock.
*/
Analog_clock::Analog_clock (Point xy, const string &label)
    : Window (xy, 480, 460 , label),
    clock_dial {Point(0,0),"aclockimage.jpg"},
    second_indicator (nullptr),
    minute_indicator (nullptr),
    hour_indicator (nullptr)
{
    attach (clock_dial);

    set_clock ();

    run_clock ();
}

// helper function
/*
Member function: rotate ();
Use: -
It is used to rotate the clock indicators
around the center point at an angle
corresponding to second, minute and hour.
angle will have been incremented
*/
Point Analog_clock::rotate (Point initial, Point pivot, double angle) {
    return Point( pivot.x + (cos(angle) * (initial.x - pivot.x)) - (sin(angle) * (initial.y - pivot.y)),
                  pivot.y + (sin(angle) * (initial.x - pivot.x)) + (cos(angle) * (initial.y - pivot.y)));
}

/*
Member function: set_clock ()
Use: -
It initializes the data members
representing clock indicators to
initial value: pointing at 12 o'clock.
*/
void Analog_clock::set_clock () {
    Point clock_center (x_max () / 2. - 2, y_max () / 2.);

    // set seconds
    const int second_indicator_length = 150;
    Point twelve_o_clock_s (x_max () / 2. - 2,  y_max () / 2. - second_indicator_length);
    second_indicator = new Line (clock_center, twelve_o_clock_s);
    second_indicator->set_style (Line_style (Line_style::solid, 2));
    second_indicator->set_color (Color::red);

    // set minutes
    const int minute_indicator_length = 150;
    Point twelve_o_clock_m (x_max () / 2. - 2,  y_max() / 2. - minute_indicator_length);
    minute_indicator = new Line (clock_center, twelve_o_clock_m);
    minute_indicator->set_style (Line_style (Line_style::solid, 8));


    // set hours
    const int hour_indicator_length = 50;
    Point twelve_o_clock (x_max () / 2. - 2,  y_max () / 2. - hour_indicator_length);
    hour_indicator = new Line (clock_center, twelve_o_clock);
    hour_indicator->set_style (Line_style (Line_style::solid, 8));

    // attach in the right order
    attach (*minute_indicator);
    attach (*hour_indicator);
    attach (*second_indicator);
}

/*
Member function: run_clock ()
Use: -
It updates the clock time by
invoking the functions responsible
for the rotation of the indicators
at a specific interval defined with
the help of the functions: clock ()
and sleep ().
*/
void Analog_clock::run_clock () {
    // get real time and set the clock
    // ...

    // run the clock
    while (true) {
        for (auto i = 0; i < 60; i++) {
            for (auto i = 0; i < 60; i++) {
                cb_seconds (0, this);
                Sleep (1000);
            }
            cb_minutes (0, this);
        }
        cb_hours (0, this);
    }
}

// action functions
/*
Member function: increment_second ()
Use: -
It increments the second indicator by
rotating the line that represents it
by an angle of 6 degrees.
*/
void Analog_clock::increment_second () {

    Point center = second_indicator->point (0);
    Point old_time = second_indicator->point (1);

    // rotate 6 degrees (6 degrees x 60 seconds = 360 one rotation)
    double angle_radians = ((6.) * PI) / 180.;
    Point new_time = rotate (old_time, center, angle_radians);

     // delete old indicator, create new one and attach it
     detach (*second_indicator);
     second_indicator = new Line (center, new_time);
     second_indicator->set_style (Line_style (Line_style::solid, 2));
     second_indicator->set_color (Color::red);
     attach (*second_indicator);

    // redraw ();
    draw();
}

/*
Member function: increment_minute ()
Use: -
It increments the minute indicator by
rotating the line that represents it
by an angle of 6 degrees.
*/
void Analog_clock::increment_minute () {
    Point center = minute_indicator->point (0);
    Point old_time = minute_indicator->point (1);

    // rotate 6 degrees (6 degrees x 60 seconds = 360 one rotation)
    double angle_radians = ((6.) * PI) / 180.;
    Point new_time = rotate (old_time, center, angle_radians);

    // delete old indicator, create new one and attach it
    detach (*minute_indicator);
    minute_indicator = new Line (center, new_time);
    minute_indicator->set_style (Line_style (Line_style::solid, 8));

    attach (*minute_indicator);

    // redraw ();
    draw();
}

/*
Member function: increment_hour ()
Use: -
It increments the hour indicator by
rotating the line that represents it
by an angle of 30 degrees.
*/
void Analog_clock::increment_hour () {
    Point center = hour_indicator->point (0);
    Point old_time = hour_indicator->point (1);

    // rotate 6 degrees (6 degrees x 60 seconds = 360 one rotation)
    double angle_radians = ((30.) * PI) / 180.;
    Point new_time = rotate (old_time, center, angle_radians);

    // delete old indicator, create new one and attach it
    detach (*hour_indicator);
    hour_indicator = new Line (center, new_time);
    hour_indicator->set_style (Line_style (Line_style::solid, 8));

    attach (*hour_indicator);

    // redraw ();
    draw ();
}
