#!/usr/bin/perl -w

#
# Name:		ag_xmlrepos
# Authors:	Lukas Ocilka <locilka@suse.cz>
# Summary:	Agent for parsing XML files in format One Click Install Standard
#		http://en.opensuse.org/Standards/One_Click_Install
#

use strict;
use lib "/usr/lib/YaST2/agents_non_y2";
use ycp;
use XML::Bare;

sub GetListFromVariable {
    my $variable = shift || {};

    if ($variable =~ /^ARRAY\(/) {
	return $variable;
    } elsif ($variable =~ /^HASH\(/) {
	return [$variable];
    } else {
	y2error ("Unknown: ".$variable);
	return [];
    }
}

##
# Converts XML to a list of maps with all repositories described in the XML content.
#
# @param XML content as descibed at http://en.opensuse.org/Standards/One_Click_Install
# @return list <map, <string, any> >
#
# @struct [
#    $[
#         "distversion" : "openSUSE Factory",
#         "url" : "full url of the repository (http://.../)",
#         "format" : "yast",
#         "recommended" : true,
#         "description" : "repository description",
#         "localized_description" : $[
#             "en_GB" : "repository description (localized to en_GB)",
#             ...
#         ],
#         "summary" : "repository summary",
#         "localized_summary" : $[
#             "en_GB" : "repository summary (localized to en_GB)",
#             ...
#         ],
#         "name" : "repository name",
#         "localized_name" : $[
#             "en_GB" : "repository name (localized to en_GB)",
#             ...
#         ],
#         "mirrors" : [
#             $[
#                 "url" : "full url of the mirror (http://.../)",
#                 "location" : "?",
#                 "score" : number,
#             ]
#             ...
#         ]
#    ],
#    ...
# ]
sub GetRepositoriesFromXML {
    my $xmlcontent = shift || do {
	y2error ("First parameter needs to be an XML content");
	return undef;
    };

    # list of repositories
    my @repos = ();

    # default language is used when no 'language' string is defined
    my $default_lang = "en_US";

    my $xml = new XML::Bare ('text' => $xmlcontent);
    my $root = $xml->parse();
    undef $xml;

    # the first level we parse is <metapackage><group>...</group>[<group>...</group>]<metapackage>
    $root = $root->{'metapackage'}->{'group'} || {};

    my $groups = GetListFromVariable ($root);

    foreach my $group (@{$groups}) {
	my $distribution = $group->{'distversion'}->{'value'};
	my $repos_for_dist = GetListFromVariable ($group->{'repositories'}->{'repository'});

	foreach my $one_repo (@{$repos_for_dist}) {
	    my $this_repo = {};
	    $this_repo->{'distversion'} = $distribution;

	    if (defined $one_repo->{'format'}->{'value'}) {
		$this_repo->{'format'} = $one_repo->{'format'}->{'value'};
	    }

	    if (defined $one_repo->{'recommended'}->{'value'}) {
		# either 'true' or 'false'
		$this_repo->{'recommended'} = (
		    $one_repo->{'recommended'}->{'value'} =~ /^[\t \n]*true[\t \n]*$/ ? 'true':'false'
		);
	    }

	    my @mirrors = ();

	    my $urls = GetListFromVariable ($one_repo->{'url'} || {});

	    # URL and URL mirrors
	    foreach my $urlhash (@{$urls}) {
		my $url		= $urlhash->{'value'} || "";
		my $location	= $urlhash->{'location'}->{'value'} || "";
		my $score	= $urlhash->{'score'}->{'value'} || "";

		# No additional parameters -> Main URL
		if ($location eq "" && $score eq "") {
		    $this_repo->{'url'} = $url;
		} else {
		    push @mirrors, {
			'url' => $url,
			'score' => $score,
			'location' => $location,
		    };
		}
	    }

	    if (scalar(@mirrors) > 0) {
	        @{$this_repo->{'mirrors'}} = @mirrors;

		# Main url is not known but some mirrors were found
		if (! defined $this_repo->{'url'} || $this_repo->{'url'} eq "") {
		    $this_repo->{'url'} = $mirrors[0]->{'url'};
		}
	    }

	    # fills up:
	    #     * name, description, summary (string)
	    #     * localized_name, localized_description, localized_summary (map <string, string>)
	    foreach my $key ('name', 'description', 'summary') {
		my $variable_list = GetListFromVariable ($one_repo->{$key} || {});

		foreach my $repo_keys (@{$variable_list}) {
		    my $lang = "";

		    if (defined $repo_keys->{'lang'}->{'value'}) {
			$lang = $repo_keys->{'lang'}->{'value'};
		    } elsif (defined $repo_keys->{'xml:lang'}->{'value'}) {
			$lang = $repo_keys->{'xml:lang'}->{'value'};
		    } else {
			$lang = $default_lang;
		    }

		    $this_repo->{'localized_'.$key}->{$lang} = $repo_keys->{'value'} || "";
		}
		
		if (defined $this_repo->{'localized_'.$key}->{$default_lang}) {
		    $this_repo->{$key} = $this_repo->{'localized_'.$key}->{$default_lang};
		}
	    }

	    # URL must be defined
	    # The XML content might be, for instance, 404 Error Page, etc.
	    if (defined $this_repo->{'url'} && $this_repo->{'url'} ne "") {
		push @repos, $this_repo;
	    } else {
		y2warning ("Ignoring repository, no URL defined");
	    }
	}
    }

    return \@repos;
}

while (<STDIN>) {
    my ($command, $path, $file) = ycp::ParseCommand ($_);

    if ($command eq "Read") {
	if (! defined $file || $file eq "") {
	    y2error ("No file defined!");
	    ycp::Return (undef);
	    next;
	} elsif (! -e $file) {
	    y2error ("File ".$file." doesn't exist!");
	    ycp::Return (undef);
	    next;
	}

	y2milestone ("Reading: '".$file."'");
	open (FILE, $file) || do {
	    y2error ("Cannot open file ".$file.": ".$!);
	    next;
	};
	my @lines = <FILE>;
	close FILE;

	ycp::Return (GetRepositoriesFromXML(join ('', @lines)));
    } elsif ($command eq "result") {
	exit 0;
    } else {
	my $return_value = sprintf( "Unknown instruction %s", $command);
	ycp::Return ($return_value);
    }
}
