package cfgfile1;

# Copyright (C) 1999 Norman Walsh, <ndw@nwalsh.com>
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
# WARRANTY
# 
# THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
# PURPOSE.

use strict;
use XML::Parser;

{
    package cfgfile1::cfgentry;

    sub new {
	my $type = shift;
	my $name = shift;
	my $value = shift;
	my(%attr) = @_;
	my $class = ref($type) || $type;
	my $self = {};

	$attr{'name'} = $name;
	$attr{'value'} = $value;

	$self->{'NAME'} = $name;
	$self->{'VALUE'} = $value;
	%{$self->{'ATTR'}} = %attr;

	return bless $self, $class;
    }

    sub write {
	my $self = shift;
	local *F = shift;
	my($count, $key);

	print F "  <entry";
	foreach $key (keys %{$self->{'ATTR'}}) {
	    my(%attr) = %{$self->{'ATTR'}};
	    if ($attr{$key} =~ /\"/) {
		print F " $key='", $attr{$key}, "'";
	    } else {
		print F " $key=\"", $attr{$key}, "\"";
	    }
	}
	print F "/>\n";
    }

    sub name {
	my $self = shift;
	return $self->{'NAME'};
    }

    sub getValue {
	my $self = shift;
	return $self->{'VALUE'};
    }

    sub setValue {
	my $self = shift;
	my $value = shift;
	$self->{'VALUE'} = $value;
	return $value;
    }
}

{
    package cfgfile1::cfgsection;

    sub new {
	my $type = shift;
	my $name = shift;
	my(%attr) = @_;
	my $class = ref($type) || $type;
	my $self = {};

	$attr{'name'} = $name;

	$self->{'NAME'} = $name;
	%{$self->{'ATTR'}} = %attr;

	$self->{'ENTRIES'} = [];
	$self->{'ENASSOC'} = {};
	$self->{'ECOUNT'} = 0;

	return bless $self, $class;
    }

    sub write {
	my $self = shift;
	local *F = shift;
	my($count, $key);

	print F "<section";
	foreach $key (keys %{$self->{'ATTR'}}) {
	    my(%attr) = %{$self->{'ATTR'}};
	    if ($attr{$key} =~ /\"/) {
		print F " $key='", $attr{$key}, "'";
	    } else {
		print F " $key=\"", $attr{$key}, "\"";
	    }
	}
	print F ">\n";

	for ($count = 0; defined($self->{'ENTRIES'}->[$count]); $count++) {
	    my $entry = $self->{'ENTRIES'}->[$count];
	    $entry->write(*F);
	}

	print F "</section>\n";
    }

    sub name {
	my $self = shift;
	return $self->{'NAME'};
    }

    sub getEntry {
	my $self = shift;
	my $name = shift;
	my $num = shift;
	my($count);

	# if $name and $num are defined, return the $num'th $name
	# if $name is defined and $num isn't, return the _LAST_ $name
	# if $name is '*' and $num is defined, return the $num'th entry
	# if $name and $num are both undefined, return undef

	if (defined($num)) {
	    for ($count = 0; defined($self->{'ENTRIES'}->[$count]); $count++) {
		if (($name eq '*') 
		    || ($name eq $self->{'ENTRIES'}->[$count]->name())) {
		    $num--;
		    return $self->{'ENTRIES'}->[$count] if $num == 0;
		}
	    }

	    return undef;
	} elsif (defined($name)) {
	    return $self->{'ENASSOC'}->{$name};
	} else {
	    return undef;
	}
    }

    sub addEntry {
	my $self = shift;
	my $name = shift;
	my $value = shift;
	my(%attr) = @_;
	my $entry = new cfgfile1::cfgentry $name, $value, %attr;

	$self->{'ENTRIES'}->[$self->{'ECOUNT'}] = $entry;
	$self->{'ECOUNT'}++;
	$self->{'ENASSOC'}->{$name} = $entry;
	
	return $entry;
    }
}

my($self); # package global so that the start_handler can access it.

sub new {
    my($type, $cfgfile) = @_;
    my $class = ref($type) || $type;
    
    $self = {};
    $self->{'SECTIONS'} = [];
    $self->{'SECASSOC'} = {};
    $self->{'SCOUNT'} = 0;
    $self->{'CURSEC'} = 0;
    
    bless $self, $class;
    
    my $parser = new XML::Parser(ErrorContext => 2);
    $parser->setHandlers(Start => \&cfgfile1::start_handler);
    $parser->parsefile($cfgfile);
    
    return $self;
}

sub write {
    my $self = shift;
    local *F = shift;
    my($count);
    
    print F "<?xml version='1.0'?>\n";
    print F "<cfgfile>\n";
    
    for ($count = 0; defined($self->{'SECTIONS'}->[$count]); $count++) {
	my $section = $self->{'SECTIONS'}->[$count];
	$section->write(*F);
    }
    
    print F "</cfgfile>\n";
}

sub addSection {
    my $self = shift;
    my $name = shift;
    my(%attr) = @_;
    
    my $sec = new cfgfile1::cfgsection $name, %attr;
    $self->{'SECTIONS'}->[$self->{'SCOUNT'}] = $sec;
    $self->{'SCOUNT'}++;
    $self->{'CURSEC'} = $sec;
    $self->{'SECASSOC'}->{$name} = $sec 
	if ! exists $self->{'SECASSOC'}->{$name};
    
    return $sec;
}

sub getProfileSection {
    my $self = shift;
    my $name = shift;
    my $num = shift;
    my($count);
    
    # if $name and $num are defined, return the $num'th $name
    # if $name is defined and $num isn't, return the _LAST_ $name
    # if $name is '*' and $num is defined, return the $num'th entry
    # if $name and $num are both undefined, return undef
    
    if (defined($num)) {
	for ($count = 0; defined($self->{'SECTIONS'}->[$count]); $count++) {
	    if (($name eq '*') 
		|| ($name eq $self->{'SECTIONS'}->[$count]->name())) {
		$num--;
		return $self->{'SECTIONS'}->[$count] if $num == 0;
	    }
	}
	return undef;
    } elsif (defined($name)) {
	return $self->{'SECASSOC'}->{$name};
    } else {
	return undef;
    }
}

sub getProfileString {
    my $self = shift;
    my $secname = shift;
    my $varname = shift;
    my $default = shift;
    my $section = $self->{'SECASSOC'}->{$secname};
    
    return $default if !$section;
    
    my $entry = $section->getEntry($varname);
    
    return $entry->getValue() if $entry;
    
    return $default;
}

sub setProfileString {
    my $self = shift;
    my $secname = shift;
    my $varname = shift;
    my $value = shift;
    my $section = $self->getProfileSection($secname);
    my(%attr) = ();
    
    $section = $self->addSection($secname) if !$section;
    
    my $entry = $section->getEntry($varname);
    
    if ($entry) {
	return $entry->setValue($value);
    } else {
	$section->addEntry($varname, $value, %attr);
	return $value;
    }
}

sub start_handler {
    my $p = shift;
    my $el = shift;
    my %attr = @_;
    
    if ($el eq 'section') {
	my $name = $attr{'name'};
	$self->addSection($name, %attr);
    } elsif ($el eq 'entry') {
	my $name = $attr{'name'};
	my $val = $attr{'value'};
	$self->{'CURSEC'}->addEntry($name, $val, %attr);
    }
}


1;
