#!/usr/local/bin/php
<?php
# Simple Countdown or Countup Timer for PHP-GTK
#
# Last modified Feb. 13, 2018.
# 
# First released March 31, 2017.
#
#------------------------------------------------------------------------------
#
#
# All arguments are optional.  If no arguments are provided, the timer will
# display a 5 minute countdown by default.
#
#
# Arg 1: "title"
# The title you want the timer window to have.
#
# Arg 2: "note"
# A note you want shown near the top of the timer window.
#
# Arg 3: "icon_fillepath"
# A fillepath (full, absolute filepath) to your desired icon for the
# timer window's title bar.
#
#
# Arg 4:  "count_up_or_down"
# Possible settings: "up" or "down"
#
#
# Arg 5: "dest_datetime"
#
# To make the timer count down (or up) all the seconds until a particular date/time,
# set this to any date/time text that PHP's strtotime() function can understand.
#
# This is impressively flexible - see the below links for examples.
#
# http://php.net/manual/en/datetime.formats.relative.php
# http://php.net/manual/en/datetime.formats.time.php
# http://php.net/manual/en/datetime.formats.date.php
#
# If you set Arg 5, don't set Args 8 thru 13, since they'll be ignored.
#
# 
# Arg 6: "dest_datetime_format"
#
# If you don't set this, dest_datetime will be displayed in the timer window
# in this format by default: "Fri, 31 Mar 2017 14:56:04 -0400"
#
# Arg 6 lets you choose your own format.  See this link for more info:
#
# http://php.net/manual/en/function.date.php
#
#
# Arg 7: "shell_command_to_exec_when_timer_done"
# A shell command you want executed after the timer is done with its countdown (or countup).
#
#
# Args 8, 9, 10: "total_hours", "total_mins", "total_secs"
# The total amount of time you want the timer to run.
#
# Ignored if Arg 5 ("dest_datetime") is present.
#
#
# Args 11, 12, 13: "start_hours", "start_mins", "start_secs"
# Set one or more of these if you want to make the timer skip ahead to a later time in
# the countdown (or countup).
#
# Ignored if Arg 5 ("dest_datetime") is present.
#
#
# Arg 14: "should_show_start_and_end_times"
# Set to "y" or "yes" to make the timer definitely display the text "Timer started:"
# and "Timer ended:".
#
# Set to anything else to hide that text.
#
# If not set, the value of the constant "Default_for_should_show_start_and_end_times"
# is used instead.
#
#
#------------------------------------------------------------------------------
#
#
# Under the GNU Affero General Public License v3.0.
#
# Copyright (C) 2017/2018  Apollia
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Contact info:     http://astroblahhh.com/contact-apollia.shtml
#
#-----


$scripthome=dirname(__FILE__);
$Fillepath____Default_Icon="$scripthome/Icons/clock-icon.png";

			
			
$Default_Total_Hours=0;
$Default_Total_Mins=5;
$Default_Total_Secs=0;

$orig_working_dir=getcwd();
define ("Orig_Working_Dir", $orig_working_dir);

define ("Default_for_should_show_start_and_end_times", true);




trait Magic_Getter
{
	public function __get($property)
	{
		if (property_exists($this, $property) )
		{
			return $this->$property;
		}
		else
		{
			$class=get_class($this);
			echo "$class doesn't have a $property property!  Aborting.";
			exit;
		}	
	}
}




class Prototype___Mr_Cliargs
{
	protected $script_filename;
	protected $args;
	protected $arg_count;
	


	public function Get_Cliargs ()
	{
		global $argv;
		
		$args=$argv;
		$script_filename=array_shift($args);
		
		$arg_count=count($args);

		$this->arg_count=$arg_count;
		$this->script_filename=$script_filename;
		$this->args=$args;

	}
	
	
	
	public function Maybe_Get_Arg( $num )
	{
		$args=$this->args;
		
		if (isset ($args[$num] ) )
		{
			return $args[$num];
		}
		else
		{
			return null;
		}
	}
	
	
	
	public function Get_Arg( $num )
	{
		$args=$this->args;
		
		if (isset ($args[$num] ) )
		{
			return $args[$num];
		}
		else
		{
			$display_num=$num+1;
			echo "Aborting: No arg $display_num found!";
			br();
			exit;
		}
	}
	
	
	
	public function Get_Count()
	{
		return $this->$arg_count;
	}
}




class Prototype___Mr_Raw_Input
{
	protected $args_to_get=array("title", "note", "icon_fillepath", "count_up_or_down", "dest_datetime", "dest_datetime_format", "shell_command_to_exec_when_timer_done", "total_hours", "total_mins", "total_secs", "start_hours", "start_mins", "start_secs", "should_show_start_and_end_times" );
	
	protected $raw_input=array();
	

	use Magic_Getter;
	
	
	
	public function Fetch_Raw_Input()
	{
		global $Mr_Cliargs;
		
		$args_to_get=$this->args_to_get;
		
		$argnum=0;
		foreach ($args_to_get as $arg)
		{
			
			$this->raw_input[$arg]= $Mr_Cliargs->Maybe_Get_Arg($argnum);
			$argnum++;
		}
		print_r($this->raw_input);
		
	}
}




class Prototype___Mr_Standardized_Input
{

	protected $raw_input;
	protected $standardized_input;
	
	use Magic_Getter;



	public function __construct()
	{
		global $Mr_Raw_Input;
		
		$this->raw_input=$Mr_Raw_Input->raw_input;
		
	}

	
	
	public function Standardize_Raw_Input()
	{
		$raw=$this->raw_input;
		
		#echo "Raw title:";
		#echo $raw["title"];
		
		#--------------------------------------
		# title
		if ( ! $raw["title"] )
		{	
			$this->standardized_input["title"]="Timer";
		}
		else
		{	
			$this->standardized_input["title"]=$raw["title"];
		}
		
		
		#--------------------------------------
		# note
		
		$this->standardized_input["note"]=$raw["note"];
		

		#--------------------------------------
		# icon_fillepath
		
	#	$this->standardized_input["icon_fillepath"]=$raw["icon_fillepath"];
		if ( ! $raw["icon_fillepath" ] )
		{
			global $Fillepath____Default_Icon;
			$this->standardized_input["icon_fillepath"]=$Fillepath____Default_Icon;
		}
		else
		{
			$this->standardized_input["icon_fillepath"]=$raw["icon_fillepath"];
		}
		
		#--------------------------------------
		# count_up_or_down
		
		
		if ( $raw["count_up_or_down"] == "up" || $raw["count_up_or_down"] == "down")
		{
			$this->standardized_input["count_up_or_down"]=$raw["count_up_or_down"];
		}
	
		else if ( $raw["count_up_or_down"] != "up" && $raw["count_up_or_down"] != "down")
		{
			$this->standardized_input["count_up_or_down"]="down";
		}
		
		
		#--------------------------------------
		# dest_datetime
		
		if ( $raw["dest_datetime"] )
		{
			$dest_unixtime=strtotime( $raw["dest_datetime"] );
			#echo "dest_unixtime: $dest_unixtime";
			if ($dest_unixtime==false)
			{	
				$this->standardized_input["dest_datetime"]="";
			}
			else
			{
				$this->standardized_input["dest_datetime"]=$dest_unixtime;
			}
		}
		else
		{
			$this->standardized_input["dest_datetime"]="";
		}
		
		
		#--------------------------------------
		# dest_datetime_format
		
		if ( ! $raw["dest_datetime_format"] )
		{
			$this->standardized_input["dest_datetime_format"] = "r";
		}

		
		#--------------------------------------
		# shell_command_to_exec_when_timer_done
		
		$this->standardized_input["shell_command_to_exec_when_timer_done"]=$raw["shell_command_to_exec_when_timer_done"];
		
		
		#--------------------------------------
		# total_hours
		
		$this->standardized_input["total_hours"]=$raw["total_hours"];
		
		#--------------------------------------
		# total_mins
		
		$this->standardized_input["total_mins"]=$raw["total_mins"];
		
		#--------------------------------------
		# total_secs
		
		$this->standardized_input["total_secs"]=$raw["total_secs"];
		
		#--------------------------------------
		# start_hours
		
		$this->standardized_input["start_hours"]=$raw["start_hours"];
		
		#--------------------------------------
		# start_mins
		
		$this->standardized_input["start_mins"]=$raw["start_mins"];
		
		#--------------------------------------
		# start_secs
		
		$this->standardized_input["start_secs"]=$raw["start_secs"];
		
		
		#--------------------------------------
		# should_show_start_and_end_times
		
		$should_show_start_and_end_times = $raw["should_show_start_and_end_times"];
		
		
		if ($should_show_start_and_end_times == "y" || $should_show_start_and_end_times == "yes" )
		{
			$this->standardized_input["should_show_start_and_end_times"] = "y";
		}
		else if ($should_show_start_and_end_times )
		{
			$this->standardized_input["should_show_start_and_end_times"]=false;
		}
		else if (!$should_show_start_and_end_times)
		{
			$this->standardized_input["should_show_start_and_end_times"]=Default_for_should_show_start_and_end_times;
		}
	}
}




class Prototype___Mr_Valichecker
{

		
	
	public function Do_Valicheck_of_Input()
	{
		global $Mr_Standardized_Input;
		
		$input = $Mr_Standardized_Input->standardized_input;
		
		$this->Valicheck_Arg____Must_Be_Integer_or_Empty($input["total_hours"], "total hours");
		$this->Valicheck_Arg____Must_Be_Integer_or_Empty($input["total_mins"], "total mins");
		$this->Valicheck_Arg____Must_Be_Integer_or_Empty($input["total_secs"], "total secs");
		
		$this->Valicheck_Arg____Must_Be_Integer_or_Empty($input["start_hours"], "start hours");
		$this->Valicheck_Arg____Must_Be_Integer_or_Empty($input["start_mins"], "start mins");
		$this->Valicheck_Arg____Must_Be_Integer_or_Empty($input["start_secs"], "start secs");
		
		$this->Valicheck_Arg____count_up_or_down( $input["count_up_or_down"]);
		$this->Valicheck_Arg____dest_datetime( $input["dest_datetime"] );
		return true;
	}
	
	
	
	protected function Valicheck_Arg____dest_datetime( $arg)
	{
		if (! $arg)
		{
			return true;
		}
		/*
		$now_unixtime=time();
		
		if  ( ($arg - $now_unixtime ) < 0 )
		{
			$abort_message="Aborting: dest_datetime must not be in the past!";
			
			echo "\n\n$abort_message\n";
			Alert($abort_message);
			exit(2);
		}
		* */
	}
	
	
	
	protected function Valicheck_Arg____count_up_or_down( $arg )
	{
		if ( $arg != "down" && $arg != "up" )
		{
			echo "Aborting because count_up_or_down must be either up or down!";
			exit;
		}
	}
	
	
	
	protected function Valicheck_Arg____Must_Be_Integer_or_Empty( $arg, $what_it_is )
	{
		if ( !$arg )
		{	return true;
		}
		if (is_int( (int) $arg) )
		{
			return true;
		}
		echo "Aborting: $what_it_is must be an integer, but it's $arg!";
		exit;
	}
	
}



function Calculate_Seconds_in_Hours_Mins_Secs($hours, $mins, $secs)
{
	$hoursecs=60*60*$hours;
	$minsecs=60*$mins;

	return $hoursecs+$minsecs+$secs; 
}



function Alert($msg)
{
    $Dialog = new GtkDialog('Alert', null, Gtk::DIALOG_MODAL);
    $Dialog->set_position(Gtk::WIN_POS_CENTER_ALWAYS);
    
    $vbox = $Dialog->vbox;
    $vbox->pack_start($hbox = new GtkHBox());
    
    $stock = GtkImage::new_from_stock(Gtk::STOCK_DIALOG_WARNING,
        Gtk::ICON_SIZE_DIALOG);
        
    $hbox->pack_start($stock, 0, 0);
    $hbox->pack_start(new GtkLabel($msg));
    
    $Dialog->add_button(Gtk::STOCK_OK, Gtk::RESPONSE_OK);
    $Dialog->set_has_separator(false);
    $Dialog->show_all();
    $Dialog->run();
    $Dialog->destroy();
}







function Start_Timer( $Args_for_Timer )
{
	global $Total_Time;
	
    $Mr_Timer = new Prototype___Mr_Timer;
    
    $Mr_Timer->Initialize( $Args_for_Timer );
    
    $Mr_Timer->Start();   
}



class Prototype___Mr_Timer
{
    protected $Timer_Text;
    protected $Timer_Dialog;
    
    protected $DateTime_At_Start;
    protected $DateTime_At_End;
    
    protected $Start_Label;
    protected $End_Label;
    
    protected $Pause_Button;
    protected $Resume_Button;
    
    protected $Pause_Label;
    protected $Note_Label;
    
    protected $total_time;
    protected $total_time_text;
    
    protected $time_left;
    
    protected $timeout_ID=null;
    
    protected $start_time;

	protected $counting_down;
	protected $counting_up;
	protected $count_up_or_down;

	protected $timer_done=false;
	protected $past_dest_datetime;
	protected $Past_DateTime;
	
	protected $should_show_start_and_end_times;
	
	
	
	//~ public function On_Status_Icon_Click()
	//~ {
		//~ echo "yay";
		//~ Alert("test");
		//~ $Timer_Dialog=$this->Timer_Dialog;
		//~ 
		//~ if ($Timer_Dialog->is_visible())
		//~ {	
			//~ $Timer_Dialog->hide_all();
		//~ }
		//~ else
		//~ {	$Timer_Dialog->show_all();
		//~ }
	//~ }
	public function Start()
	{
		#if ($this->counting_up)
		#{
			$this->Update_Timer();
			$this->Start_Timer();
		#}
		#else
		#{
		#	$this->Update_Timer();
		#	$this->Start_Timer();
		#}
	}
	
	
	
    public function Initialize( $Args_for_Timer)
    {   
		foreach ($Args_for_Timer as $key => $value)
		{
			#echo "Arg: $key  $value\n";
			${$key}=$value;
		}
		
		
		if ($past_dest_datetime)
		{
			$count_up_or_down="up";
		}
		
		$this->count_up_or_down=$count_up_or_down;
		$this->shell_command_to_exec_when_timer_done=$shell_command_to_exec_when_timer_done;
		$this->past_dest_datetime=$past_dest_datetime;
		$this->should_show_start_and_end_times=$should_show_start_and_end_times;
		

		
		if ( $count_up_or_down == "up" )
		{
			$this->counting_down=false;
			$this->counting_up=true;
			$this->Set_Time_Left_to_($Total_Time-$Start_Time);
		}
		else
		{
			$this->counting_down=true;
			$this->counting_up=false;
			$this->Set_Time_Left_to_($Total_Time);
			$this->Set_Time_Left_to_($Total_Time-$Start_Time);
		}
		
		$this->Set_Total_Time_to_($Total_Time);
		
		
		
		
		$Timer_Dialog = new GtkDialog("$Title", null, Gtk::DIALOG_MODAL);
       
       
       # 15:46:46 02/13/20185.  Couldn't get the status icon to work at all.
       # 
       # http://www.devshed.com/c/a/php/building-your-own-system-tray-application-using-php-gtk/
       
       //~ $statusicon = new GtkStatusIcon();
		//~ global $Fillepath____Default_Icon;
		//~ $statusicon->set_from_stock(Gtk::STOCK_FILE);
		//~ #$statusicon->set_from_file("$Fillepath____Default_Icon");
		//~ $statusicon->set_visible(true);
		//~ $statusicon->set_blinking(true);
		//~ $statusicon->connect('activate', array(&$this, 'On_Status_Icon_Click') );

		#Alert("test");
		if ( $Icon_Fillepath )
		{
			$pixbuf=GdkPixbuf::new_from_file("$Icon_Fillepath");
       
			$Timer_Dialog->set_icon($pixbuf);
		}
       
		
        $Accel_Group=new GtkAccelGroup();
        $Timer_Dialog->add_accel_group($Accel_Group);
        
        
		#$Timer_Dialog->set_size_request(400, 140);
       
        $vbox = $Timer_Dialog->vbox;
        
        
        
       # $this->Timer_Text = new GtkLabel();
        
        if ( ! $dest_datetime )
        {
			$Quit_Button=GtkButton::new_from_stock(Gtk::STOCK_QUIT);
		
			#$Quit_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_Q, Gdk::CONTROL_MASK, 0 );
			$vbox->pack_start($Quit_Button);
			$Quit_Button->connect('clicked', array(&$this, "Quit_Timer") );
			
			#$Timer_Dialog->set_default($this->Timer_Text);
			
			#$this->Timer_Text->grab_focus();
		}
		
		
        $this->Countdown_or_Up_Label = new GtkLabel();
        $this->Countdown_or_Up_Label->modify_font(new PangoFontDescription("Sans 12"));
        
     
		$this->Pause_Label = new GtkLabel();
        $this->Pause_Label->modify_font(new PangoFontDescription("Sans 12"));
        
        
        $this->Note_Label = new GtkLabel();
        $this->Note_Label->modify_font(new PangoFontDescription("Sans 15"));
        $this->Note_Label->set_text("$Note");
        
        echo "TITLE: $Title";
        $total_time_seconds = $this->total_time;

        $Now_DateTime=new DateTime("now");
		$End_DateTime=new DateTime("now");
		$End_DateTime->add(DateInterval::createFromDateString("$total_time_seconds seconds") );
				
		$Interval=$End_DateTime->diff($Now_DateTime, true);
		
		$format=$this->Get_Date_Format_But_Omit_Larger_Units_If_Possible($Interval);
		$total_time_text=$Interval->format($format);
			
		$this->total_time_text=$total_time_text;
		
		
		if ($this->counting_down )
		{
			$label = "Counting down ";
		}
		else
		{
			$label = "Counting up ";
		}
			
		if ($dest_datetime)
		{
			
			$formatted_dest_datetime=date( $dest_datetime_format, $dest_datetime );
			
			if ($past_dest_datetime)
			{
				$label="Time since $formatted_dest_datetime:";
			}
			else
			{
				$label .= "$total_time_text to $formatted_dest_datetime";
			}
		}
		else
		{
			if ($this->counting_down )
			{
				$label .= "from $total_time_text";
			}
			else
			{
				$label .= "to $total_time_text";
			}
		}
		
		$countdown_or_up_label_text=$label;
       
		$this->countdown_or_up_label_text=$countdown_or_up_label_text;
        
        $this->Countdown_or_Up_Label->set_text("$countdown_or_up_label_text");
        
        
        $vbox->pack_start($this->Note_Label);
        
        $this->DateTime_At_Start=new DateTime("now");
	    
        if ( $this->should_show_start_and_end_times )
        {

			$this->Start_Label = new GtkLabel();
			$vbox->pack_start($this->Start_Label);
			
			
			
			$label="Timer Started: ";
			$label .= $this->DateTime_At_Start->format("r");
			
			$this->Start_Label->set_text( $label );
		}
        
        echo "\n\nStarted Timer: ";
        echo $this->DateTime_At_Start->format("r");
       # echo "\n";
        
        echo "\n\nTimer Note: $Note";
        
        $vbox->pack_start($this->Countdown_or_Up_Label);
        
        
		$this->Timer_Text = new GtkLabel();
        $this->Timer_Text->modify_font(new PangoFontDescription("Sans 24"));
        
        if ($this->counting_down)
        {
			$timer_text_label="$total_time_text";

        }
        else
        {	
			if ($past_dest_datetime)
			{

				$Past_DateTime=new DateTime();
				$Past_DateTime->setTimestamp($dest_datetime);
				
				$Now_DateTime=new DateTime();
				
				$Time_Since=$Past_DateTime->diff($Now_DateTime);
				
				$format=$this->Get_Date_Format_But_Omit_Larger_Units_If_Possible($Time_Since);

				$timer_text_label=$Time_Since->format("$format");
				
				$this->Past_DateTime=$Past_DateTime;
				
			}
			else
			{

				if (strlen($total_time_text)>5 )
				{
					$timer_text_label="00:00:00";
					
				}
				else
				{
					$timer_text_label="00:00";
				}
			}
		}
        
        
        $this->Timer_Text->set_text($timer_text_label);
        
        $vbox->pack_start($this->Timer_Text);
        
        
        #$Timer_Dialog->set_default($this->Timer_Text);
			
		#$this->Timer_Text->grab_focus();
			
        $vbox->pack_start($this->Pause_Label);
        
        
        if ( $this->should_show_start_and_end_times )
        {
			$this->End_Label = new GtkLabel();
			$vbox->pack_start($this->End_Label);
        }
        
		$Blank_Button=new GtkButton(" ");
		$vbox->pack_start($Blank_Button);
		$Timer_Dialog->set_default($Blank_Button);
		$Blank_Button->grab_focus();
        
        if ( !$dest_datetime )
        {
			$Pause_Button=new GtkButton("Pause");
			$Resume_Button=new GtkButton("Resume");
        
			$Pause_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_P, Gdk::CONTROL_MASK, 0 );
			$Pause_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_P, 0, 0 );
			$Pause_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_S, Gdk::CONTROL_MASK, 0 );
			$Pause_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_S, 0, 0 );
			$Resume_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_R, Gdk::CONTROL_MASK, 0 );
			$Resume_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_R, 0, 0 );
			
			$vbox->pack_start($Pause_Button);
			$vbox->pack_start($Resume_Button);
        
			$Pause_Button->connect('clicked', array(&$this, "Pause_Timer") );
			$Timer_Dialog->set_default($Pause_Button);
			$Pause_Button->grab_focus();
			
			$Resume_Button->connect('clicked', array(&$this, "Resume_Timer") );
        }
        else
        {
			$Quit_Button=GtkButton::new_from_stock(Gtk::STOCK_QUIT);
	
			$vbox->pack_start($Quit_Button);
			$Quit_Button->connect('clicked', array(&$this, "Quit_Timer") );
			
			#$Timer_Dialog->set_default($this->Timer_Text);
			
			#$this->Timer_Text->grab_focus();
		}
		
		
		
		
		
		
		$Quit_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_Q, Gdk::CONTROL_MASK, 0 );
  #      $Quit_Button->add_accelerator('clicked', $Accel_Group, Gdk::KEY_Q, 0, 0 );
        
		
		$Timer_Dialog->connect('key-press-event', array (&$this, 'on_timer_window_keypress')); 
		
		
		#$Quit_Button->set_sensitive(false);
		
        $Timer_Dialog->set_has_separator(false);
        $Timer_Dialog->show_all();
        
        #$Quit_Button->hide();
        
        $this->Timer_Dialog = $Timer_Dialog;

        $Timer_Dialog->connect('delete-event',
            array( &$this, "Upon_Delete_Event"));

        $this->start_time = microtime(1);
    }
    
    
    
    protected function Note_End_Time()
    {
		$this->DateTime_At_End=new DateTime("now");
	}
	
	
	
	public function Set_End_Label()
	{
	
		
        
        $label="Timer Ended: ";
        $label .= $this->DateTime_At_End->format("r");
        
        $this->End_Label->set_text( $label );
        
	}
	
	
	
	public function Set_Total_Time_to_($seconds)
	{
		$this->total_time=$seconds;
	}
	
	
	
	public function Set_Time_Left_to_($seconds)
	{	$this->time_left=$seconds;
	}
	
	
	
	public function Start_Timer()
	{	
		$count_up_or_down=$this->count_up_or_down;
		
		echo "\n\n\n"; echo "Starting Count$count_up_or_down Timer";
		echo " - ";
		echo $this->countdown_or_up_label_text;
		
		#$this->Show_Time_Left();
		$this->Show_Time_Left_and_Time_Elapsed();
		
		$this->start_time = microtime(1);
		$this->timeout_ID = Gtk::timeout_add(1000,
			array(&$this, 'Update_Timer'));
	}
	
	
	
	public function Resume_Timer()
	{
		if ( $this->timer_done)
		{
			return false;
		}
		if ($this->timeout_ID === null )
		{
			echo "\n\n"; echo "Resuming Timer"; echo "\n";
			$this->Pause_Label->set_text('');

			$this->Start_Timer();
			
		}
	}
	
	
	
	protected function Perform_Pause()
	{
		
		Gtk::timeout_remove($this->timeout_ID);
		$this->timeout_ID=null;	
		
		echo "\n\n\n"; echo "Pausing Timer"; echo "\n";
		
		$this->Show_Time_Left_and_Time_Elapsed();
	}
	
	
	public function Quit_Timer()
	{
		exit(0);
	}
	public function Pause_Timer()
	{
		if ( $this->timer_done)
		{
			return false;
		}
		if ($this->timeout_ID === null)
		{
			return true;
		}
		$this->Pause_Label->set_text('(paused)');
		$this->Perform_Pause();
	}
	
	
	
	protected function Show_Time_Left()
	{
		$timetext=$this->Assemble_Time_Text($this->time_left);
	
		echo "\nTime left: $timetext";
	}
	
	
	
	protected function Show_Time_Elapsed()
	{
		$timetext=$this->Assemble_Time_Text( ( $this->total_time - $this->time_left ));
		echo "\nTime elapsed: $timetext";
	}
	
	
	
	protected function Show_Time_Left_and_Time_Elapsed()
	{
		$this->Show_Time_Left();
		$this->Show_Time_Elapsed();	
	}
	
	
	
	protected function Convert_to_Hours_Mins_Secs($time)
	{
		$hours= floor( $time / 60 / 60 );
		$hoursecs=$hours*60*60;
        
		$minutes=floor(($time-$hoursecs)/60);
		$minutesecs=$minutes*60; 
        
		$seconds=floor($time-$hoursecs-$minutesecs);
			
		return array($hours, $minutes, $seconds);
	}
	
	
	
	protected function Get_Date_Format_But_Omit_Larger_Units_If_Possible($interval)
	{
		$format="";
	
		if ($interval->y)
		{
			$format .="%yy ";
		}
		if ($interval->m)
		{	
			$format .= "%mm ";
		}
		if ($interval->d)
		{
			$format .= "%dd ";
		}
		
		$format.="%H:%I:%S";
		
		return $format;
	}
	protected function Assemble_Time_Text( $time )
	{
		list($hours, $minutes, $seconds) = $this->Convert_to_Hours_Mins_Secs($time);
		
		 # 04:56:13 03/30/2017.  Left-padding with zeros:
		if ($hours!=0)
		{
			$hours=sprintf("%02d:", $hours);
		}
		else
		{	$hours="";
		}
		$minutes=sprintf("%02d:", $minutes);
		$seconds=sprintf("%02d", $seconds);

		$timetext="$hours$minutes$seconds";
		
		return $timetext;

	}
	
	
	
    public function Update_Timer()
    {   
        $elapsed_time = microtime(1) - $this->start_time;
        $remaining_time = $this->total_time - $elapsed_time;
        
	
        	
        if ( abs($remaining_time - $this->time_left ) > 1)
        {
			$remaining_time=$this->time_left;
			$elapsed_time=$this->total_time - $this->time_left;
		}

		
		if ( $this->counting_down )
		{	
			$remaining_time=floor($remaining_time);

			$Now_DateTime=new DateTime("now");
			$End_DateTime=new DateTime("now");
			$End_DateTime->add(DateInterval::createFromDateString("$remaining_time seconds") );
			
			$Interval=$End_DateTime->diff($Now_DateTime);
			
			$format=$this->Get_Date_Format_But_Omit_Larger_Units_If_Possible($Interval);
			$timetext=$Interval->format($format);
			

			#$timetext=$this->Assemble_Time_Text($remaining_time);

		}
		else
		{	
			if ($this->past_dest_datetime)
			{
				$Past_DateTime=$this->Past_DateTime;
				
				$Time_Since=$Past_DateTime->diff( new DateTime('now') );
				
				$format=$this->Get_Date_Format_But_Omit_Larger_Units_If_Possible($Time_Since);
				
				$timetext=$Time_Since->format($format);
				
			}
			else
			{
				$Now_DateTime=new DateTime("now");
				$End_DateTime=new DateTime("now");
				$elapsed_time=floor($elapsed_time);
				$End_DateTime->add(DateInterval::createFromDateString("$elapsed_time seconds") );
				
				$Interval=$End_DateTime->diff($Now_DateTime);
				
				$format=$this->Get_Date_Format_But_Omit_Larger_Units_If_Possible($Interval);
				$timetext=$Interval->format($format);
			
				#$timetext=$this->Assemble_Time_Text($elapsed_time);
			}
		}


		$this->Timer_Text->set_text("$timetext");
		 
		
		--$this->time_left;

        
        while (Gtk::events_pending()) {Gtk::main_iteration();}

        if ($remaining_time>0)
        {
            return true;
        } 
        else 
        {   
			if ($this->past_dest_datetime)
			{
				# 22:55:48 03/30/2017.  If the dest_datetime is in the past, just keep going forever.
				
				return true;
			}
            chdir(Orig_Working_Dir);
            #Alert(Orig_Working_Dir);
            
            $shell_command_to_exec_when_timer_done=$this->shell_command_to_exec_when_timer_done;
            
            
            
            if ($shell_command_to_exec_when_timer_done)
            {
				echo "\n\n\nShell command about to be executed: $shell_command_to_exec_when_timer_done";
				
				exec("$shell_command_to_exec_when_timer_done", $output, $retcode);
				
				$output_string=implode("\n", $output);
				echo "\n\n\nOutput:";
				
				echo "\n\n";
				echo $output_string;
				echo "\n\n";
            }
            
           # $this->Timer_Dialog->destroy();
            
            if ( $this->timeout_ID)
            {
				Gtk::timeout_remove($this->timeout_ID);
			}
			
			$this->Pause_Label->set_text("Timer Done");
			
			$this->Note_End_Time();
			
			if ( $this->should_show_start_and_end_times )
			{
				$this->Set_End_Label();
			}
			echo "\n\nTimer Done: ";
			echo $this->DateTime_At_End->format("r");
			echo "\n";
			
			$this->timer_done=true;
			
			#exit;
			return false;
        }
    }


	public function on_timer_window_keypress($widget, $event)
	{
		
		echo "\n\nA key was pressed while timer window was in focus!";
		
		echo "Key: ";
		echo $event->keyval;
		
		if ($event->keyval==Gdk::KEY_Escape) 
		{
			echo "\n\nEscape was pressed!\n\n";
		
		
			#!!!!
			return true;
				# 12:40:54 06/27/2020.  This blocks Esc from closing the timer window!
				#
				# 12:50:09 06/27/2020.  This page helped me figure out how to do this:
				#
				# https://www.kksou.com/php-gtk2/sample-codes/setup-interactive-search-in-GtkTreeView-for-PHP-GTK-v2.0-Part-1-non-OOP-version.php
				
		}
    
	}
	
    // Called when user closes the timer.
    public function Upon_Delete_Event($widget, $event)
    {
		echo "\n\n";
		echo "Timer window closed!";
		
		#echo "Widget: \n\n";
		#print_r($widget);
		
		#echo "Event: \n\n";
		#print_r($event);

    
    
        $this->Timer_Dialog->destroy();
        
        if ($this->timeout_ID)
        {
			Gtk::idle_remove($this->timeout_ID);
		}
		
		exit;
    }
}




$Mr_Cliargs=new Prototype___Mr_Cliargs;
$Mr_Cliargs->Get_Cliargs();

$Mr_Raw_Input=new Prototype___Mr_Raw_Input;
$Mr_Raw_Input->Fetch_Raw_Input();

$Mr_Standardized_Input=new Prototype___Mr_Standardized_Input;
$Mr_Standardized_Input->Standardize_Raw_Input();

$Mr_Valichecker=new Prototype___Mr_Valichecker;
$Mr_Valichecker->Do_Valicheck_of_Input();



$input=$Mr_Standardized_Input->standardized_input;

$Icon_Fillepath=$input["icon_fillepath"];

$count_up_or_down=$input["count_up_or_down"];
$dest_datetime=$input["dest_datetime"];
$dest_datetime_format=$input["dest_datetime_format"];

$past_dest_datetime=false;
$should_show_start_and_end_times=$input["should_show_start_and_end_times"];



if ($dest_datetime)
{
	$now_unixtime=time();
	
	if ( $dest_datetime < $now_unixtime)
	{
		$past_dest_datetime=true;
		#echo date("r");
		$Start_Time=$now_unixtime-$dest_datetime;
		$Total_Time=0;
	}
	else
	{
		$Total_Time=$dest_datetime-$now_unixtime;
		$Start_Time=0;
	}
}
else
{
	$Total_Time=Calculate_Seconds_in_Hours_Mins_Secs($input["total_hours"], $input["total_mins"], $input["total_secs"]);

	if ($Total_Time==0)
	{
		$Total_Time=Calculate_Seconds_in_Hours_Mins_Secs($Default_Total_Hours, $Default_Total_Mins, $Default_Total_Secs);
	}



	if ( $input["start_hours"] || $input["start_mins"] || $input["start_secs"] )
	{
		$Start_Time=Calculate_Seconds_in_Hours_Mins_Secs($input["start_hours"], $input["start_mins"], $input["start_secs"]);
		
		if ($count_up_or_down=="down")
		{
			$Start_Time=$Total_Time-$Start_Time;
		}
	}
	else
	{
		$Start_Time=0;
	}




	if ( $Start_Time > $Total_Time )
	{
		$abort_message="Aborting!  Start time ($Start_Time seconds) may not be larger than total time ($Total_Time seconds).";
		
		echo "\n\n$abort_message.\n";
		
		Alert("$abort_message");
		exit(1);
	}
}


$Note=$input["note"];
$Title=$input["title"];
$shell_command_to_exec_when_timer_done=$input["shell_command_to_exec_when_timer_done"];



$Names_of_Args_for_Timer=array("count_up_or_down", "Total_Time", "Start_Time", "Title", "Note", "shell_command_to_exec_when_timer_done", "dest_datetime", "dest_datetime_format", "past_dest_datetime", "should_show_start_and_end_times", "Icon_Fillepath");


foreach ($Names_of_Args_for_Timer as $arg)
{
	$Args_for_Timer["$arg"] = ${$arg};
}


Start_Timer( $Args_for_Timer );


Gtk::main();