{$A+,D-} { We are a desk accessory, and we don't want full debug mode! }

PROGRAM Alarm ;

CONST

{ Personal Pascal GEM library constant definitions. }

    Max_Tree	= 100 ;
    Null_Index	= -1 ;           { Index of a non-existent item }
    

  { Event values: }
    E_Message	= $10 ;
    E_Timer	= $20 ;


  { Text effects: }
    Normal	= 0 ;
    Thickened	= 1 ;
    Lightened	= 2 ;
    Slanted	= 4 ;
    Underlined	= 8 ;
    Outlined	= 16 ;
    Shadowed	= 32 ;

  { Font types: }
    System_Font = 3 ;
    Small_Font  = 5 ;

  { Graphic types of obs: }
    G_Box	= 20 ;
    G_Text	= 21 ;
    G_BoxText	= 22 ;
    G_IBox	= 25 ;
    G_Button	= 26 ;
    G_String	= 28 ;
    G_FText	= 29 ;
    G_FBoxText	= 30 ;

  { Object flags: }
    None	= $000 ;    { Also used for fill pattern and text effects. }
    Selectable	= $001 ;
    Default	= $002 ;
    Exit_Btn	= $004 ;
    Editable	= $008 ;
    Radio_Btn	= $010 ;
    Last_Ob	= $020 ;
    Touch_Exit	= $040 ;
    Hide_Tree	= $080 ;

  { Object states: }
    { Use Normal for no special state }
    Selected	= $01 ;
    Checked	= $04 ;
    Disabled	= $08 ;
  { Outlined and Shadowed are defined under text effects }


  { Color register shorthands: }
    White	= 0 ;
    Black	= 1 ;

  { Desk-Accessory values: }
    AC_Open	= 40 ;
    AC_Close	= 41 ;

{ End of constants used in GEM interface }
{ ====================================================================== }

TYPE

{ Common GEM type definitions for Personal Pascal library. }

    Color_Reg  = 0..15 ;

    Dialog_Ptr	= ^CHAR ;  {arbitrary pointer type}

    Message_Buffer = ARRAY [ 0..15 ] OF Short_Integer ;

    Str255	= STRING [ 255 ] ;

  { Editable text justification: }
    TE_Just	= ( TE_Left, TE_Right, TE_Center ) ;

  { Possible indices into a tree: }
    Tree_Index = Null_Index..Max_Tree ;


{ End of TYPEs used in GEM library definitions }
{ ====================================================================== }

{ EXTERNAL declarations, etc.,  for Personal Pascal GEM library routines. }

FUNCTION Init_Gem 
	: Short_Integer ;
	EXTERNAL ;
PROCEDURE Exit_Gem ;
	EXTERNAL ;

{ dialog-oriented subprograms }

FUNCTION New_Dialog( n_items, x, y, w, h : Short_Integer )
	: Dialog_Ptr ;
	EXTERNAL ;
FUNCTION Add_DItem( dial          : Dialog_Ptr ; 
		    i_type, flags : Short_Integer ;
		    x, y, w, h    : Short_Integer ;
		    border, color : Short_Integer )
	: Tree_Index ;
	EXTERNAL ;
FUNCTION D_Color( border, text   : Short_Integer ;
		  text_mode      : Boolean ;
		  pattern, fill  : Short_Integer )
	: Short_Integer ;
	EXTERNAL ;
PROCEDURE Set_DText( dial : Dialog_Ptr ;
		     item : Tree_Index ;
		     s    : Str255 ;
		     font : Short_Integer ;
		     just : TE_Just ) ;
	EXTERNAL ;
PROCEDURE Set_DEdit( dial : Dialog_Ptr ;
		     item : Tree_Index ;
		     template, valid, initial : Str255 ;
		     font : Short_Integer ;
		     just : TE_Just ) ;
	EXTERNAL ;
PROCEDURE Obj_SetState( dial   : Dialog_Ptr ;
			index  : Tree_Index ;
			state  : Short_Integer ;
			redraw : boolean ) ;
	EXTERNAL ;
PROCEDURE Center_Dialog( dial : Dialog_Ptr ) ;
	EXTERNAL ;
FUNCTION Do_Dialog( dial      : Dialog_Ptr ;
		    start_obj : Short_Integer )
	: Short_Integer ;
	EXTERNAL ;
PROCEDURE Get_DEdit( dial  : Dialog_Ptr ;
		     item  : Tree_Index ;
		     VAR s : Str255 ) ;
	EXTERNAL ;
PROCEDURE End_Dialog( dial : Dialog_Ptr ) ;
	EXTERNAL ;

FUNCTION Menu_Register( id : Short_Integer ;      { for desk accessories, only }
			VAR name : Str255 )
	: Short_Integer ;
	EXTERNAL ;

{ event management }

FUNCTION Get_Event(	event_mask : Short_Integer ;
			btn_mask   : Short_Integer ;
			btn_state  : Short_Integer ;
			n_clicks   : Short_Integer ;
			ticks      : Long_Integer ;
                        r1_flag    : boolean ;
				r1x, r1y, r1w, r1h : Short_Integer ;
                        r2_flag    : boolean ;
				r2x, r2y, r2w, r2h : Short_Integer ;
			VAR message : Message_Buffer ;
			VAR key     : Short_Integer ;
			VAR bstate,bcnt   : Short_Integer ;
			VAR mx, my : Short_Integer ;
			VAR kbd_state : Short_Integer  )
	: Short_Integer ;
	EXTERNAL ;

{ End of gemsubs.pas }

  VAR
      ap_id,                { Our application identification handle. }
      menu_id : Integer ;   { Index of our menu item in "Desk" menu. }
      our_name: Str255 ;    { The name of our accessory. }
		Nhours, Nmin : Short_Integer ;  { Numeric Time }
		Ttime : Str255;		{ Text time string }
		ASet, RingIt, CorClock : Boolean ;
      msg : Message_Buffer ;
		ShowIt : Short_Integer ;
		AlarmPtr : Dialog_Ptr ;
		Title, Author, SDate, STime, AlmTme, SETIT,
		CANCEL, STOPIT, COOL,
		Shour, Smin, Ssec, Smonth, Sday, Syear : Short_Integer ;
		Etime, Edate, YorN : Str255 ;
		MyColor : Short_Integer ;
		i, b : Long_Integer ;
		j : Short_Integer ;
		OnOrOff, YearHead, CClock : Short_Integer ;

{ ============== begin the procedures ============== }

{**************************************************************************
*
*	AUXSUBS.PAS - Declarations for PASAUX library
*
***************************************************************************}

{ Constants used by the auxilliary library }


{ now the various external and other declarations }


{*************************************************************************
*
*	date stuff
*
*************************************************************************}

PROCEDURE Set_Date( Mon, Day, Yr : Short_Integer );
	EXTERNAL;
PROCEDURE Set_Time( Hour, Min, Sec : Short_Integer );
	EXTERNAL;
PROCEDURE Get_Date( VAR Mon, Day, Yr : Short_Integer );
	EXTERNAL;
PROCEDURE Get_Time( VAR Hour, Min, Sec : Short_Integer );
	EXTERNAL;

{ Move to a specific screen coordinate.  Home is (1,1). }
PROCEDURE GotoXY( x, y : Short_Integer );      
   { make a local definition for the BIOS call }
   PROCEDURE cout ( dev, c : Short_Integer ) ;  BIOS( 3 ) ;
   BEGIN
        cout( 2,27   ) ;  cout( 2,ORD( 'Y' ) ) ;
        cout( 2,32+y ) ;  cout( 2,32+x ) ;
   END ;

PROCEDURE TimeDateToText ;

VAR
	Temp1, Temp2, Temp3, Temp4, Temp5, Temp6 : Char ;

BEGIN
	Temp1 := Chr( (Shour DIV 10) + 48 );
	Temp2 := Chr( (Shour MOD 10) + 48 );
	Temp3 := Chr( (Smin DIV 10) + 48 );
	Temp4 := Chr( (Smin MOD 10) + 48 );
	Etime := concat( Temp1, Temp2, Temp3, Temp4 ) ;
	
	Temp1 := Chr( (Smonth DIV 10) + 48 );
	Temp2 := Chr( (Smonth MOD 10) + 48 );
	Temp3 := Chr( (Sday DIV 10) + 48 );
	Temp4 := Chr( (Sday MOD 10) + 48 );
	Temp5 := Chr( ( (Syear MOD 100) DIV 10 ) + 48 );
	Temp6 := Chr( (Syear MOD 10) + 48 );
	Edate := concat( Temp1, Temp2, Temp3, Temp4, Temp5, Temp6 );
	YearHead := ( ( Syear DIV 100 ) * 100 ) ;
END;
	

PROCEDURE SetSystemTimeDate ;

BEGIN
	Get_Time( Shour, Smin, Ssec );
	Get_Date( Smonth, Sday, Syear);

	TimeDateToText ;

	Set_DEdit( AlarmPtr, SDate, 'Date    :__/__/__', '999999', Edate,
					System_Font, TE_Center );
	Set_DEdit( AlarmPtr, STime, 'Time       :__:__', '9999', Etime,
					System_Font, TE_Center ); 

END;

PROCEDURE SetUpDialog ;

BEGIN
	MyColor := D_Color( Black, Black, True, 5, White );
	AlarmPtr := New_Dialog( 15, 0,0, 40,14 ) ;
	OnOrOff := Add_DItem( AlarmPtr, G_IBox, None, 37,1, 2,1, 1, MyColor );
	Title := Add_DItem( AlarmPtr, G_Text, None, 11,1, 20,1, 0, MyColor ) ;
	Author := Add_DItem( AlarmPtr, G_Text, None, 9,2, 23,1, 0, MyColor ) ;
	SDate := Add_DItem( AlarmPtr, G_FText, Editable, 12,4, 17,1, 0, MyColor);
	STime := Add_DItem( AlarmPtr, G_FText, Editable, 12,5, 17,1, 0, MyColor);
	AlmTme := Add_DItem(AlarmPtr,G_FText, Editable, 12,6, 17,1, 0, MyColor);
	CClock := Add_DItem(AlarmPtr, G_FText, Editable, 12,7, 15,1, 0,MyColor);
	SETIT := Add_DItem( AlarmPtr, G_Button, Selectable | Exit_Btn , 2,9,
								10,1, 0, MyColor );
	COOL := Add_DItem( AlarmPtr, G_Button, Selectable | Exit_Btn,
								13,9, 14,1, 0, MyColor );
	STOPIT := Add_DItem( AlarmPtr, G_Button, Selectable | Exit_Btn, 28,9,
								10,1, 0, MyColor );
	CANCEL := Add_DItem(AlarmPtr, G_Button, Selectable | Exit_Btn | Default,
								13,11, 14,1, 0, MyColor );
	
	SetSystemTimeDate ;
	Set_DEdit( AlarmPtr, CClock, 'Corner Clock? _',	'A', 'Y', System_Font,
					TE_Center );
	Set_DText( AlarmPtr, Title, 'Personal Alarm Clock', System_Font,
					TE_Center );
	Set_DText( AlarmPtr, Author, 'by Jose M.C.S. Gallardo', Small_Font,
					TE_Center );
	Set_DEdit( AlarmPtr, AlmTme, 'Alarm Time :__:__', '9999', '9999',
					System_Font, TE_Center );
	Set_DText( AlarmPtr, SETIT, 'Set Alarm', System_Font, TE_Center );
	Set_DText( AlarmPtr, COOL, ' Set The Clock ', System_Font, TE_Center);
	Set_DText( AlarmPtr, STOPIT, 'Stop Alarm', System_Font, TE_Center);
	Set_DText( AlarmPtr, CANCEL, '  C A N C E L  ', System_Font, TE_Center);
	Obj_SetState( AlarmPtr, STOPIT, Disabled, false );

END;

PROCEDURE ConvertTime;

BEGIN
	Nhours := ( (ord(Ttime[1])-48)*10 ) + ( ord(Ttime[2])-48 );
	Nmin := ( (ord(Ttime[3])-48)*10 ) + ( ord(Ttime[4])-48 );
	Shour :=((ord(Etime[1])-48)*10) + (ord(Etime[2])-48);
	Smin :=((ord(Etime[3])-48)*10) + (ord(Etime[4])-48);
	Smonth :=((ord(Edate[1])-48)*10)+(ord(Edate[2])-48);
	Sday   :=((ord(Edate[3])-48)*10)+(ord(Edate[4])-48);
	Syear  :=((ord(Edate[5])-48)*10)+(ord(Edate[6])-48)+YearHead;
	IF (Shour >= 24) THEN Shour := Shour - 24 ;
END ;

PROCEDURE SoundBeep ;
	
	BEGIN
		write(chr($07))
	END;

PROCEDURE CheckAlarm ;

	VAR
		CkHr, CkMin, CkSec : Short_Integer ;

	BEGIN
		Get_Time( CkHr, CkMin, CkSec ) ;

		Get_DEdit( AlarmPtr, AlmTme, Ttime ) ;
		ConvertTime ;

		IF ((Nhours < 12) AND (CkHr > 12)) THEN
			Nhours := Nhours + 24 ;

		IF (( Nhours <= CkHr) AND (Nmin <= CkMin)) THEN 
				RingIt := true ;
	END;

PROCEDURE CCTest ;

	BEGIN
		IF (YorN = 'Y') THEN CorClock := true ;
		IF (YorN = 'N') THEN
		BEGIN
			CorClock := false ;
			GotoXY( 70,0) ;
			Write('         ');
		END ;
		IF (YorN <> 'Y') AND (YorN <> 'N') THEN
		BEGIN
			IF (CorClock = true) THEN 
	Set_DEdit( AlarmPtr, CClock, 'Corner Clock? _','A', 'Y', System_Font,
					TE_Center )
			ELSE
	Set_DEdit( AlarmPtr, CClock, 'Corner Clock? _',	'A', 'N', System_Font,
					TE_Center );
		END;
	END;

PROCEDURE ClStuff ;

	BEGIN
							Get_DEdit( AlarmPtr, SDate, Edate );
							Get_DEdit( AlarmPtr, STime, Etime );
							i := Clock ;
							ConvertTime ;
							b := Clock ;
							Ssec := ( Ssec + ( b - i ) ) ;
							IF (Ssec >= 60) THEN
							BEGIN
								j := Ssec DIV 60 ;
								Ssec := Ssec - ( j * 60 );
								Smin := Smin + j ;
							END;
							Set_Time( Shour, Smin, Ssec );
							Set_Date( Smonth, Sday, Syear );
							Get_DEdit( AlarmPtr, CClock, YorN );
							CCTest ;
	END;		

PROCEDURE DoIt ;

	BEGIN
		Center_Dialog( AlarmPtr );
		ShowIt := Do_Dialog( AlarmPtr, SDate );
		End_Dialog(AlarmPtr);

		IF ShowIt = CANCEL THEN BEGIN
							msg[0] := AC_Close;
							Obj_SetState( AlarmPtr, CANCEL, Normal, false )
						END ELSE
		IF ShowIt = SETIT THEN BEGIN
							Get_DEdit( AlarmPtr, AlmTme, Ttime ) ;
							ClStuff ;
							IF (Nhours < 24) THEN
							BEGIN
								ASet := true;
								Obj_SetState( AlarmPtr, OnOrOff, Checked, false );
								Obj_SetState( AlarmPtr, STOPIT, Normal, false );
							END ;
							Obj_SetState( AlarmPtr, SETIT, Normal, false );
							msg[0] := AC_Close 
					   END ELSE
		IF ShowIt = STOPIT THEN BEGIN
							ASet := false ;
							RingIt := false ;
							Obj_SetState( AlarmPtr, OnOrOff, Normal, false );
							Obj_SetState( AlarmPtr, STOPIT, Disabled, false );
							msg[0] := AC_Close 
						END ELSE
		IF ShowIt = COOL THEN BEGIN
							msg[0] := AC_Close;
							ClStuff ;
							Obj_SetState( AlarmPtr, COOL, Normal, false );
						END ;		

	END;

	PROCEDURE Do_Msg ;

		BEGIN
        	CASE msg[0] OF
				AC_Open:
					BEGIN
						SetSystemTimeDate;
						DoIt ;
					END;
            
	      END;  {Case}
		END; {Do_Msg}	


  PROCEDURE Event_Loop ;

    VAR event, junk : Integer ;

    BEGIN
      WHILE True DO
        BEGIN
          event := Get_Event( E_Message | E_Timer, 0, 0, 0, 500,
                     false, 0, 0, 0, 0, false, 0, 0, 0, 0,
                     msg, junk, junk, junk, junk, junk, junk ) ;

			IF ( event & E_Message ) <> 0 THEN
				Do_Msg ;
					
			IF ASet THEN CheckAlarm ;

			IF RingIt THEN SoundBeep ;

			IF CorClock THEN
			BEGIN
				Get_Time( Shour, Smin, Ssec ) ;
				GotoXY( 70,0 ) ;

				IF (Shour < 10)
				THEN
					Write('0',Shour,':')
				ELSE
					Write(Shour,':');

				IF (Smin < 10)
				THEN
					Write('0',Smin,':')
				ELSE
					Write(Smin,':');

				IF (Ssec < 10)
				THEN
					Write('0',Ssec)
				ELSE						
					Write(Ssec) ;						
			END;	         
        END; {while}
    END ;


  BEGIN
    ap_id := Init_Gem ; { We do need to save our application ID... }
    IF ap_id >= 0 THEN  { that's a change from most programs. }
		BEGIN 
			ASet := false ;
			RingIt := false ;
			CorClock := true ;
			YearHead := 1900 ;
			our_name := '  Alarm Clock' ;
			menu_id := Menu_Register( ap_id, our_name ) ;
			SetUpDialog ;
			Event_Loop ;
		END
  END.
