/********************************************************************
    Constraint-based Graphical Programming in B-Prolog
    %
    clocks that show different local times
    (C) Afany Software
*********************************************************************/
go:-
    cgWindow(Win,"clocks"), Win^topMargin #= 30, Win^leftMargin #= 10, 
    Win^width #= 910, Win^height #= 200,
    clocks(["Beijing","Cairo","LA","London","Moscow","NY","Paris","Sydney","Tokyo"],
           [-12      ,-8     ,4   ,-5      ,-7      ,0        ,-6     ,-14     ,-13], %time differrences from NY
	   Clocks,Win),
    constrainClocks(Clocks),
    packClocks(Clocks),
    showClocks(Clocks),
    handleWindowClosing(Win,Flag),
    timer(Timer,1000),
    timer_start(Timer),
    time_handler(Clocks,Timer,Flag).

clocks([],_,Clocks,Win):-Clocks=[].
clocks([CityName|Names],[Dif|Difs],[Clock|Clocks],Win):-
    Clock=clock(Label,Rect,Circle,Digit,Analog,Dif),
    cgLabel(Label,CityName), Label^fontSize #= 12, Label^fontStyle #= bold,
    cgRectangle(Rect),Rect^fill #= 0,
    Label^width #= 90, 
    cgSetAlignment(Label,center),
    Rect^width #= 90,
    cgCircle(Circle),Circle^fill #= 0,
    Circle^width #= 90,
    cgSameColumn([Label,Rect,Circle]),
    cgSame([Label,Rect,Circle],window,Win),
    %
    digitClock(Digit,Rect,Win),
    %
    analogClock(Analog,Circle,Win),
    clocks(Names,Difs,Clocks,Win).

/* build a digit clock in the area Rect */
digitClock(Digit,Rect,Win):-
    Digit=digit(Hour,Minute,Second,Sep1,Sep2),
    Comps=[Hour,Sep1,Minute,Sep2,Second],
    cgString(Comps,["00",":","00",":","00"]),
    cgSame(Comps,fontSize,12),
    cgSame(Comps,fontStyle,bold),
    Minute^center #= Rect^center,
    cgTable([Comps]),
    cgInside(Comps,Rect),   % whould like to but inefficient
    cgSame(Comps,window,Win).

/* build an analog clock in the area Cicle */
analogClock(Analog,Circle,Win):-
    Analog=analog(Circle,HourP,MinuteP,SecondP,Segs),
    cgLine([HourP,MinuteP,SecondP]),
    HourP^x1 #= Circle^centerX,HourP^y1 #= Circle^centerY,
    MinuteP^x1 #= Circle^centerX,MinuteP^y1 #= Circle^centerY,
    SecondP^x1 #= Circle^centerX,SecondP^y1 #= Circle^centerY,
    %
    length(Segs,60),
    cgLine(Segs),
    cgSame([HourP,MinuteP,SecondP|Segs],window,Win),
    constrainSegs(Segs,0,Circle).

% wait until Circle is packed
constrainSegs(Legs,I,Circle):-
    Circle^x #= X,
    Circle^y #= Y,
    Circle^width #= W,
    constrainSegs(Legs,I,Circle,X,Y,W).

% delay until X,Y,W are instantiated
constrainSegs(Legs,I,Circle,X,Y,W),no_vars_gt(3,0),{ins(X),ins(Y),ins(W),ins(H)} => true. 
constrainSegs(Legs,I,Circle,X,Y,W) => 
    Circle^centerX #= CenterX,
    Circle^centerY #= CenterY,
    computeSegs(Legs,0,Circle,CenterX,CenterY,W).

computeSegs([],I,Circle,CenterX,CenterY,W).
computeSegs([Seg|Segs],I,Circle,CenterX,CenterY,W):-
    Angle is I*(2*pi/60),
    Radius1 is W//2,
    (I mod 5=:=0-> 
     Radius2 is Radius1*2//3;
     Radius2 is Radius1*7//8
     ),
    %
    X1 is CenterX+round(cos(Angle)*Radius1),
    Y1 is CenterY+round(sin(Angle)*Radius1),
    X2 is CenterX+round(cos(Angle)*Radius2),
    Y2 is CenterY+round(sin(Angle)*Radius2),
    Seg^x1 #= X1,
    Seg^y1 #= Y1,
     %
    Seg^x2 #= X2,
    Seg^y2 #= Y2,
     %
    I1 is I+1,
    computeSegs(Segs,I1,Circle,CenterX,CenterY,W).

constrainClocks([]).
constrainClocks([_]).
constrainClocks([C1,C2|Clocks]):-
    C1=clock(Label1,Rect1,Circle1,_,_,_),
    C2=clock(Label2,Rect2,Circle2,_,_,_),
    Label1^y #= Label2^y,
    Rect1^rightX+10 #= Rect2^x,
    Circle1^rightX+10 #= Circle2^x,
    constrainClocks([C2|Clocks]).
    
packClocks([]).
packClocks([clock(Label,Rect,Circle,Digit,Analog,Dif)|Clocks]):-
    cgPack([Label,Rect,Circle]),
    packClocks(Clocks).

showClocks([]).
showClocks([clock(Label,Rect,Circle,Digit,Analog,Dif)|Clocks]):-
    Digit=digit(Hour,Minute,Second,Sep1,Sep2),
    Analog=analog(Circle,HourP,MinuteP,SecondP,Legs),
    cgShow([Label,Rect,Circle]),
    cgShow([Hour,Minute,Second,Sep1,Sep2]),
    cgShow(Legs),
    showClocks(Clocks).

% stop ticking when Flag is instantiated
time_handler(Clocks,Timer,Flag),var(Flag),{time(Timer)} =>
    critical_region(tick_clocks(Clocks)).
time_handler(Clocks,Timer,Flag) => timer_kill(Timer).

tick_clocks([Clock|Clocks]):-
    tick_clock(Clock),
    tick_clocks(Clocks).
tick_clocks([]).

tick_clock(clock(Label,Rect,Circle,Digit,Analog,Dif)):-
    Digit=digit(Hour,Minute,Second,Sep1,Sep2),
    Analog=analog(Circle,HourP,MinuteP,SecondP,Segs),
    time(H,M,S), %get the current time
    LocalH is H-Dif,
    (LocalH<0->LocalH1 is LocalH+24;LocalH1 is LocalH),
    LocalH2 is LocalH1 mod 24,
    number_codes(LocalH2,HString), addZero(HString,HString1), 
    cgSetText(Hour,HString1),
    number_codes(M,MString), addZero(MString,MString1),
    cgSetText(Minute,MString1),
    number_codes(S,SString), addZero(SString,SString1),
    cgSetText(Second,SString1),
    Circle^width #= W,
    Circle^centerX #= CenterX,
    Circle^centerY #= CenterY,
    HAngle is 1/2*pi-(LocalH2 mod 12)*pi/6,
    HX2 is CenterX+floor(cos(HAngle)*W/5),
    HY2 is CenterY-floor(sin(HAngle)*W/5),
    HourP^x2 #:= HX2,  HourP^y2 #:= HY2, 
    %
    MAngle is 1/2*pi-(M mod 60)*pi/30,
    MX2 is CenterX+floor(cos(MAngle)*W/3),
    MY2 is CenterY-floor(sin(MAngle)*W/3),
    MinuteP^x2 #:= MX2,  MinuteP^y2 #:= MY2, 
    %
    SAngle is 1/2*pi-(S mod 60)*pi/30,
    SX2 is CenterX+floor(cos(SAngle)*W/3),
    SY2 is CenterY-floor(sin(SAngle)*W/3),
    SecondP^x2 #:= SX2,  SecondP^y2 #:= SY2,
    cgShow([HourP,MinuteP,SecondP,Hour,Minute,Second]).

addZero([D],String1):-!,String1=[0'0,D].
addZero(String,String1):-String1=String.

handleWindowClosing(Win,Flag),{windowClosing(Win)} =>
    Flag=1,
    cgClose(Win).

