#!/usr/bin/perl

use strict;
use warnings;

use Snap;

my $command = shift( @ARGV ) || '';
my $conf = readconf();
my $commands = Snap::Commands->new();
my $sources = Snap::Sources->new( $conf->{'sources'} );

if ( $ARGV[0] && $ARGV[0] eq '-h' ) {
	$commands->commandhelp( $command );
	}
elsif ( $command eq 'dump' ) {
	print "\n";

	foreach my $arg ( @ARGV ) {
		my $package = Snap::Package->new( $arg );

		$package->dump();

		print "\n";
		}
	}
elsif ( $command eq 'files' ) {
	my $opts = {
		all => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-a' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			},
		verbose => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-v' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};
	my $string = "@ARGV";

	foreach my $arg ( @ARGV ) {
		my $package = Snap::Package->new( $arg );

		$package->files( $opts );
		}
	}
elsif ( $command eq 'genpkg' ) {
	if ( ! @ARGV ) {
		Snap->error( -1, "Failed to provide package name" );
		}

	foreach my $arg ( @ARGV ) {
		genpkg( $arg );
		}
	}
elsif ( $command eq 'help' ) {
	$commands->help();
	}
elsif ( $command eq 'info' ) {
	my $cnt = 0;

	if ( ! @ARGV ) {
		Snap->error( 0, "'$command': You must supply an argument" );

		$commands->commandhelp( 'info' );

		exit( -1 );
		}
		
	foreach my $arg ( @ARGV ) {
		my $package = Snap::Package->new( $arg );

		if ( $cnt ) {
			print "\n";
			}

		$package->printself();

		$cnt++;
		}
	}
elsif ( $command eq 'install' ) {
	my @attribs = qw( source repo );
	my $opts = {
		nodeps => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '--no-deps' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			},
		quiet => 1,
		yes => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-y' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};
	my $string = "@ARGV";
	my $packages = [];
	my $bytes = 0;
	my $virtfs = 0;

	setup();

	foreach my $attrib ( @attribs ) {
		if ( $string =~ /$attrib\s*:\s*(\S+)/ ) {
			$opts->{$attrib} = $1;

			$string =~ s/$attrib\s*:\s*(\S+)? //;
			}
		}

	$sources->readpkgs();

	####################################################
	#
	# This loop replaces source packages with any
	# package files supplied on the command line.
	#
	# This allows the user to override the source
	# with local packages.
	#
	####################################################

	foreach my $arg ( split( /\s/, $string ) ) {
		if ( -f $arg ) {
			my $package = Snap::Package->new( $arg );
			$sources->{'pkgs'}{$package->{'name'}} = [];
			push( @{$sources->{'pkgs'}{$package->{'name'}}},
				$package );
			}
		}

	foreach my $arg ( split( /\s/, $string ) ) {
		my $package;

		if ( ! -f $arg ) {
			my ( $name, $version ) =
				split( /(((<|>)=?|=)(.*))/, $arg );
			$opts->{'name'} = $name;
			$opts->{'version'} = $version;

			$package = $sources->search( $opts );
			}
		else {
			$package = Snap::Package->new( $arg );
			}

		if ( ! $package ) {
			exit( -1 );
			}

		if ( ! $opts->{'nodeps'} ) {
			print "Resolving dependencies for"
				. " $package->{'name'}\n";

			$package->depends( $sources, $packages );
			}
		else {
			print "Ignoring dependencies for $package->{'name'}\n";
			}

		if ( ! grep( $_->{'name'} eq $package->{'name'},
		@$packages ) ) {
			push( @$packages, $package );
			}
		}

	for ( my $i = 0; $i <= $#$packages; $i++ ) {
		my $snapinfo = Snap->INSTDIR . "/$packages->[$i]{'name'}/"
			. "snapinfo";
		my $version;
		my $oldbytes;
		my $chk;

		if ( $packages->[$i]{'path'} =~ /https*:\/\// ) {
			( my $filename = $packages->[$i]{'path'} ) =~ s/.*\///;

			if ( ! -f Snap->PKGDIR . "/$filename" ) {
				Snap->httpget( $packages->[$i]{'path'},
					Snap->PKGDIR . "/$filename", 0644 );
				}

			$packages->[$i]{'path'} = Snap->PKGDIR . "/$filename";
			}

		if ( ! -f $snapinfo ) {
			$packages->[$i]{'status'} = 'installing';
			$bytes += $packages->[$i]{'bytes'};

			$packages->[$i]->conflicts( $sources );

			next;
			}

		open( SNAPINFO, "<$snapinfo" ) || Snap->error( int( $! ),
			"open(): $snapinfo: $!" );

		while ( <SNAPINFO> ) {
			if ( $_ =~ /version:\s*(.*)$/ ) {
				$version = $1;
				}
			if ( $_ =~ /bytes:\s*(.*)$/ ) {
				$oldbytes = $1;
				}
			}

		close( SNAPINFO );

		$chk = Snap->vercmp( $version, $packages->[$i]{'version'} );

		if ( $chk == -1 ) {
			$packages->[$i]{'status'} = 'upgrading';
			$bytes -= $oldbytes;
			}
		elsif ( $chk == 0 ) {
			print "Package $packages->[$i]{'name'}="
				. "$packages->[$i]{'version'}"
				. " is already installed\n";

			splice( @$packages, $i, 1 );

			$i--;

			next;
			}
		elsif ( $chk == 1 ) {
			$packages->[$i]{'status'} = 'downgrading';
			$bytes -= $oldbytes;
			}

		$packages->[$i]->conflicts( $sources );
		$bytes += $packages->[$i]{'bytes'};
		}

	if ( ! @$packages ) {
		print "\nNothing to do\n\n";

		exit;
		}

	foreach my $status ( qw( installing upgrading downgrading ) ) {
		my $termsize = Snap->termsize();
		my $cnt = 0;

		foreach my $package ( sort { $a->{'name'} cmp $b->{'name'} }
		( @$packages ) ) {
			if ( $package->{'status'} ne $status ) {
				next;
				}

			if ( ! $cnt ) {
				print "\nThe following packages will be";
				}

			if ( ! $cnt && $status eq 'installing' ) {
				print " installed:\n  ";
				}
			elsif ( ! $cnt && $status eq 'upgrading' ) {
				print " upgraded:\n  ";
				}
			elsif ( ! $cnt && $status eq 'downgrading' ) {
				print " downgraded:\n  ";
				}

			if ( $termsize->{'col'} - ( length(
			$package->{'name'} ) + 3 ) <= 0 ) {
				print "\n  ";

				$termsize = Snap->termsize();
				}

			print "$package->{'name'} ";

			$termsize->{'col'} -= length( $package->{'name'} ) + 1;
			$cnt++;
			}
		}

	if ( $bytes < 0 ) {
		print "\n\nInstall will recover " . human( -1 * $bytes )
			. ". Continue? (y/n): ";
		}
	else {
		print "\n\nInstall will require " . human( $bytes )
			. ". Continue? (y/n): ";
		}

	if ( ! $opts->{'yes'} ) {
		chkyes();
		}

	foreach my $package ( @$packages ) {
		if ( ! $virtfs ) {
			$virtfs = virtfs( 'mount' );
			}

		print "\n";

		$package->install( $sources );
		}

	if ( $virtfs ) {
		virtfs( 'umount' );
		}
	}
elsif ( $command eq 'list' ) {
	my $opts = {
		verbose => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-v' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};
	my $packages = list( \@ARGV );

	foreach my $package ( sort( keys( %$packages ) ) ) {
		if ( $opts->{'verbose'} ) {
			print "\n";

			$packages->{$package}->printself();
			}
		else {
			$packages->{$package}->printbrief();
			}
		}

	if ( ! keys( %$packages ) ) {
		print "No installed packages found matching '@ARGV'\n";
		}
	}
elsif ( $command eq 'provides' ) {
	( my $string = $ARGV[0] ) =~ s/^\/*/\//;
	my $len = length( $string );

	$sources->readpkgs();

	foreach my $pkgname ( sort( keys( %{$sources->{'installed'}} ) ) ) {
		my $package = $sources->{'installed'}{$pkgname};

		$package->files( { quiet => 1 } );

		foreach my $file ( @{$package->{'files'}} ) {
			if ( substr( "/$file", -$len, $len ) eq "$string" ) {
				print "$pkgname\n";

				last;
				}
			}
		}
	}
elsif ( $command eq 'purge' ) {
	print "Not yet implemented\n";

	exit( -1 );
	}
elsif ( $command eq 'rebuild' ) {
	print "Not yet implemented\n";

	exit( -1 );
	}
elsif ( $command eq 'refresh' ) {
	setup();

	$sources->refresh();

	exit;

	foreach my $source ( @{$conf->{'sources'}} ) {
		print "Updating package list for $source->{'name'}\n";

		Snap::Sources->refresh( $source );

		print "\n";
		}
	}
elsif ( $command eq 'reinstall' ) {
	my $opts = {
		quiet => 1,
		yes => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-y' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};
	my $termsize = Snap->termsize();
	my $packages = [];
	my $virtfs = 0;
	my $cnt = 0;

	setup();

	$sources->readpkgs();

	foreach my $pkgname ( @ARGV ) {
		my $package;

		if ( -f $pkgname ) {
			$package = Snap::Package->new( $pkgname );
			}
		else {
			if ( ! $sources->{'installed'}{$pkgname} ) {
				Snap->error( -1, "$pkgname not installed" );
				}

			$package = $sources->{'installed'}{$pkgname};
			$package = $sources->search( {
				name => $package->{'name'},
				version => $package->{'version'},
				quiet => 1
				} );
			}

		if ( $package->{'path'} =~ /https*:\/\// ) {
			( my $filename = $package->{'path'} ) =~ s/.*\///;

			if ( ! -f Snap->PKGDIR . "/$filename" ) {
				Snap->httpget( $package->{'path'},
					Snap->PKGDIR . "/$filename", 0644 );
				}

			$package->{'path'} = Snap->PKGDIR . "/$filename";
			}

		$package->files( $opts );

		if ( ! grep( $_->{'name'} eq $package->{'name'},
		@$packages ) ) {
			push( @$packages, $package );
			}
		}

	foreach my $package ( sort { $a->{'name'} cmp $b->{'name'} }
	( @$packages ) ) {
		if ( ! $cnt ) {
			print "The following packages will be"
				. " reinstalled:\n  ";

			$cnt++;
			}

		if ( $termsize->{'col'} - ( length(
		$package->{'name'} ) + 3 ) <= 0 ) {
			print "\n  ";

			$termsize = Snap->termsize();
			}

		print "$package->{'name'} ";

		$termsize->{'col'} -= length( $package->{'name'} ) + 1;
		}

	print "\n\nContinue? (y/n): ";

	if ( ! $opts->{'yes'} ) {
		chkyes();
		}

	foreach my $package ( @$packages ) {
		if ( ! $virtfs ) {
			$virtfs = virtfs( 'mount' );
			}

		print "\n";

		$package->install( $sources );
		}

	virtfs( 'umount' );
	}
elsif ( $command eq 'remove' ) {
	my $opts = {
		nodeps => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '--no-deps' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			},
		quiet => 1,
		yes => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-y' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};
	my $string = "@ARGV";
	my $packages = [];
	my $bytes = 0;
	my $cnt = 0;
	my $termsize = Snap->termsize();
	my $virtfs = 0;

	$sources->readpkgs();

	foreach my $arg ( split( /\s/, $string ) ) {
		my $package = Snap::Package->new( $arg );

		if ( ! $package ) {
			Snap->error( -1, "$arg: No such package found" );
			}

		if ( ! $opts->{'nodeps'} ) {
			print "Resolving reverse dependencies for"
				. " $package->{'name'}\n";

			$package->revdeps( $sources, $packages,
				{ noreq => 1 } );
			}
		else {
			print "Ignoring reverse dependencies for"
				. " $package->{'name'}\n";
			}

		if ( ! grep( $_->{'name'} eq $package->{'name'},
		@$packages ) ) {
			push( @$packages, $package );
			}
		}

	foreach my $package ( sort { $a->{'name'} cmp $b->{'name'} }
	( @$packages ) ) {
		if ( ! $cnt ) {
			print "\nThe following packages will be removed";

			if ( Snap->TARGET ) {
				print " from " . Snap->TARGET;
				}

			print ":\n  ";
			}

		if ( $termsize->{'col'} - ( length(
		$package->{'name'} ) + 3 ) <= 0 ) {
			print "\n  ";

			$termsize = Snap->termsize();
			}

		print "$package->{'name'} ";

		$termsize->{'col'} -= length( $package->{'name'} ) + 1;
		$cnt++;

		$bytes += $package->{'bytes'};
		}

	print "\n\n" . human( $bytes ) . " will be recovered."
		. " Continue? (y/n): ";

	if ( ! $opts->{'yes'} ) {
		chkyes();
		}

	print "\n";

	$cnt = 0;

	foreach my $package ( @$packages ) {
		if ( ! $virtfs ) {
			$virtfs = virtfs( 'mount' );
			}

		if ( $cnt ) {
			print "\n";
			}

		$package->remove( $sources );

		$cnt++;
		}

	if ( $virtfs ) {
		virtfs( 'umount' );
		}
	}
elsif ( $command eq 'revdep' ) {
	my $revdeps = [];

	$sources->readpkgs();

	foreach my $arg ( @ARGV ) {
		my $package = Snap::Package->new( $arg );

		$package->revdeps( $sources, $revdeps, { noreq => 1 } );
		}

	foreach ( sort { $a->{'name'} cmp $b->{'name'} } ( @$revdeps ) ) {
		print "$_->{'name'}\n";
		}

	if ( ! @$revdeps ) {
		print "No reverse dependencies found\n";
		}
	}
elsif ( $command eq 'search' ) {
	my @attribs = qw( name version depends source repo description );
	my $opts = {
		all => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-a' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			},
		verbose => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-v' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};
	my $string = "@ARGV";

	foreach my $attrib ( @attribs ) {
		if ( $string =~ /$attrib\s*:\s*(\S+)/ ) {
			$opts->{$attrib} = $1;

			$string =~ s/$attrib\s*:\s*(\S+)//;
			}
		}

	if ( ! $opts->{'name'} && ! $opts->{'version'} &&
	 $string =~ /(\S+)\s*=\s*(\S+)/ ) {
		$opts->{'name'} = $1;
		$opts->{'version'} = $2;
		$string =~ s/(\S+)\s*=\s*(\S+)//;
		}

	if ( $string ) {
		( $opts->{'string'} = $string ) =~ s/^ *| *$//g;
		}

	if ( ! $sources->readpkgs() ) {
		exit( -1 );
		}

	if ( ! $sources->search( $opts ) ) {
		exit( -1 );
		}
	}
elsif ( $command eq 'source' ) {
	my $opts = {
		latest => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-l' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			},
		quiet => 1
		};
	$sources->readpkgs();

	if ( ! @ARGV ) {
		Snap->error( 0, "'$command': You must supply an argument" );

		$commands->commandhelp( 'source' );

		exit( -1 );
		}
		
	foreach my $arg ( @ARGV ) {
		my $package;# = Snap::Package->new( $arg );

		if ( $sources->{'installed'}{$arg} && ! $opts->{'latest'} ) {
			$package = $sources->{'installed'}{$arg};
			}
		else {
			my ( $name, $version ) =
				split( /(((<|>)=?|=)(.*))/, $arg );
			$opts->{'name'} = $name;
			$opts->{'version'} = $version;
			$package = $sources->search( $opts );
			}

		$package->source();
		}
	}
elsif ( $command eq 'upgrade' ) {
	print "Not yet implemented\n";

	exit( -1 );
	}
elsif ( $command eq 'verify' ) {
	my $opts = {
		verbose => eval {
			for ( my $i = 0; $i <= $#ARGV; $i++ ) {
				if ( $ARGV[$i] eq '-v' ) {
					splice( @ARGV, $i, 1 );

					return( 1 );
					}
				}
			}
		};

	foreach my $arg ( @ARGV ) {
		my $package = Snap::Package->new( $arg );

		$package->verify( $opts );
		}
	}
elsif ( $command eq 'version' ) {
	print Snap->VERSION . "\n";
	}
elsif ( $command ) {
	Snap->error( 0, "'$command': Invalid command" );

	$commands->help();

	exit( -1 );
	}
else {
	Snap->error( 0, "You must supply a command" );

	$commands->help();

	exit( -1 );
	}

