#!/usr/bin/perl
#Copyright (c) 2007, 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;
use Config::Tiny;
use File::BaseDir qw/xdg_config_home/;

#version function
sub main::VERSION_MESSAGE {
	print "zbgset 1.1.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".
		"-p <path file>  pathfile override to use\n".
		"-o <override file>  override override to use\n".
		"-c <config file>  config file to use\n".
		"-P <profile>  profile to use in the config file\n".
		"\n".
		"FILL TYPES\n".
		"center\n".
		"tiled\n".
		"full\n".
		"fill\n"; 	
};

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

##
##config load
##
sub configload{
	my $file=$_[0];
	my $profile=$_[1];

	my %config=();

	#reads the file
	my $ini=Config::Tiny->read($file);

	#a list of possible variables to set
	my @variables=('lastfile', 'numberoflast', 'wmrefresh', 'wmrefresher', 'maxdiff', 'pathdir', 
		'filltype', 'full', 'tile', 'fill', 'center', 'path', 'override');
	
	my $variablesInt=0;#used for intering through @variables

	#runs through it reelling it in
	$variablesInt=0;
	while(defined($variables[$variablesInt])){
		$config{$variables[$variablesInt]}=$ini->{_}->{$variables[$variablesInt]};
		$variablesInt++;
	};

	#runs through it reelling it in
	$variablesInt=0;
	if ($profile ne "_"){
		while(defined($variables[$variablesInt])){
			$config{$variables[$variablesInt]}=$ini->{$profile}->{$variables[$variablesInt]};
			$variablesInt++;
		};
	};
	
	#sets the last file, if not set
	if(!defined($config{lastfile})){
		$config{lastfile}=xdg_config_home."/zbg-1/var/last";
	};
	
	#sets the numberoflast, if not defined
	if(!defined($config{numberoflast})){
		$config{numberoflast}="15";
	};

	#sets the wmrefresh if not defined
	if(!defined($config{wmrefresh})){
		$config{wmrefresh}="true";
	};

	#sets the wmrfresher if not defined
	if(!defined($config{wmrefresher})){
		$config{wmrefresher}="true";
	};

	#sets the maxdiff, if not defined
	if(!defined($config{maxdiff})){
		$config{maxdiff}=".2";
	};

	#sets the pathdir, if not set
	if(!defined($config{pathdir})){
		$config{pathdir}=xdg_config_home."/zbg-1/paths";
	};

	#define override if it does not exist
	if (!defined($config{override})){
		$config{override}=xdg_config_home."/zbg-1/overrides/default";
	};

	#sets the filltype, if not set
	if(!defined($config{filltype})){
		$config{filltype}="auto";
	};

	#sets the full, if not set
	if(!defined($config{full})){
		$config{full}='hsetroot -full \'%%%THEFILE%%%\'';
	};

	#sets the full, if not set
	if(!defined($config{tile})){
		$config{tile}='hsetroot -tile \'%%%THEFILE%%%\'';
	};

	#sets the full, if not set
	if(!defined($config{fill})){
		$config{fill}='hsetroot -fill \'%%%THEFILE%%%\'';
	};

	#sets the center, if not set
	if(!defined($config{center})){
		$config{center}='hsetroot -center \'%%%THEFILE%%%\'';
	};

	#sets the path, if not set
	if(!defined($config{path})){
		$config{path}='default';
	};

	my $myhostname=`hostname`;
	chomp($myhostname);
	if(!defined($myhostname)){
		$myhostname='localhost';
	};

	$config{hostname}=$myhostname;

	#runs through it reelling it in
	$variablesInt=0;
	while(defined($variables[$variablesInt])){
		$config{$variables[$variablesInt]}=~ s/\%\%\%HOSTNAME\%\%\%/$myhostname/g;
		$config{$variables[$variablesInt]}=~ s/~/$ENV{HOME}/g;
		$config{$variables[$variablesInt]}=~ s/\%\%\%DISPLAY\%\%\%/$ENV{DISPLAY}/g;
		$variablesInt++;
	};
	
	$config{display}=$ENV{DISPLAY};
	
	return %config;
};

##
##creates the base
##
sub initConfig{
	mkdir(xdg_config_home."/zbg-1/");
	mkdir(xdg_config_home."/zbg-1/configs");
	system('touch '.xdg_config_home."/zbg-1/configs/default");
	mkdir(xdg_config_home."/zbg-1/exclude");
	system('touch '.xdg_config_home."/zbg-1/exclude/default");
	mkdir(xdg_config_home."/zbg-1/overrides");
	system('touch '.xdg_config_home."/zbg-1/overrides/default");
	mkdir(xdg_config_home."/zbg-1/var");
	system('touch '.xdg_config_home."/zbg-1/var/default");
	mkdir(xdg_config_home."/zbg-1/paths");
	system('touch '.xdg_config_home."/zbg-1/paths/default");
	
	return;
};

##
##decides if it should use full or fill maximization
##
sub autodecide{ #arg1=the image to use	arg2=the max difference
	my ($image, $maxdiff)=($_[0], $_[1]);
	my ($imageX, $imageY)=imgsize("$image");
#	print"$imageX $imageY\n";
	if (! $imageX){
		die($image." is not a image\n");
	};
	#this needs done twice since one direction only can't be trusted...
	my $ixydiff1=$imageX/$imageY - 1; #gets the scale difference...
	$ixydiff1=abs($ixydiff1); #make the scale differ into a absolute value for easier use
	my $ixydiff2=$imageY/$imageX - 1; #gets the scale in the other directions...
	$ixydiff2=abs($ixydiff2); #abs the other one also
#	print "$ixydiff1\n$ixydiff2\n";
	if ($ixydiff1 <= $maxdiff || $ixydiff2 <= $maxdiff){
		return "fill";
	}else{
		return "full";
	};
};

##
##load last image
##
sub lastimage{
	my $lastfile=$_[0];
	
	#gets the raw last file
	open("lastfile", $lastfile)||die("Could not open ".$lastfile."!\n");
	my @rawlast=<lastfile>;
	close("lastfile");

	my @lastsplit=split(/:/, $rawlast[0]);
	
	my @return=($lastsplit[2], $lastsplit[3]);
	chomp($return[1]);

	return @return;
};

##
##override
##
sub override{
	my $overrideFile=$_[0];
	my $file=$_[1];

	$overrideFile=~s/\~/$ENV{HOME}/g;

	#0=type
	#1=maxdiff
	my @return=();
	#open the file, but don't exit... just return if it can't be opened
	open("theoverridefile", $overrideFile)||return @return;
	my @overrides=<theoverridefile>;
	close("theoverridefile");

	#runs through eachline parsing each till it finds a hit
	#type:maxdiff:regexp
	my $overridesInt=0;
	while(defined($overrides[$overridesInt])){
		my @line=split(/:/, $overrides[$overridesInt]);

		my $type=$line[0];
		my $maxdiff=$line[1];
		my $regexp=$line[2];

		#reassemble the regexp if it has any : in it
		my $lineInt=3;
		while(defined($line[$lineInt])){
			$regexp=$regexp.":".$line[$lineInt];
			$lineInt++;
		};
		chomp($regexp);

		#test the file
		my $test=$file;
#print "test=".$test."\n";
#print "regexp=".$regexp."\n";
		$test =~ s/$regexp//g;
#print "test=".$test."\n";
		if ($test ne $file){
			$return[0]=$type;
			$return[1]=$maxdiff;
			return @return;
		}
		$overridesInt++;
	}
	return @return;
};


##
##load the path file
##
sub loadpathfile{
	my $pathfile=$_[0];
	my @paths=();
	
	open("thepathfile", $pathfile)||die("Could not open the pathfile:".$pathfile."\n");
	@paths=<thepathfile>;
	close("thepathfile");
	
	return @paths
};

#exit after printing help or version
$Getopt::Std::STANDARD_HELP_VERSION="TRUE";

#this will hold the settings and values
my %theconfig=();

#gets the options
my %opts=();
getopts('F:f:c:lp:rP:I', \%opts);

#get the config file to use
my $configfile='';
if (defined($opts{c})){
	$configfile=$opts{c};
}else{
	if (-e xdg_config_home."/zbg-1/"){
		if (defined($opts{I})){
			initConfig;
			print "config struture created \n";
			exit 0;
		};
	};
	$configfile=xdg_config_home."/zbg-1/configs/default";
};

my $profile='_' unless defined $opts{P};
%theconfig=configload($configfile, $profile);

if(defined($opts{r})){
	#figures out what to use
	if(defined($opts{p})){
		$theconfig{path}=$opts{p};
	}else{
		if(defined($theconfig{pathfile})){
			$theconfig{path}=$theconfig{pathfile};
		}else{
			$theconfig{path}=$theconfig{pathdir}."/".$theconfig{path};
		};
	};

	my @paths=loadpathfile($theconfig{path});

	#find number in path file
	my $pathsInt=0;
	while (defined($paths[$pathsInt])){
		$pathsInt++;		
	};

	my $randomPathInt=rand($pathsInt);
	$randomPathInt =~ s/\.[0123456789]*//;

	my $path=$paths[$randomPathInt];
	chomp($path);
	
	my @files=findInPath($path);

	#find number of files
	my $filesInt=0;
	while (defined($files[$filesInt])){
		$filesInt++;
	};

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

	my $file=$files[$randomFilesInt];
	chomp($file);

	$file =~ s/\/\//\//g;

	$theconfig{file}=$file;
}else{
	if (!defined($opts{l})){
		#check if a file was specified
		if (!defined($opts{f})){
  			print "file to use not specified...\n ";
  			exit 1;
		}else{
			$theconfig{file}=$opts{f};
		};
	};
};

#get what filltype to use and over rides the config if necesary
my $filltype=$opts{F};
$filltype="auto" unless defined $opts{F};
#adds the fill type to the config array
%theconfig=(%theconfig, "filltype", $filltype);


#decide if the last should be used or not
my $uselast=$opts{l};
#adds it to the config array
if (defined($opts{l})){
	($theconfig{filltype}, $theconfig{file})=lastimage($theconfig{lastfile});
};

#check for overrides
my @overrides=override($theconfig{override}, $theconfig{file});
if (defined($overrides[0])){
	$theconfig{filltype}=$overrides[0];
};
if (defined($overrides[1])){
	$theconfig{maxdiff}=$overrides[1]
};

#if it is auto, get the type
if ($theconfig{filltype} eq "auto"){
	$theconfig{filltype}=autodecide($theconfig{file}, $theconfig{maxdiff});
};

#get the command to use and run the regexp over it
my $thecommand=$theconfig{$theconfig{filltype}};
$thecommand =~ s/%%%THEFILE%%%/$theconfig{file}/g;
#this runs it
system($thecommand)
	== 0 or die "setting the bg failed: $?\n";

#don't write it if the last file is being used
if(!defined($opts{l})){

	#gets the raw last file
	open("lastfile", $theconfig{lastfile})||die("Could not open $theconfig{lastfile}!\n");
	my @rawlast=<lastfile>;
	close("lastfile");

	my $newlast="$theconfig{hostname}$theconfig{display}:$theconfig{filltype}\:$theconfig{file}\n";

	@rawlast=($newlast, @rawlast[0..($theconfig{numberoflast}-2)]);

	open("LASTFILE", ">$theconfig{lastfile}")||die("Could not open $theconfig{lastfile}!\n");
	foreach my $lastline (@rawlast){
		print LASTFILE "$lastline";
	};
	close("LASTFILE");
};

##
##the last thing to do now is to call zbgrefresh this script will take care of
##refreshing the defined menu includes
##
if ($theconfig{wmrefresh} eq "true"){
	system($theconfig{wmrefresher});
};


#-----------------------------------------------------------
# POD documentation section
#-----------------------------------------------------------

=head1 NAME

zbgset - set a image as a background

=head1 SYNOPSIS

zbgset (B<-f> <file>|B<-r>|B<-l>|B<-I>) [B<-F> <fill>] [B<-c> <config file>] 
[B<-p> <path file>] [B<-o> <override file>] [B<-P> <profile>]

=head1 USAGE

When running it, either the -f, -r, -l, or -I options need specified. It currently
does not set the background on a specific workspace or screen, but for them all.

=head1 ARGUEMENTS

=head2 -c <config file>

=head2 -F <fill>

The fill type to use.

=head2 -f <file>

The image to us.

=head2 -I

Create the base directory structure used for the configuration. The Freedesktop.org Basedir
xdg_config_home is used as the base path with it then being zbg-1 directory after that.
This  makes the default path ~/.config/zbg-1/ .

=head2 -l

Use the last set background. This is useful for when starting up X.

=head2 -o <override file>

The override file to use.

=head2 -P <profile>

The profile to use inside of the config file.

=head2 -p <path file>

The path file to use.

=head2 -r

Set a random background.

=head1 FILLTYPES

=head2 auto

Select ether to use fill or full automatically. This is done by comparing X/Y, Y/X,
and then checking if it is more than the maxdiff variable. 

=head2 center

Center the image and do not resize.

=head2 fill

Scale the image and stretch it to fill the screen.

=head2 full

Center the image and scale it, without stretching it.

=head2 tile

Tile the image.

=head1 CONFIG FILE

The default configuration file is ~/.config/zbg-1/configs/defaults . This file is
INI file. The root of the INI file is the default and each section is considered a
profile. The root is then read and the profile is then read and used to override
any thing set in the root directory.

The names are case sensitive as well as true/false.

=head2 savelast

Save the file that was just set in last. The default is true.

=head2 lastfile

This is the file used for storing the last information. The default is
~/.config/zbg-1/var/last .

=head2 numberoflast

The number of previous backgrounds to save. The default is 15.

=head2 wmrefresh (true|false)

Wether or not to run a refresh tool for any thing windowmanager related or the like.
The default is true.

=head2 wmrefresher

The program to use for refreshing the window manager or whatever.

=head2 maxdiff

The maximum difference between X and Y for choosing if to use fill or full
fill type.

=head2 pathdir

The directory to find the path file in. The default is ~/.config/zbg-1/paths/ .

=head2 path

The path file to use. This is found using the pathdir variable. The default is default.

=head2 filltype

The fill type to use. The default is auto.

=head2 override

The override file to use. The default is ~/.config/zbg-1/overrides/default .

=head2 center

The setter to use for the center type. %%%THEFILE%%% is replaced with the filename.
The default is... hsetroot -center \'%%%THEFILE%%%\'

=head2 full

The setter to use for the full type. %%%THEFILE%%% is replaced with the filename.
The default is... hsetroot -full \'%%%THEFILE%%%\'

=head2 fill

The setter to use for the fill type. %%%THEFILE%%% is replaced with the filename.
The default is... hsetroot -fill \'%%%THEFILE%%%\'

=head2 tile

The setter to use for the tile type. %%%THEFILE%%% is replaced with the filename.
The default is... hsetroot -tile \'%%%THEFILE%%%\'

=head1 OVERRIDES

The default override file is ~/.config/zbg-1/overrides/default . The file contains one entry
per line. Each entry is made up of two sections with each section being seperated by a ':'. The
first section of a line is the fill type and the second is a regexp.

=head1 LASTFILE

The default lastfile is ~/.config/zbg-1/var/last . The file is is a : delimited file with a entry
per line. The first column is the hostname of the machine it was run on, second is the display
number, the third is fill type, and the fourth is file.

=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 - set a image as a background

=cut

#-----------------------------------------------------------
# End of POD documentation
#-----------------------------------------------------------