use strict;
use warnings;
use Prima::VB::CfgMaint;

die <<ABOUT unless @ARGV;
format: cfgmaint [options] command object [parameters]
options:
   -r    use root config to write
   -b    use both user and root config to read
   -x    do not write backups
   -o    read-only mode
   -p    execute 'use Prima;' code
commands:
   a  - add     p|m    name
   l  - list    w|p|m  ( w - [page])
   d  - remove  w|p|m  name
   r  - rename  w|p    name new_name
   m  - move    w|p    name new_page or none to end
objects:
   w  - widgets
   p  - pages
   m  - modules

examples:
   cfgmaint -r a m CPAN/Prima/VB/New/MyCtrls.pm
   cfgmaint -b l w
ABOUT

my @cmd  = ();
my $both = 0;
my $ro   = 0;
$Prima::VB::CfgMaint::systemWide = 0;
$Prima::VB::CfgMaint::backup     = 1;

for ( @ARGV) {
	push( @cmd, $_), next unless /^-/;
	$_ = lc $_;
	s/^-//;
	for ( split( '', $_)) {
		if ( $_ eq 'b') {
			$both = 1;
		} elsif ( $_ eq 'r') {
			$Prima::VB::CfgMaint::systemWide = 1;
		} elsif ( $_ eq 'x') {
			$Prima::VB::CfgMaint::backup = 0;
		} elsif ( $_ eq 'o') {
			$ro = 1;
		} elsif ( $_ eq 'p') {
			eval "use Prima;";
			die "$@" if $@;
		} else {
			die "Unknown option: $_\n";
		}
	}
}

sub check
{
	die "format: $cmd[0] [$_[0]]\n" if scalar @cmd < $_[1];
	my %h = map { $_ => 1 } split( '', $_[0]);
	return if $h{$cmd[1]};
	die "Invalid sub-option: $cmd[1]. Use one of '$_[0]'\n";
}

sub assert
{
	die "$_[1]\n" unless $_[0];
}


die "Insufficient number of parameters\n" if @cmd < 2;

$cmd[$_] = lc $cmd[$_] for 0..1;
if ( $cmd[0] eq 'a') {
	check('pm', 3);
} elsif ( $cmd[0] eq 'l') {
	check('wpm', 2);
} elsif( $cmd[0] eq 'd') {
	check('wpm', 3);
} elsif( $cmd[0] eq 'r') {
	check('wp', 4);
} elsif( $cmd[0] eq 'm') {
	check('wp', 3);
	die "Insufficient number of parameters\n" if @cmd < 4 && $cmd[1] eq 'w';
} else {
	die "Unknown action: $cmd[0]\n";
}

my @r;
if ( $both) {
	@r = Prima::VB::CfgMaint::read_cfg();
} else {
	@r = Prima::VB::CfgMaint::open_cfg();
}
die "$r[1]\n" unless $r[0];

if ( $cmd[0] eq 'a') {
	if ( $cmd[1] eq 'm') {
		my %cs = %Prima::VB::CfgMaint::classes;
		my %pg = map { $_ => 1} @Prima::VB::CfgMaint::pages;
		assert( Prima::VB::CfgMaint::add_module( $cmd[2]));
		for ( @Prima::VB::CfgMaint::pages) {
			next if $pg{$_};
			print "page '$_' added\n";
		}
		for ( keys %Prima::VB::CfgMaint::classes) {
			next if $cs{$_};
			print "widget '$_' added\n";
		}
	} elsif ( $cmd[1] eq 'p') {
		my %pg = map { $_ => 1} @Prima::VB::CfgMaint::pages;
		die "Page '$cmd[2]' already exists\n" if $pg{$cmd[2]};
		push @Prima::VB::CfgMaint::pages, $cmd[2];
	}
} elsif ( $cmd[0] eq 'l') {
	if ( $cmd[1] eq 'w') {
		my $ok = defined $cmd[2] ? 0 : 1;
		for ( keys %Prima::VB::CfgMaint::classes) {
			next if defined $cmd[2] && $Prima::VB::CfgMaint::classes{$_}-> {page} ne $cmd[2];
			print "$_\n";
			$ok = 1
		}
		die "Page '$cmd[2]' doesn't exist\n" unless $ok;
	} elsif ( $cmd[1] eq 'p') {
		print join( "\n", @Prima::VB::CfgMaint::pages);
	} elsif ( $cmd[1] eq 'm') {
		my %pk = ();
		$pk{$Prima::VB::CfgMaint::classes{$_}-> {module}} = 1
		for keys %Prima::VB::CfgMaint::classes;
		for ( keys %pk) { print "$_\n"; }
	}
	exit;
} elsif( $cmd[0] eq 'd') {
	if ( $cmd[1] eq 'w') {
		die "Widget '$cmd[2]' doesn't exist\n" unless
			$Prima::VB::CfgMaint::classes{$cmd[2]};
		delete $Prima::VB::CfgMaint::classes{$cmd[2]};
	} elsif ( $cmd[1] eq 'p') {
		my @p;
		for ( @Prima::VB::CfgMaint::pages) {
			push ( @p, $_) unless $cmd[2] eq $_;
		}
		die "Page '$cmd[2]' doesn't exist\n" 
			if scalar @Prima::VB::CfgMaint::pages == scalar @p;
		@Prima::VB::CfgMaint::pages = @p;
		for ( keys %Prima::VB::CfgMaint::classes) {
			next unless $Prima::VB::CfgMaint::classes{$_}-> {page} eq $cmd[2];
			delete $Prima::VB::CfgMaint::classes{$_};
			print "Widget '$_' deleted\n";
		}
	} elsif ( $cmd[1] eq 'm') {
		my %dep;
		my $ok = 0;
		for ( keys %Prima::VB::CfgMaint::classes) {
			unless ( $Prima::VB::CfgMaint::classes{$_}-> {module} eq $cmd[2]) {
				$dep{$Prima::VB::CfgMaint::classes{$_}-> {page}} = 1;
				next;
			}
			delete $Prima::VB::CfgMaint::classes{$_};
			$ok = 1;
			print "widget '$_' removed\n";
		}
		my @newpages;
		for ( @Prima::VB::CfgMaint::pages) {
			push ( @newpages, $_) , next if $dep{$_};
			print "page '$_' removed\n";
		}
		@Prima::VB::CfgMaint::pages = @newpages;
		die "Package '$cmd[2]' not found\n" unless $ok;
	}
} elsif( $cmd[0] eq 'r') {
	if ( $cmd[1] eq 'w') {
		die "Widget '$cmd[2]' doesn't exist\n" unless
			$Prima::VB::CfgMaint::classes{$cmd[2]};
		die "Widget '$cmd[3]' already exist\n" if
			$Prima::VB::CfgMaint::classes{$cmd[3]};
		$Prima::VB::CfgMaint::classes{$cmd[3]} = $Prima::VB::CfgMaint::classes{$cmd[2]};
		delete $Prima::VB::CfgMaint::classes{$cmd[2]};
	} elsif ( $cmd[1] eq 'p') {
		my %pg = map { $_ => 1} @Prima::VB::CfgMaint::pages;
		die "Page '$cmd[2]' doesn't exist\n" unless $pg{$cmd[2]};
		die "Page '$cmd[3]' already exist\n" if $pg{$cmd[3]};
		for ( @Prima::VB::CfgMaint::pages) {
			$_ = $cmd[3], last if $_ eq $cmd[2];
		}
		for ( keys %Prima::VB::CfgMaint::classes) {
			$Prima::VB::CfgMaint::classes{$_}-> {page} = $cmd[3] if
				$Prima::VB::CfgMaint::classes{$_}-> {page} eq $cmd[2];
		}
	}
} elsif( $cmd[0] eq 'm') {
	if ( $cmd[1] eq 'w') {
		my %pg = map { $_ => 1} @Prima::VB::CfgMaint::pages;
		die "Page '$cmd[3]' doesn't exist\n" unless $pg{$cmd[3]};
		die "Widget '$cmd[2]' doesn't exist\n" unless
			$Prima::VB::CfgMaint::classes{$cmd[2]};
		$Prima::VB::CfgMaint::classes{$cmd[2]}-> {page} = $cmd[3];
	} elsif ( $cmd[1] eq 'p') {
		my %pg = map { $_ => 1} @Prima::VB::CfgMaint::pages;
		die "Page '$cmd[2]' doesn't exist\n" unless $pg{$cmd[2]};
		die "Page '$cmd[3]' doesn't exist\n" if ! exists $pg{$cmd[3]} && defined $cmd[3];
		my @p;
		for ( @Prima::VB::CfgMaint::pages) {
			push ( @p, $_) unless $cmd[2] eq $_;
		}
		@Prima::VB::CfgMaint::pages = @p;
		@p = ();
		if ( defined $cmd[3]) {
			for ( @Prima::VB::CfgMaint::pages) {
				push ( @p, $cmd[2]) if $_ eq $cmd[3];
				push ( @p, $_);
			}
			@Prima::VB::CfgMaint::pages = @p;
		} else {
			push @Prima::VB::CfgMaint::pages, $cmd[2];
		}
		print join( "\n", @Prima::VB::CfgMaint::pages);
	}
}

assert( Prima::VB::CfgMaint::write_cfg) unless $ro;

__DATA__

=pod

=head1 NAME

cfgmaint - configuration tool for Visual Builder

=head1 SYNTAX

cfgmaint [ -rbxop ] command object [ parameters ]

=head1 DESCRIPTION

Maintains widget palette configuration for the Visual Builder.
It can be stored in the system-wide and the local user config files.
C<cfgmaint> allows adding, renaming, moving, and deleting the
classes and pages in the Visual Builder widget palette.

=head1 USAGE

C<cfgmaint> is invoked with C<command> and C<object> arguments,
where C<command> defines the action to be taken, and C<object> - 
the object to be handled. 

=head2 Options

=over

=item -r

Write configuration to the system-wide config file

=item -b

Read configuration from both system-wide and user config files

=item -x

Do not write backups

=item -o

Read-only mode

=item -p

Execute C<use Prima;> code before start. This option
might be necessary when adding a module that relies on the toolkit
but does not invoke the code itself.

=back

=head2 Objects

=over

=item m

Selects a module. Valid for add, list, and remove commands.

=item p

Selects a page. Valid for all commands.

=item w

Selects a widget. Valid for list, remove, rename, and move commands. 

=back

=head2 Commands

=over

=item a  

Adds a new object to the configuration. Can be either a page or
a module.

=item d

Removes an object.

=item l

Prints object name. In case object is a widget, prints all
registered widgets. If the string is specified as an additional
parameter, it is treated as a page name and only widgets from
the page are printed.

=item r

Renames an object to a new name, which is passed as additional parameter.
Can be either a widget or a page.

=item m

If C<object> is a widget, relocates one or more widgets to a new page.
If C<object> is a page, moves the page before the page specified as an additional parameter,
or to the end if no additional page specified.

=back

=head1 EXAMPLE

Add a new module to the system-wide configuration:

	cfgmaint -r a m CPAN/Prima/VB/New/MyCtrls.pm 

List widgets, present in both config files:

	cfgmaint -b l w

Rename a page:

	cfgmaint r p General Basic

=head1 FILES

F<Prima/VB/Config.pm>, F<~/.prima/vbconfig>

=head1 AUTHOR

Dmitry Karasik, E<lt>dmitry@karasik.eu.orgE<gt>.

=head1 SEE ALSO

L<VB>, L<Prima::VB::CfgMaint>


=cut

