#! /usr/bin/env perl

use warnings;
use strict;

our $VERSION = '1.0';
my $TESTING = 1;

my %installationTypes = (
                           'ipfire' => {
                               'post process' => \&ipfire
                            }
                      );
                      
my %installation = (
                     'Configuration Directory' => '/etc/camp',
                     'Modules Directory' => 'modules',
                     'Scripts Directory' => 'scripts',
                     'Executable Directory' => '/opt/camp'
                   );

my %configuration;

my %packageActions = (
                  'modules' => {
                     'type'       => 'directory',
                     'target'     => 'Modules Directory',
                     'owner'      => 'root:root',
                     'dir perms'  => '0755',
                     'file perms' => '0755'
                  },
                  'scripts' => {
                     'type'       => 'directory',
                     'target'     => 'Scripts Directory',
                     'owner'      => 'root:root',
                     'dir perms'  => '0755',
                     'file perms' => '0755'
                  },
                  'notes' => {
                     'type'       => 'file',
                     'target'     => 'Configuration Directory',
                     'owner'      => 'root:root',
                     'dir perms'  => '0755',
                     'file perms' => '0644'
                  },
                  'sysinfo_client' => {
                     'type'       => 'file',
                     'target'     => 'Executable Directory',
                     'owner'      => 'root:root',
                     'dir perms'  => '0755',
                     'file perms' => '0755',
                     'cron'       => &findCron(),
                     'link'       => '/usr/bin'
                  },
                  'install.pl' => {
                     'type'       => 'file',
                     'target'     => 'Configuration Directory',
                     'owner'      => 'root:root',
                     'dir perms'  => '0755',
                     'file perms' => '0755'
                  },
                  'sysinfo.conf.template' => {
                     'type'       => 'file',
                     'target'     => 'Configuration Directory',
                     'owner'      => 'root:root',
                     'dir perms'  => '0755',
                     'file perms' => '0644'
                  }
               );
my $installationType;
my $cronLocation; 
my $installMode;

# prompt the user for a response, then allow them to enter it
# the first response is considered the default and is printed
# in all caps if more than one exists
# first answer is used if they simply press the Enter
# key. The response is returned all lower case if more than one
# exists.
# it is assumed 
sub getAnswer {
   my ( $prompt, @answers ) = @_;
   my $onlyOneAnswer = scalar( @answers ) == 1;
   print $prompt . '[';
   $answers[0] = uc $answers[0] unless $onlyOneAnswer;
   print join( '/', @answers ) . ']: ';
   $thisAnswer = <>;
   chomp $thisAnswer;
   $thisAnswer = $answers[0] unless $thisAnswer;
   return $onlyOneAnswer ? lc $thisAnswer : $thisAnswer;
}

sub checkInstall {
   my $installed = `which sysinfo-client`;
   my $action;
   chomp $installed;
   my $oldconf = (-f '/etc/sysinfo/sysinfo.conf' ) ? '/etc/sysinfo/sysinfo.conf' : '';
   if ( $installed ) {
      $action = &getAnswer( 'Application appears to already be installed\nWhat do you want to do', ('configure','remove') );
   } else {
      my $useOldConf = &getAnswer( 'Doing a fresh install, and old sysinfo configuration exists, use it for defaults?',('y','n') ) if $oldconf;
      $action = 'install';
      $action .= ':' . $oldconf if $useOldConf eq 'y';
   }
   return $action;
}

# simply attempts to detect the operating system so we can do OS
# specific actions at the end.   
sub getOperatingSystem {
   my @OSTypes = ( 'ipfire','debian' );
   my $OS = `uname -a`;
   foreach $osType ( @OSTypes ) {
      return $osType if $OS =~ m/$osType/i;
   }
   return '';
} # getOperatingSystem

# see if we can find a cron of some kind that will run the process
# daily
sub findCron {
   while ( $location = shift ) {
      return $location if -d $location;
   }
   return '';
}

sub customizeInstall {
   my ( $installation, $cronLocation ) = @_;
   # Allow user to customize the installation paths
   foreach my $answer ( sort keys %$installation ) {
      if ( $answer eq 'Modules Directory' or $answer eq 'Scripts Directory' ) {
         $$installation{$answer} = $$installation{'Configuration Directory'} . '/' . $$installation{$answer};
      }
      $$installation{$answer} = &getAnswer( $answer, ( $$installation{$answer} ) );
   }
   $$cronLocation = &getAnswer( 'Link executable into which location', ( $$cronLocation ) );
}   

sub makeDirectory {
   my ( $directory, $owner, $permissions ) = @_;
   return if -d $directory;
   # make_path is very good and efficient, if File is installed, so we try it
   eval { use File::Path 'make_path'; };
   if ( $@ ) {
      if ( $TESTING ) {
         print "Using OS to create $directory\n";
         print "mkdir -p $directory\n";
         print "chown $owner $directory\n";
         print "chmod $permissions $directory\n";
         return;
      }
      `mkdir -p $directory`;
      `chown $owner $directory`;
      `chmod $permissions $directory`;
      die "Could not create directory [$directory]\n" unless -d $directory;
   } else { # we could not load library, so do it via the OS
      if ( $TESTING ) {
         print "Using make_path to create $directory\n";
         print "make_path( $directory, { mode => $permissions, owner=>$owner } )\n";
         return;
      }
      make_path( $directory, { mode => $permissions, owner=>$owner, error => \my $err } );
      die "Could not create directory [$directory]\n" if @$err;
   }
}

sub doInstall {
   my ( $installation, $cronLocation, $packageActions ) = @_;
   foreach my $item ( keys %$packageActions ) {
      &makeDirectory( 
         $$installation{$$packageActions{$item}{'target'}}, 
         $$packageActions{$item}{'owner'}, 
         $$packageActions{$item}{'dir perms'} 
         );
      my $targetDir = $$installation{$$packageActions{$item}{'target'}};
      my $perms = $$packageActions{$item}{'file perms'};
      my $owner = $$packageActions{$item}{'owner'};
      $item .= '/*' if  $$packageActions{$item}{'type'} eq 'directory';
      if ( $TESTING ) {
         print "mv $item $targetDir\n";
         print "chmod $perms \"$targetDir/$item\"\n";
         print "chown $owner \"$targetDir/$item\"\n";
         # is this one supposed to be run as cron?
         if ( $$packageActions{$item}{'cron'} && $cronLocation ) {
            print "ln -s \"$targetDir/$item\" \"$cronLocation/$item\"\n";
         }
      } else {
         `mv $item $targetDir`;
         `chmod $perms "$targetDir/$item"`;
         `chown $owner "$targetDir/$item"`;
         # is this one supposed to be run as cron?
         if ( $$packageActions{$item}{'cron'} && $cronLocation ) {
            `ln -s "$targetDir/$item" "$cronLocation/$item"`;
         }
         if ( $$packageActions{$item}{'link'} ) {
            my $linkTo = $$packageActions{$item}{'link'} . "/$item";
            `ln -s "$targetDir/$item" "$linkTo"`;
         }
      }
   } # foreach
}

sub readConfiguration {
   my ( $configuration,$confFileName ) = @_;
}   

sub ipFire {
   my @BACKUP_DIRS = ('/opt/sysinfo', '/etc/sysinfo');
   my @INSTALL_COMMANDS = (
              'cp -av opt /',
              'cp -av etc /',
              'ln -s /opt/sysinfo/sysinfo /etc/fcron.daily/sysinfo.cron'
              );
   # now, check for backup directories not in include.user
   open BACKUP, '</var/ipfire/backup/include.user';
   while ( $line = <BACKUP> ) {
      chomp $line;
      for (my $i = 0; $i < @BACKUP_DIRS; $i++ ) {
         if ( $BACKUP_DIRS[$i] eq $line ) {
            $BACKUP_DIRS[$i] = '';
            last;
         } # if
      }# for
   } # while
   close BACKUP;

   # if any remain, append them to include.user
   open BACKUP, '>>/var/ipfire/backup/include.user';
   foreach my $backupDir ( @BACKUP_DIRS ) {
      print BACKUP "$backupDir\n" if $backupDir;
   }
   close BACKUP;

   # set all modules with ipfire in the name to run
   opendir $moduleDir, $MODULES_DIR;
   my @modules = grep { /ipfire/ } readdir $moduleDir;
   closedir $moduleDir;
   foreach my $module (@modules) {
      `chmod 755 $MODULES_DIR/$module`;
   }
}

#####################################################################
# Main Program
#####################################################################

$installMode = &checkInstall();
# if installationType or cronLocation not set, find them
$installationType = &getOperatingSystem() unless $installationType;
$cronLocation = &findCron( '/etc/cron.daily', '/etc/fcron.daily' ) unless $cronLocation;
# allow use to change installation paths
&customizeInstall( \%installation,\$cronLocation, \%packageActions );

print "Install mode is: $installMode\n";
print "Cron job will be run from: $cronLocation\n";
print "The operating system is: $installationType\n";
foreach my $answer ( sort keys %installation ) {
   print "$answer = $installation{$answer}\n";
}
print 'Continue? [Y/n]: ';
$answer = <>;
chomp $answer;
$answer = $answer ? uc $answer : 'Y';
exit unless $answer eq 'Y';
print "Continuing the installation\n";

&doInstall( \%installation, $cronLocation, \%packageActions );

1;
