#Autodisplay.pm
#Date: 5/11/2005
#Purpose: To allow automatic display brightness on-off at specific times.
#Author: Felix Mueller <felix.mueller(at)gwendesign.com>
#Redesigned for Slimserver 6.0+ by Tobias Goldstone <tgoldstone(at)familyzoo.net>
#
#	Copyright (c) 2004 - 2006 GWENDESIGN
#	All rights reserved.
#
#       Based on AlarmClock
#	And based on Autodisplay Revision .2 by Felix Mueller
#
#	History:
#	2006/12/24 v0.82 Minor bug fix.
#	2006/12/10 V0.81 24H time format does not save in Web UI
#	2006/11/12 V0.8 - Various bugfixes
#	2006/09/24 v0.7 - Brighness level now selectable
#			  (Idea by Philip Ivanier, programmed by Tobias Goldstone)
#	2006/08/85 v0.6	- SS 6.5 beta ready
#	2006/05/20 v0.5 - Some cleanup from Daryle, Thanks.
#	2005/11/07 v0.41 - Added Web UI
#	2005/11/02 v0.4a - The display is shutoff whenever it is in the 'display off'
#	                    time window, as opposed to only at the rollover times.
#	                    It also does not blank when 'on' and presumably in use.
#	                    (Thanks to Daryle A. Tilroe)
#
#	2005/10/31 v0.4 - Fix a bug (Thanks to Daryle and LJ for reporting)
#	2005/05/11 v0.3 - Cleaned code and allowed use with Slimserver 6.0+
#	
#	2004/05/15 v0.2	- Make use of the server integrated functions:
#	                    timeDigits and scrollTime
#	2004/04/27 v0.1	- Initial version
#	----------------------------------------------------------------------
#       To do:
#
#       - Multi language
#
#
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation; either version 2 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 General Public License for more details.
#
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
#	02111-1307 USA
#

package Plugins::AutoDisplay;

use strict;
use Slim::Utils::Strings qw (string);
use Time::HiRes;
use Slim::Utils::Misc;

use vars qw($VERSION);
$VERSION = substr(q$Revision: 0.82 $,10);

sub strings  { return '
PLUGIN_AUTODISPLAY_NAME
	EN	Auto Dim Display
PLUGIN_ON_SET
	EN	Set display on time
PLUGIN_OFF_SET
	EN	Set display off time
PLUGIN_BRIGHTNESS_SET
	EN	Set brightness level
PLUGIN_AUTODIM_DESC
	EN	Set automatic display on/off
PLUGIN_FLAG_ON_OFF
	EN	Turn plugin On or Off
PLUGIN_TURNING_OFF
	EN	Plugin off
PLUGIN_TURNING_ON
	EN	Plugin on
SETUP_GROUP_PLUGIN_PLUGINNAME
	EN	Auto Dim Display
SETUP_GROUP_PLUGIN_PLUGINNAME_DESC
	EN	Automatically turns off display at set time
PLUGIN_REJECT_INTRO
	EN	Value rejected
PLUGIN_REJECT_MSG
	EN	You have entered an incorrect value
PLUGIN_BRIGHTNESS_DARK
	EN	Dark
PLUGIN_BRIGHTNESS_BRIGHTEST
	EN	Brightest';
}

sub getDisplayName {
	return 'PLUGIN_AUTODISPLAY_NAME'
};

Slim::Buttons::Common::addMode('AutoDisplay', getFunctions(), \&Plugins::AutoDisplay::setMode);

my @browseMenuChoices;
my %menuSelection;
our %functions = ();
our $brightness = ();

setTimer();

sub setMode() {
	my $client = shift;
	@browseMenuChoices = (
		$client->string('PLUGIN_FLAG_ON_OFF'),
		$client->string('PLUGIN_OFF_SET'),
		$client->string('PLUGIN_ON_SET'),
		$client->string('PLUGIN_BRIGHTNESS_SET')
		);
	
	if (!defined($menuSelection{$client})) { $menuSelection{$client} = 0; };
	$client->lines(\&lines);

	#Set client default
	my $flag = $client->prefGet('PLUGIN.autodisplay_flag');
	if (!defined($flag)) {$client->prefSet('PLUGIN.autodisplay_flag',0); }
	

	#get previous set on time or set a default
	my $ontime = $client->prefGet('PLUGIN.autodisplay_on_time');
	if (!defined($ontime)) {$client->prefSet('PLUGIN.autodisplay_on_time',8 * 60 * 60); }

	#get previous set off time or set a default
	my $offtime = $client->prefGet('PLUGIN.autodisplay_off_time');
	if (!defined($offtime)) {$client->prefSet('PLUGIN.autodisplay_off_time',20 * 60 * 60); }

	#get brightness level or set a default
	my $brightness = $client->prefGet('PLUGIN.autodisplay_brightness');
	if (!defined($brightness)) {$client->prefSet('PLUGIN.autodisplay_brightness',0); }
}
   
sub lines {
	my $client = shift;
	my ($line1, $line2, $overlay2);

	$overlay2 = overlay($client);
	
	$line1 = $client->string('PLUGIN_AUTODISPLAY_NAME') . " (" . ($menuSelection{$client}+1) . " " . $client->string('OF') . " " . ($#browseMenuChoices + 1) . ")";

	$line2 = $browseMenuChoices[$menuSelection{$client}];
	
	return ($line1, $line2, undef, $overlay2);
}

sub overlay {
	my $client = shift;
	
	return Slim::Display::Display::symbol( 'rightarrow');

	return undef;
}

sub getFunctions { return \%functions;}

sub initPlugin {
    %functions = (
    	'up' => sub  {
    		my $client = shift;
    		my $newposition = Slim::Buttons::Common::scroll($client, -1, ($#browseMenuChoices + 1), $menuSelection{$client});
    
    		$menuSelection{$client} =$newposition;
    		$client->update();
    	},
    	'down' => sub  {
    		my $client = shift;
    		my $newposition = Slim::Buttons::Common::scroll($client, +1, ($#browseMenuChoices + 1), $menuSelection{$client});
    
    		$menuSelection{$client} =$newposition;
    		$client->update();
    	},

# Need more information about knob first, like how to make it stop at the end of the list
#	'knob' => sub {
#    		my $client = shift;
#
#    		my $newposition = Slim::Buttons::Common::scroll($client, $client->knobPos() - $menuSelection{$client}, ($#browseMenuChoices + 1), $menuSelection{$client});
#    
#    		$menuSelection{$client} =$newposition;
#    		$client->update();
#	},

    	'left' => sub  {
    		my $client = shift;
    
    		Slim::Buttons::Common::popModeRight($client);
    	},
    	'right' => sub {
    		my $client = shift;
###    		my @oldlines = Slim::Display::Display::curLines($client);
    		my @oldlines = $client->curLines();

			if ($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_FLAG_ON_OFF')) {
				my $flag = $client->prefGet('PLUGIN.autodisplay_flag');
				
			#Turn on or off plugin using player menu
			#If plugin is 'ON' then set to 'OFF' and display message
				if ($flag ==1) {
					
					$client->prefSet('PLUGIN.autodisplay_flag','0');
					my $newflag = $client->prefGet('PLUGIN.autodisplay_flag');
					$client->showBriefly($client->string('PLUGIN_TURNING_OFF'),'');
				}	
			#If plugin has been set to'OFF' then set to 'ON'
				else {
				
					$client->prefSet('PLUGIN.autodisplay_flag','1');
					$client->showBriefly($client->string('PLUGIN_TURNING_ON'),'');
					my $displayflag = $client->prefGet('PLUGIN.autodisplay_flag');
					
				}
			}

			#Set time to turn off display
			elsif($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_OFF_SET')) {
				
				my %params = (
					'header' => $client->string('PLUGIN_OFF_SET'),
					'valueRef' => $client->prefGet('PLUGIN.autodisplay_off_time'),
					'cursorPos' => 0,
					'callback' => \&settingsExitHandler
				);
				Slim::Buttons::Common::pushModeLeft($client, 'INPUT.Time',\%params);
			}

			#Set time for plugin to turn on display	
			elsif ($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_ON_SET')) {
    			my %params = (
    					'header' => $client->string('PLUGIN_ON_SET'),
    					'valueRef' => $client->prefGet('PLUGIN.autodisplay_on_time'),
    					'cursorPos' => 0,
    					'callback' => \&settingsExitHandler
    				);
    				Slim::Buttons::Common::pushModeLeft($client, 'INPUT.Time',\%params);
			}
			elsif ($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_BRIGHTNESS_SET')) {
    			my %params = (
						'listRef' => ['0','1','2','3','4'],
						'externRef' => [string('BRIGHTNESS_DARK'),1,2,3,string('BRIGHTNESS_BRIGHTEST')],
    					'header' => $client->string('PLUGIN_BRIGHTNESS_SET'),
    					'valueRef' => $client->prefGet('PLUGIN.autodisplay_brightness'),
    					'cursorPos' => 0,
    					'callback' => \&settingsExitHandler,
    				);
				Slim::Buttons::Common::pushModeLeft($client, 'INPUT.List',\%params);
			}
    	},
    	'play' => sub {
    		my $client = shift;
    	},
    );

	setTimer();
}

sub settingsExitHandler {
	my ($client,$exittype) = @_;

	$exittype = uc($exittype);

	if ($exittype eq 'LEFT') {
		if ($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_ON_SET')) {
			my $value = ${$client->param('valueRef')};

			#$::d_plugins && msg ("my value is $value\n");

			$client->prefSet('PLUGIN.autodisplay_on_time',${$client->param('valueRef')});
    			Slim::Buttons::Common::popModeRight($client);
		}
		elsif ($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_OFF_SET')){
			$client->prefSet('PLUGIN.autodisplay_off_time',${$client->param('valueRef')});
    			Slim::Buttons::Common::popModeRight($client);
		}
		elsif ($exittype eq 'RIGHT') {
			$client->bumpRight();
		}
		elsif ($browseMenuChoices[$menuSelection{$client}] eq $client->string('PLUGIN_BRIGHTNESS_SET')){
			$client->prefSet('PLUGIN.autodisplay_brightness',${$client->param('valueRef')});
    			Slim::Buttons::Common::popModeRight($client);
		}
		else {
		return;
		}
	}
}
sub setTimer {

	# timer to check alarms on an 60 second interval
	Slim::Utils::Timers::setTimer(0, Time::HiRes::time() + 60, \&checkOnOff);
}

sub checkOnOff {
	
	$::d_plugins && msg("Checking timer Autodisplay plugin\n");
	
	foreach my $client (Slim::Player::Client::clients()) {
		my $ontime =  $client->prefGet('PLUGIN.autodisplay_on_time');
		my $offtime =  $client->prefGet('PLUGIN.autodisplay_off_time');
		my $flag = $client->prefGet('PLUGIN.autodisplay_flag');
		my $brightness = $client->prefGet('PLUGIN.autodisplay_brightness');
		my $clientname = $client->prefGet('playername');

		my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(&getFuzzyTime($client));
		my $time = $hour * 60 * 60 + $min * 60;

		#If client has autodisplay on/off preference and times set then continue...
		if (defined($flag) && defined($offtime) && defined($ontime)) {
			#If autodisplay has been set to "ON" then continue...
    			if ($flag==1) {

				#Get client power state
    				my $power = $client->prefGet('power');
				
        			if ( !$power && ( ($offtime > $ontime && ($time >= $offtime || $time <= $ontime)) || ($offtime < $ontime && $time >= $offtime && $time <= $ontime) ) ) {
					
					$::d_plugins && msg("PLUGIN.Autodisplay - $clientname...Hiding display\n");
					$::d_plugins && msg("PLUGIN.Autodisplay - $clientname brightness set to $brightness\n");
        				#Set client brightness off
        				$client->brightness($brightness);
       			}
        	  
        			elsif (!$power) {
       			
           				#Reset display brightness to user preference.
        				$client->brightness($client->prefGet('powerOffBrightness'));
        			}
        		}			
		}
	}
	setTimer();
}
sub setupGroup {
	my $client = shift;
	my %group = (
		'PrefOrder' => ['PLUGIN.autodisplay_flag','PLUGIN.autodisplay_off_time','PLUGIN.autodisplay_on_time','PLUGIN.autodisplay_brightness']
		,'GroupHead' => Slim::Utils::Strings::string('SETUP_GROUP_PLUGIN_PLUGINNAME')
		,'GroupDesc' => Slim::Utils::Strings::string('SETUP_GROUP_PLUGIN_PLUGINNAME_DESC')
		,'GroupLine' => 1
		,'GroupSub'  => 1
		,'PrefsInTable' => 1
		,'Suppress_PrefHead' => 1
		,'Suppress_PrefDesc' => 1
		,'Suppress_PrefLine' => 1
		,'Suppress_PrefSub' => 1
	);
	my %prefs = (
		'PLUGIN.autodisplay_flag' => {
###			'validate' => \&Slim::Web::Setup::validateTrueFalse
			'validate' => \&Slim::Utils::Validate::trueFalse
			,'currentValue' => sub {
				my $client = shift;
				my $val = $client->prefGet('PLUGIN.autodisplay_flag');
				if (!defined $val) {
							$client->prefSet('PLUGIN.autodisplay_flag',0);
						}

				return $val;
			}
			
			,'PrefHead' => ' '
			,'PrefChoose' => string('PLUGIN_FLAG_ON_OFF').string('COLON')
			,'changeIntro' => string('PLUGIN_FLAG_ON_OFF').string('COLON')
			,'options'  => {
				'1' => string('ON'),
				'0' => string('OFF'),		
			}
		},

		'PLUGIN.autodisplay_on_time' => {
###			'validate' => \&Slim::Web::Setup::validateTime
			'validate' => \&Slim::Utils::Validate::isTime
			,'validateArgs' => [0,undef]
			,'PrefHead' => ' '
			,'PrefChoose' => string('PLUGIN_ON_SET').string('COLON')
			,'rejectIntro' => string('PLUGIN_REJECT_INTRO').string('COLON').string('PLUGIN_ON_SET')
			,'rejectMSG' => string('PLUGIN_REJECT_MSG')
			,'changeIntro' => string('PLUGIN_ON_SET').string('COLON')
			,'currentValue' => sub {
				my $client = shift;
				return if (!defined($client));
				my $time =  $client->prefGet('PLUGIN.autodisplay_on_time');
				my ($h0, $h1, $m0, $m1, $p) = Slim::Buttons::Common::timeDigits($client,$time);
				my $timestring = ((defined($p) && $h0 == 0) ? ' ' : $h0) . $h1 . ":" . $m0 . $m1 . " " . (defined($p) ? $p : '');
				
				return $timestring;
										
			}
			,'onChange' => sub {
				my ($client,$changeref,$paramref,$pageref) = @_;
				return if (!defined($client));
				my $time = $changeref->{'PLUGIN.autodisplay_on_time'}{'new'};
				my $newtime = 0;
				$time =~ s{
					^(0?[0-9]|1[0-9]|2[0-4]):([0-5][0-9])\s*(P|PM|A|AM)?$
				}{
					if (defined $3) {
						$newtime = ($1 == 12?0:$1 * 60 * 60) + ($2 * 60) + ($3 =~ /P/?12 * 60 * 60:0);
					} else {
						$newtime = ($1 * 60 * 60) + ($2 * 60);
					}
				}iegsx;
				$client->prefSet('PLUGIN.autodisplay_on_time',$newtime);
			}
		},
		'PLUGIN.autodisplay_off_time' => {
###			'validate' => \&Slim::Web::Setup::validateAcceptAll
			'validate' => \&Slim::Utils::Validate::isTime
			,'PrefHead' => ' '
			,'PrefChoose' => string('PLUGIN_OFF_SET').string('COLON')
			,'changeIntro' => string('PLUGIN_OFF_SET').string('COLON')
			,'rejectIntro' => string('PLUGIN_REJECT_INTRO').string('COLON').string('PLUGIN_OFF_SET')
			,'rejectMSG' => string('PLUGIN_REJECT_MSG')
			,'validateArgs' => [0,undef]
			,'currentValue' => sub {
				my $client = shift;
				return if (!defined($client));
				my $time =  $client->prefGet('PLUGIN.autodisplay_off_time');
				my ($h0, $h1, $m0, $m1, $p) = Slim::Buttons::Common::timeDigits($client,$time);
				my $timestring = ((defined($p) && $h0 == 0) ? ' ' : $h0) . $h1 . ":" . $m0 . $m1 . " " . (defined($p) ? $p : '');
				return $timestring;							
			}
			,'onChange' => sub {
				my ($client,$changeref,$paramref,$pageref) = @_;
				my $time = $changeref->{'PLUGIN.autodisplay_off_time'}{'new'};
				my $newtime = 0;
				
				$time =~ s{
					^(0?[0-9]|1[0-9]|2[0-4]):([0-5][0-9])\s*(P|PM|A|AM)?$
				}{
					if (defined $3) {
						$newtime = ($1 == 12?0:$1 * 60 * 60) + ($2 * 60) + ($3 =~ /P/?12 * 60 * 60:0);
					} else {
						$newtime = ($1 * 60 * 60) + ($2 * 60);
					}
				}iegsx;
				$client->prefSet('PLUGIN.autodisplay_off_time',$newtime);
			}
		},
		'PLUGIN.autodisplay_brightness' => {
			'validate'     => \&Slim::Utils::Validate::isInt
			,'validateArgs' => [0,1,2,3,4]
			,'optionSort'   => 'NK'
			,'options'      => \&getBrightnessOptions
			,'PrefHead' => ' '
			,'PrefChoose' => string('PLUGIN_BRIGHTNESS_SET').string('COLON')
			,'changeIntro' => string('PLUGIN_BRIGHTNESS_SET').string('COLON')
			,'rejectIntro' => string('PLUGIN_REJECT_INTRO').string('COLON').string('PLUGIN_BRIGHTNESS_SET')
			,'rejectMSG' => string('PLUGIN_REJECT_MSG')
			,'currentValue' => sub {
				my $client = shift;
				my $brightness =  $client->prefGet('PLUGIN.autodisplay_brightness');
				return $brightness;							
			}
			,'onChange' => sub {
				my ($client,$changeref,$paramref,$pageref) = @_;
				my $brightness = $changeref->{'PLUGIN.autodisplay_brightness'}{'new'};
				$client->prefSet('PLUGIN.autodisplay_brightness',$brightness);
			}
		}
	);
	
	return (\%group,\%prefs,1);

sub getBrightnessOptions {
	my %brightnesses = (
						'0' => '0 ('.string('PLUGIN_BRIGHTNESS_DARK').')',
						'1' => '1',
						'2' => '2',
						'3' => '3',
						'4' => '4 ('.string('PLUGIN_BRIGHTNESS_BRIGHTEST').')',
						);
	return \%brightnesses;
}

}

sub getFuzzyTime {
	my $client = shift;
	if ( defined($Plugins::FuzzyTime::Plugin::apiVersion) ) {
		return int(Plugins::FuzzyTime::Public::getClientTime($client));
	}
	return time();
}

1;

__END__	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      