package xm::css::sheet;
use xm::o;

# subroutines for handling of css infos/files

# convert a css fieldhash into its text 
# representation. Assumes fieldhash container
# as returned by mkfield. does not output
# fields starting with x-. see asfieldx.
# inv: mkfield for the inverse operation
# use: in assheet subs to generate inverse of mksheet
sub asfield
{
    my $x = shift;
    my $T = " ";       

    if (exists $$x{_class})
    {
	$T .= $$x{_markup} if exists $$x{_markup};
	$T .= " " x (7 - length $T) if length $T < 10;
	$T .= ".".$$x{_class};
    }
    elsif (exists $$x{_name})
    {
	$T .= $$x{_name};          
    }else{
	# print STDERR "<no markup name>";
	return "";
    }
    # print STDERR "[",$T,"] ";

    if (length $T < 20) { $T .= " " x (20 - length $T); }
    
    $T .= " { ";
    my $k;
    for $k (keys %$x)
    {
	if ($k !~ /^_/ and $$x{$k} !~ /^x-/)
	{
	    if ((length $T) % 80 > 60) { $T .= "\n                       "; }
	    $T .= $k." : ".$$x{$k}." ; ";
	}
    }
    $T .= "}\n";

    return $T;
}
# convert a css fieldhash into its text 
# representation. Assumes fieldhash container
# as returned by mkfield. 
# inv: mkfield for the inverse operation
# use: in assheet subs to generate inverse of mksheet
sub asfieldx
{
    my $x = shift;
    my $T = " ";

    if (exists $$x{_name})
    {
	$T .= $$x{_name};
    }elsif (exists $$x{_class})
    {
	my $markup = "";
	$markup = $$x{_markup} if exists $$x{_markup};
	$T .= $markup.".".$$x{_class};
    }else{
	# print STDERR "<no markup name>";
	return "";
    }

    if (length $T < 20) { $T .= " " x (20 - length $T); }
    
    $T .= " { ";
    my $k;
    for $k (keys %$x)
    {
	if ($k !~ /^_/)
	{
	    if ((length $T) % 80 > 60) { $T .= "\n                       "; }
	    $T .= $k." : ".$$x{$k}." ; ";
	}
    }
    $T .= "}\n";

    return $T;
}

# reading a text containing css field information.
# this reader is only useful in conjunction with 
# the xm system as it expects all css tags to
# be in a simple format - just one leader 
# and one subclass. The leader may be empty, but
# the dot must be always there (and just one dot).
#
# example:
#     .myclass { color: #F0F0F0 ; }
# pre .class2  { color: #D0D0D0 ; }
# h2           { font-size: 6 ; }
#
# return:  hash.
# css-info -> fields 
#   with some automatic fields named with
#   a leading underscore, especially _name
#   _class and _markup. 
# results are:
#   _name has everything before the opening brace
#       e.g. ".myclass" "pre .class" "h2"
#   _markup has everything before the first special char
#       e.g.  "" "pre" "h2"
#   _class has everything after the last dot (that marks a class="ref")
#       e.g. "myclass" "class2" "/h2"
#    -> note that a nonclassed css-field gets its markup as classifcation
#   the fields class_prefix contains the 
#   corresponding rest that was left over by the split operation

# inv: asfield for the inverse operation
# use: mksheet for generating a hash of these.
sub mkfield
{
    local $_ = shift;
    my $x = { };
    my $v = "";
    my $choke = sub 
    { 
	my $v = shift; 
	$v =~ s/^\s*//; 
	$v =~ s/\s*$//; 
	return $v;
    };
    
    s{ (^|\}|<style>) ([^\{\}]*) \{ } { $$x{_name} = &$choke($2); $1 }sex;
    while (
       s{ (^|\}|<style>) \s* ([\w-]+) \s* : ([^\;]*) \; } 
	{ $$x{$2} = &$choke($3); $1 }gsex
    ) {};
    if ($$x{_name} =~ m{ ^ (.*) \. ([\s\w\-]*) $ }sx)
    {
	$$x{_markup} = $1;
	$$x{_class} = $2;
	$$x{_markup} =~ s{\s+}{}gs;
	$$x{_class} =~ s{\s+}{}gs;
    }else{
	$$x{_markup} = $$x{_name};
	$$x{_markup} =~ s{\s+}{}gs;
    }

    return $x;
}

# reading a filetext containing css information.
# this reader is only useful in conjunction with 
# the xm system as it expects all css tags to
# be in a simple format - just one leader 
# and one subclass. The leader my be empty to
# match as a subclass to any other leader, but
# the dot must be always there (and just one dot).
# and, to speak it aloud EVERY SUBCLASS MUST BE
# UNIQUE. The leader is only used for formatting
# in html documents (if no leader is given, a
# <span> will be generated).
#
# example:
#
# pre .class2  { color: #D0D0D0 ; }
#
# return: a hash of hash.
# subclass -> css-info
# css-info -> fields 
#
# use: if exists sheet{class} where class is an xm-markup
# inv: sub assheet is the inverse operation
#
sub mksheet
{
    local $_ = shift;
    my $sheet = { };

    while(
    s{ (^|\}|<style>|\*\/\s) 
	   (\s*[\w\-\:]*\s*[\.]\s*) ([\w\-]+) (\s*\{ [^\{\}]* \}) } 
    {     
	$$sheet{$3} = mkfield($2.$3.$4); 
	# print STDERR "<",$3,">\n";
	$1 
    }gsex
    ) {};

    while(
    s{ (^|\}|<style>|\*\/\s) 
	   (\s*) ([\w\-\:]+) (\s*\{ [^\{\}]* \}) } 
    {     
	$$sheet{"/".$3} = mkfield($2.$3.$4); 
	# print STDERR "</",$3,">\n";
	$1 
    }gsex
    ) {};

    return $sheet;
}

# convert a css sheethash into its text 
# representation. Assumes sheethash container
# as returned by mksheet
# inv: mksheet is the inverse operation
sub assheet
{
    my $x = shift;
    my $T = "";

    my $k;
    for $k (sort keys %$x)
    {
	if ($k !~ /^_/ and $$x{$k} !~ /^x-/)
	{
	    $T .= asfield($$x{$k});
	}
    }

    return $T;
}

# convert a css sheethash into its text 
# representation. Assumes sheethash container
# as returned by mksheet. Outputs also x-
# defines in the fields.
# see: assheet for the normal usage
# inv: mksheet for the inverse operation
sub assheetx
{
    my $x = shift;
    my $T = "";

    my $k;
    for $k (sort keys %$x)
    {
	if ($k !~ /^_/ and $$x{$k} !~ /^x-/)
	{
	    $T .= asfield($$x{$k});
	}
    }

    return $T;
}

# convert a css sheethash into its text 
# representation. Assumes sheethash container
# as returned by mksheet. Outputs ONLY those
# classes which are present in the 2nd arg hash.
# use: for embedding css-style information into some
#      output text - the output text's header has only
#      those css-tags that are used inside the text.
# inv: mksheet to generate a css-sheet.
sub assheetuse
{
    my $x = shift;
    my $a = shift;
    my $T = "";
    my $k;
    for $k (sort keys %$x)
    {
	if ($k !~ /^_/ and exists $$a{$k})
	{
	    $T .= asfield($$x{$k});
	}
    }
    return $T;
}

sub DESC {"
 read the css-style input, convert it to its internal form,
 and print the recogized information as css-style output.
"}

sub DO
{
    my $css = xm::css::sheet::mksheet($_[0]); 
    print "<style length=\"",scalar %$css,"\">\n"; 
    print xm::css::sheet::assheet($css);
    print "</style>\n";
}
sub ARGS { return    xm::o::args_stdin(@_,DESC); }
sub main { return DO(xm::o::args_stdin(@_,DESC)); }

1;