#!/usr/bin/perl
#Copyright (c) 2008, Zane C. Bowers
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without modification,
#are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
#IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
#BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
#DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
#THE POSSIBILITY OF SUCH DAMAGE.

use strict;
use warnings;
use Getopt::Std;
use Image::Size::FillFullSelect;;
use ZConf;

$Getopt::Std::STANDARD_HELP_VERSION=1;

#version function
sub main::VERSION_MESSAGE {
	print "zbgset 3.0.0\n";
};

#print help
sub main::HELP_MESSAGE {
	print "\n".
		"-r  choose a random background\n".
		"-f <file>  choose a set the bg as\n".
		"-l  use the last BG\n".
		"\n".
		"-F <filltype> fill type to use\n".
		"-s <set>  What set to use.\n".
		"\n".
		"FILL TYPES\n".
		"center\n".
		"tile\n".
		"full\n".
		"fill\n"; 	
};

#process
sub lastprocess{
	my $last=$_[0];
	my $file=$_[1];
	my $filltype=$_[2];
	my $numberoflast=$_[3];
	
	#gets the hostname
	my $hostname=`hostname`;
	if(!defined($hostname)){
		$hostname="localhost";
	}else{
		chomp($hostname);
	};
	
	#creates the fist line of the new last
	my $newlast=$hostname.$ENV{DISPLAY}.":".$filltype.":".$file."\n";
	
	my @lastA=split(/\n/, $last);

	my $lastInt=0;
	while(defined($lastA[$lastInt])){
		if($lastInt <= $numberoflast){
			$newlast=$newlast.$lastA[$lastInt]."\n";
		};

		$lastInt++;
	};
	
	return $newlast;
};

##
##find everything in the path
##
sub findInPath{
	my $path=$_[0];
	my $command='find \''.$path.'\' -type f';
	my @files=`$command`;
	
	return @files;
}

#gets the options
my %opts=();
getopts('F:f:lrs:', \%opts);
my %args;

#if -F is used, make sure it is a legit value
if(defined($opts{F})){
	my @filltypes=("auto", "center", "fill", "full", "tile"); #holds the various support fill types
	my $filltypesInt=0;#used for intering through @filltypes
	my $found=undef;#set to true when found
	while(defined($filltypes[$filltypesInt])){
		#checks for a match
		if($opts{F} eq $filltypes[$filltypesInt]){
			$found=1;
		};
		$filltypesInt++;
	};
	#exits if it is not supported
	if(!$found){
		warn("zbgset:6: '".$opts{F}."' is not a support fill type.");
		exit 6;
	};
};

#makes sure the file exists if -f is used.
if(defined($opts{f})){
	if(!-f $opts{f}){
		warn("zbgset:5: '".$opts{f}."' does not exist or is not a file.");
		exit 5;
	};
};

#inits zconf
my $zconf = ZConf->new();
if($zconf->{error}){
	warn("zbgset:1: Could not initiate ZConf. It failed with '".$zconf->{error}."'".
		", '".$zconf->{errorString}."'.");
	exit 1;
}

#handles set checking
if(defined($opts{s})){
	if(!$zconf->setNameLegit($opts{s})){
		warn("zbgset:4: '".$opts{s}."' is not a legit ZConf set name.");
		exit 11;
	}else{
		$args{set}=$opts{s};
	};
}else{
	$args{set}=undef;
};

#create the config if it does not exist
#if it does exist, make sure the set we are using exists
my $returned = $zconf->configExists("zbgset");
if(!$returned){
	warn("zbgset: Has not been run before. Initiating config.");
	$returned = $zconf->createConfig("zbgset");
	if($zconf->{error}){
		warn("zbgset:2: Could not create config.");
		exit 2;
	};
	#inits it
	my $returned=$zconf->writeSetFromHash({config=>"zbgset", set=>$args{set}},
						{
							savelast=>"true",
							filltype=>"auto",
							numberoflast=>"15",
							postSetRefresh=>"false",
							postSetRefresher=>"zbgfbmb -l",
							maxdiff=>".2",
							filltype=>"auto",
							full=>'hsetroot -full \'%%%THEFILE%%%\'',
							tile=>'hsetroot -tile \'%%%THEFILE%%%\'',
							fill=>'hsetroot -fill \'%%%THEFILE%%%\'',
							center=>'hsetroot -center \'%%%THEFILE%%%\'',
							path=>'default'
						}
					);
	warn("zbgset: Initilization succeeded.");
}else{
	#gets the set name if it is not defined
	if(!defined($args{set})){
		$args{set}=$zconf->chooseSet("zbgset");
	};
	
	#gets the list of sets
	my @sets=$zconf->getAvailableSets("zbgset");
	if($zconf->{error}){
		warn("zbgset:3: Could not get a list of sets.");
		exit 3;
	};
	
	#creates the default config if it the specified set has not been created before
	my $setsInt=0; #used for intering through @sets
	my $found=undef;
	while(defined($sets[$setsInt])){
		#sets $found to true if the set is found
		if($sets[$setsInt] eq $args{set}){
			$found=1;
		};
		$setsInt++;
	};

	#if found is false, initialize the set
	if(!$found){
		warn("zbgset: The specified or auto choosen set, '".$args{set}.
				"' has not been initialized before. Doing so now.");
		#inits it
		my $returned=$zconf->writeSetFromHash({config=>"zbgset", set=>$args{set}},
						{
							savelast=>"1",
							filltype=>"auto",
							numberoflast=>"15",
							postSetRefresh=>"0",
							postSetRefresher=>"zbgfbmb -l",
							maxdiff=>".2",
							filltype=>"auto",
							full=>'hsetroot -full \'%%%THEFILE%%%\'',
							tile=>'hsetroot -tile \'%%%THEFILE%%%\'',
							fill=>'hsetroot -fill \'%%%THEFILE%%%\'',
							center=>'hsetroot -center \'%%%THEFILE%%%\'',
							path=>'default',
							override=>'default',
							last=>''
						}
					);
		warn("zbgset: Initilization succeeded for set '".$args{set}."'.");
	};
};

$returned = $zconf->read({config=>"zbgset", set=>$args{set}});
if($zconf->{error}){
	warn("zbgset:3: Could not read config. It failed with '".$zconf->{error}."'".
		", '".$zconf->{errorString}."'.");
	exit 3;
};

#sets up the setter to be used
if(defined($opts{F})){
	$args{filltype}=$opts{F};
}else{
	$args{filltype}=$zconf->{conf}{zbgset}{filltype};
};

#handles it if -f is specified
if(defined($opts{f})){

	if($args{filltype} eq "auto"){
		my $iffs = Image::Size::FillFullSelect->new();
		$args{filltype} = $iffs->select($opts{f});
		if(!defined($args{filltype})){
			warn("zbgset:7: Auto selection for the image size failed. Image::Size".
				"does not regard the file, '".$opts{f}."', as a image.");
			exit 7;
		};
	};

	#get the setter to use and replace %%%THEFILE%%% with the file name.
	my $setter=$zconf->{conf}{zbgset}{$args{filltype}};
	$setter=~s/%%%THEFILE%%%/$opts{f}/;
	
	
	system($setter);
	if($? ne "0"){
		warn("zbgset:8: Setter, '".$setter."', failed with ".$?.".");
		exit 8;
	};
	
	#makes sure last is a defined variable
	if(!defined($zconf->{conf}{zbgset}{last})){
		$zconf->setVar("zbgset", "last", "");
	};

	$zconf->{conf}{zbgset}{last}=lastprocess($zconf->{conf}{zbgset}{last}, $opts{f},
									$args{filltype}, $zconf->{conf}{zbgset}{numberoflast});
	
	#saves the last
	$returned=$zconf->writeSetFromLoadedConfig({config=>"zbgset"});
	if($zconf->{error}){
		warn("zbgset:9: Could not save last information. It failed with '".$zconf->{error}."'".
			", '".$zconf->{errorString}."'.");
		exit 9;
	};
	exit 0;
};

#sets the last background
if(defined($opts{l})){
	my @lastA=split(/\n/, $zconf->{conf}{zbgset}{last});
	my @lastsplit=split(/:/, $lastA[0]);

	#get the setter to use and replace %%%THEFILE%%% with the file name.
	my $setter=$zconf->{conf}{zbgset}{$lastsplit[2]};
	$setter=~s/%%%THEFILE%%%/$lastsplit[3]/;	

	system($setter);
	if($? ne "0"){
		warn("zbgset:8: Setter, '".$setter."', failed with ".$?.".");
		exit 8;
	};
	exit 0;
};

#handles if if a random background is selected
if(defined($opts{r})){
	if(defined($opts{p})){
		$args{path}=$opts{p};
	}else{
		$args{path}=$zconf->{conf}{zbgset}{path};
	};

	my %paths=$zconf->regexVarGet("zbgset", "^paths/");

	if(!defined($paths{"paths/".$args{path}})){
		warn("zbgset:10: The requested path, '".$args{path}."' is not defined");
	};
	
	my @pathsA=split(/\n/ ,$paths{"paths/".$args{path}});
	
	#gets which path to use
	my $randomPathInt=rand($#pathsA);
	$randomPathInt =~ s/\.[0123456789]*//;
	
	my @files=findInPath($pathsA[$randomPathInt]);

	my $randomFilesInt=rand($#files);
	$randomFilesInt =~ s/\.[0123456789]*//;

	#removes any \n at the end of the line
	chomp($files[$randomFilesInt]);

	if($args{filltype} eq "auto"){
		my $iffs = Image::Size::FillFullSelect->new();
		$args{filltype} = $iffs->select($files[$randomFilesInt]);
		if(!defined($args{filltype})){
			warn("zbgset:7: Auto selection for the image size failed. Image::Size".
				"does not regard the file, '".$files[$randomFilesInt]."', as a image");
			exit 7;
		};
	};
	
	#get the setter to use and replace %%%THEFILE%%% with the file name.
	my $setter=$zconf->{conf}{zbgset}{$args{filltype}};
	$setter=~s/%%%THEFILE%%%/$files[$randomFilesInt]/;

	system($setter);
	if($? ne "0"){
		warn("zbgset:8: Setter, '".$setter."', failed with ".$?.".");
		exit 8;
	};

	#saves it
	$zconf->{conf}{zbgset}{last}=lastprocess($zconf->{conf}{zbgset}{last}, $files[$randomFilesInt],
									$args{filltype}, $zconf->{conf}{zbgset}{numberoflast});
	
	#saves the last
	$returned=$zconf->writeSetFromLoadedConfig({config=>"zbgset"});
	if($zconf->{error}){
		warn("zbgset:9: Could not save last information. It failed with '".$zconf->{error}."'".
			", '".$zconf->{errorString}."'.");
		exit 9;
	};


	exit 0;
};

exit 1;

=head1 NAME

zbgset - A perl program for managing the background.

=head1 SYNOPSIS

zbgset (B<-f> <file>|B<-r>|B<-l>) [B<-F> <fill type>] [B<-s> <ZConf set>] 

=head1 USAGE

Either -f, -r, or -l need used.

This does not actually set the background, but calls another program to do it,
but what makes it useful is it allows for random images to be used as well as
the last image to be called. This makes it useful for setting the image upon
login or changing it regullarly through cron.

When it is ran for the first time it creates a ZConf config named "zbgset" used
store the settings.

=head1 ARGUEMENTS

=head2 B<-f> <file>

This specifies a file to use.

=head2 B<-F> <fill type>

This specifies the fill type to use.

=head2 B<-l>

Use the last image.

=head2 B<-s> <ZConf set>

This is the ZConf set to use for it. If the set has not been initiated before it
will do so.

=head1 ZConf Keys

=head2 center

This contains the setter that will be used for when setting a centered image.
'%%%THEFILE%%%' is replaced at runtime with the name of the file.

	center=hsetroot -center '%%%THEFILE%%%'

=head2 fill

This key contains setter to be used for fill the background with a resized image.
'%%%THEFILE%%%' is replaced at runtime with the name of the file.

	fill=hsetroot -fill '%%%THEFILE%%%'

=head2 full

This key contains setter to be used for fill the background with a scaled image.
'%%%THEFILE%%%' is replaced at runtime with the name of the file.

	full=hsetroot -full '%%%THEFILE%%%'

=head2 last

This contains the last several images set. There is one entry per line. The format
is as below.

	<hostname>:<display>:<fill type>:<image>

=head2 maxdiff

This contains the maximum difference for between any two any two sides when choosing
between fill and full.

	maxdiff=.2

=head2 numberoflast

The number of last entries to save.

=head2 path

This is the path to use for when selecting a random image.

=head2 paths/<path>

This is a path. Each path have multiple paths. Each path is seperated by a new line.

=head2 postSetRefresh

Wether or not it should run something after it has been set. This is a perl boolean value.

	postSetRefresh=0

=head2 postSetRefresher

If 'postSetRefresh' is set to true, this is ran.

=head2 tile

This key contains setter to be used for tiling. '%%%THEFILE%%%' is replaced at
runtime with the name of the file.

	tile=hsetroot -tile '%%%THEFILE%%%'

=head1 ERROR CODES

=head2 1

Could not initial ZConf.

=head2 2

Could not create config.

=head2 3

Could not get set information.

=head2 4

-f and -r switches both used when calling program.

=head2 5

The specified file does not exist or is not a file.

=head2 6

The specified fill type is not supported.

=head2 7

Auto selection for the image size failed. Image::Size does not regard the file as a image.

=head1 8

Execution of the setter failed.

=head1 9

Saving the last information failed.

=head1 10

Requested path is not defined.

=head2 11

The set name you specified with '-s' is not a legit name for a ZConf set.

=head1 AUTHOR

Copyright (c) 2006, Zame C. Bowers <vvelox@vvelox.net>

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS` OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=head1 SCRIPT CATEGORIES

Desktop

=head1 OSNAMES

any

=head1 README

zbgset - A perl program for managing the background.

=cut