#!/usr/bin/perl -w
# NAME: sortcss.pl
# AIM: Read a file of CSS, in this case taken from my site,
# and 'sort' it - that is put each element together
# 11/07/2010 - checkout, and added input file name - parse_arg and
# output the tags with width formatting...
# 05/07/2008 geoff mclane http://geoffair.net/mperl specialised
# Decode of CSS file
# Skip C comments - /* ... */
# Starts with *, ., # or character is start of tag
# other tag names can follow after comma (,)
# definition begins with '{', and ends with '}' ...
use strict;
use warnings;
my $base_perl = 'C:/GTools/perl';
unshift(@INC, $base_perl);
require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
	my @tmpsp = split(/\\/,$pgmname);
	$pgmname = $tmpsp[-1];
}
my $outfile = $base_perl."\\temp.$pgmname.txt";
open_log($outfile);
###prt( "$0 ... Hello, World ...\n" );

my $in_file = 'C:\HOMEPAGE\GA\page2.css';
###my $in_file = 'temp.css';
my $out_file = $base_perl.'\tempcss2.css';
my $max_tag_width = 20;

# DEBUG
my $dbg1 = 0;	# show start and end of C comments
my $dbg2 = 0;	# show "$i:$lnnum:$colm:$tagnum: Got tag [$tag] ... CLOSED
my $dbg3 = 0;	# show "$tagset { $tagtxt } ($lnnum)
my $dbg4 = 0;	# show "File = $actfile [$comtxt] ...
my $dbg5 = 0;	# show "NO MATCH [$tag] ne [$ttag] - VERY, VERY NOISY!!!
my $dbg6 = 0;	# show "Searching for tag [$tag] ...\n" )

my @lines = ();
my $line = '';
my $lncnt = 0;
my %taglist = ();
my @tagarray = ();
my @warnings = ();
my $wmsg = '';
my @tagtxtdone = ();
my @tagoutput = ();

# features
my $out_nice_txt = 0;
my $out_repeats  = 0;

parse_args(@ARGV);

if ($in_file eq 'temp.css') {
	$dbg1 = 1;
	$dbg2 = 1;
	$dbg3 = 1;
	$dbg6 = 1;
}

my ($txt, $i, $ch, $len, $pch, $incom, $lnnum);
my ($colm, $tag, $intag, $tagtxt, $tagset, $tagnum);
my ($bgnln, $nch, $comtxt, $actfile, $tagp2);
if (open INF, "<$in_file") {
	@lines = <INF>;
	close INF;
	$lncnt = scalar @lines;
	$txt = join('', @lines);
	$len = length($txt);
	prt( "Got $lncnt lines to process ... $len characters ...\n" );
	$pch = '';
	$incom = 0;
	$lnnum = 1;
	$colm = 0;
	$tag = '';
	$comtxt = '';
	$actfile = '';
	for ($i = 0; $i < $len; $i++) {
		$ch = substr($txt,$i,1);
		set_line_col($ch, \$colm, \$lnnum);
		if ($incom) {
			$comtxt .= $ch;
			if ($ch eq '/') {
				if ($pch eq '*') {
					$incom = 0;
					prt( "$i:$lnnum:$colm: Exit comment ...\n" ) if ($dbg1);
					###if ($comtxt =~ /\w{1}:[\\\/]\w+/) {
					if ($comtxt =~ /\/\*.*(\w{1}:[\\\/]\w+.*)\*\//) {
						$actfile = $1;
						prt( "File = $actfile [$comtxt] ...\n" ) if ($dbg4);
					}
				}
			}
		} else {
			if ($ch eq '*') {
				if ($pch eq '/') {
					$incom = 1;
					prt( "$i:$lnnum:$colm: Entered comment ...\n" ) if ($dbg1);
					$comtxt = $pch.$ch;
				}
			} elsif ($ch =~ /[\w#\.\*]/) {
				$tag = $ch;
				$i++;
				$intag = 1;
				$tagset = '';
				$tagnum = 0;
				$bgnln = $lnnum;
				while ($intag) {
					$tagnum++;
					for (; $i < $len; $i++) {
						$ch = substr($txt,$i,1);
						set_line_col($ch, \$colm, \$lnnum);
						if ($ch =~ /[\s,\{]/) {
							$i++;
							last;
						}
						$tag .= $ch;
					}
					while (($i < $len)&&($ch =~ /\s/)) {
						# notes SPACE can also separate tags, so got to next non-space, but ...
						# EAT space
						while (($ch =~ /\s/)&&($i < $len)) {
							$ch = substr($txt,$i,1);
							set_line_col($ch, \$colm, \$lnnum);
							$i++;
						}
						if (($ch ne ',') && ($ch ne '{') && !($ch =~ /\s/) ) {
							# ok, it seems this BELONGS to the tag ???
							$tag .= ' '.$ch;
							for (; $i < $len; $i++) {
								$ch = substr($txt,$i,1);
								set_line_col($ch, \$colm, \$lnnum);
								if ($ch =~ /[\s,\{]/) {
									$i++;
									last;
								}
								$tag .= $ch;
							}
						}
					}

					if ($ch eq '{') {
						prt( "$i:$lnnum:$colm:$tagnum: Got tag [$tag] ... CLOSED \n" ) if ($dbg2);
					} else {
						prt( "$i:$lnnum:$colm:$tagnum: Got tag [$tag] ... ($ch)\n" ) if ($dbg2);
					}
					if (defined $taglist{$tag}) {
						$taglist{$tag}++;
					} else {
						$taglist{$tag} = 1;
					}
					$tagset .= '|' if length($tagset);
					$tagset .= $tag;
					$tag = '';
					# if we have a COMMA, eat spaces until next non-space char
					if (($ch eq ',')&&($i < $len)) {
						$nch = substr($txt,$i,1);
						while (($i < $len)&&($nch =~ /\s/)) {
							$i++;
							$nch = substr($txt,$i,1);
							set_line_col($nch, \$colm, \$lnnum);
						}
					}
					$intag = 0 if ($ch eq '{');
				}

				$tagtxt = '';
				if ($ch eq '{') {
					for (; $i < $len; $i++) {
						$ch = substr($txt,$i,1);
						set_line_col($ch, \$colm, \$lnnum);
						if ($ch eq '}') {
							last;
						}
						$tagtxt .= $ch;
						$pch = $ch;
					}
					$tagtxt =~ s/\n/ /g;
					$tagtxt = trim_all($tagtxt);
					prt( "Stored: [$tagset] { $tagtxt } ($lnnum)\n" ) if ($dbg3);
					push(@tagarray, ["$tagset", $tagtxt, $bgnln, 0, $actfile]);
				} else {
					mydie( "$i:$lnnum:$colm: No close tag ...\n" );
				}
		    }
		}
		$pch = $ch;
	}
} else {
	prt( "ERROR: Can NOT open $in_file ... check name. location \n" );
}
if (@tagarray) {
	my (@tags, $tgcnt, $j, $fnd, $ttag,$min,$len,$ctag);
	$tagnum = scalar @tagarray;
	$lnnum = scalar keys(%taglist);
	prt( "\n ***** RESULTS **** Options: out_nice_txt=$out_nice_txt, out_repeats=$out_repeats\n" );
	prt( "Got $tagnum tags to sort ... $lnnum different tags ...\n" );
    $min = 0;
	foreach $tag (keys %taglist) {
        $len = length($tag);
        $min = $len if ($len > $min);
        last if ($min > $max_tag_width);
    }
    $min = $max_tag_width if ($min > $max_tag_width);
	foreach $tag (sort keys %taglist) {
		$fnd = 0;
        $ctag = $tag;
        $ctag .= ' ' while (length($ctag) < $min);
		prt( "Searching for tag [$tag] ...\n" ) if ($dbg6);
		for ($i = 0; $i < $tagnum; $i++) {
			$tagset = $tagarray[$i][0];
			$tagtxt = $tagarray[$i][1];
			$bgnln  = $tagarray[$i][2];
			$actfile = $tagarray[$i][4];
			@tags = split(/\|/,$tagset);
			$tgcnt = scalar @tags;
			for ($j = 0; $j < $tgcnt; $j++) {
				$ttag = $tags[$j];
				if ($ttag eq $tag) {
					$fnd++;
					$tagarray[$i][3]++;
					$wmsg = compare_done_tags($tag, $tagtxt, @tagtxtdone);
					push(@tagtxtdone, [$tag, $tagtxt, $actfile]);
					push(@tagoutput, [$tag, $tagtxt, $actfile]) if ($wmsg ne 'repeat');
					if ($out_nice_txt) {
						if ($out_repeats) {
							prt( "$tag {\n".nice_tag_text($tagtxt)."\n} /* $bgnln $actfile $wmsg*/\n" );
						} elsif ($wmsg ne 'repeat') {
							prt( "$tag {\n".nice_tag_text($tagtxt)."\n} /* $bgnln $actfile $wmsg*/\n" );
						}
					} else {
						if ($out_repeats) {
							prt( "$ctag { $tagtxt } /* $bgnln $actfile $wmsg */ \n" );
						} elsif ($wmsg ne 'repeat') {
							prt( "$ctag { $tagtxt } /* $bgnln $actfile $wmsg */ \n" );
						}
					}
				} else {
					prt( "NO MATCH [$tag] ne [$ttag]\n" ) if ($dbg5);
				}
			}
		}
		if (!$fnd) {
			$wmsg = "WARNING: tag [$tag] NOT FOUND!";
			prt( "$wmsg\n" );
		}
	}
	$fnd = 0;
	for ($i = 0; $i < $tagnum; $i++) {
		if ($tagarray[$i][3] == 0) {
			$fnd++;
		}
	}
	if ($fnd) {
		prt( "\nWARNING: It appears $fnd items NOT utilised ...\n" );
		for ($i = 0; $i < $tagnum; $i++) {
			if ($tagarray[$i][3] == 0) {
				$tagset = $tagarray[$i][0];
				$tagtxt = $tagarray[$i][1];
				$bgnln  = $tagarray[$i][2];
				$actfile = $tagarray[$i][4];
				prt( "[$tagset] { $tagtxt } /* $bgnln $actfile */ \n" );
			}
		}
		prt( "CHECK THESE!!!\n" );
	} else {
		prt( "Appears ALL entries in tagarray covered ... as they should be ...\n" );
	}
}

if (@warnings) {
	prt( "NOTE: Got ".scalar @warnings. " warnings ...\n" );
	foreach $wmsg (@warnings) {
		prt( "$wmsg\n" );
	}
}

###my $out_file = 'tempcss2.css';
if (@tagoutput) {
	my $lastfile = '';
	if (open OUTF, ">$out_file") {
		print OUTF "/* CSS SUMMARY - ".scalar localtime(time()). " */\n";
		$tagnum = scalar @tagoutput;
		$lastfile = '';
		for ($i = 0; $i < $tagnum; $i++) {
			$tagset = $tagoutput[$i][0];
			next if ($tagset =~ /#/);
			next if (!($tagset =~ /^\./));
			next if (length($tagset) > 2);
			$tagtxt = $tagoutput[$i][1];
			$actfile = $tagoutput[$i][2];
			$wmsg = '';
			$wmsg = "/* $actfile */" if length($actfile) && ($lastfile ne $actfile);
			print OUTF "$tagset {\n".nice_tag_text($tagtxt)."\n} $wmsg\n\n";
			$lastfile = $actfile;
		}
		$lastfile = '';
		for ($i = 0; $i < $tagnum; $i++) {
			$tagset = $tagoutput[$i][0];
			next if ($tagset =~ /#/);
			next if (($tagset =~ /^\./)&&(length($tagset) <= 2));
			$actfile = $tagoutput[$i][2];
			$tagtxt = $tagoutput[$i][1];
			$wmsg = '';
			$wmsg = "/* $actfile */" if length($actfile) && ($lastfile ne $actfile);
			print OUTF "$tagset {\n".nice_tag_text($tagtxt)."\n} $wmsg\n\n";
			$lastfile = $actfile;
		}
		$lastfile = '';
		for ($i = 0; $i < $tagnum; $i++) {
			$tagset = $tagoutput[$i][0];
			next if !($tagset =~ /#/);
			next if (($tagset =~ /^\./)&&(length($tagset) <= 2));
			$tagtxt = $tagoutput[$i][1];
			$wmsg = '';
			$wmsg = "/* $actfile */" if length($actfile) && ($lastfile ne $actfile);
			print OUTF "$tagset {\n".nice_tag_text($tagtxt)."\n} $wmsg\n\n";
			$lastfile = $actfile;
		}
		print OUTF "/* eof - $out_file */\n";
		close OUTF;
		prt( "Written file $out_file ...\n" );
	}
}

close_log($outfile,1);
exit(0);
################################
# usage: $wmsg = compare_done_tags($tag, $tagtxt, @tagtxtdone);
sub trim_array_items {
	my (@ar) = @_;
	my $l = scalar @ar;
	for (my $p = 0; $p < $l; $p++) {
		$ar[$p] = trim_all($ar[$p]);
	}
	return @ar;
}

sub compare_done_tags {
	my ($tg, $tt, @done) = @_;
	my $sz = scalar @done;
	my @arr1 = split(';',$tt);
	my $al1 = scalar @arr1;
	my ($m, $n, $p, $q, $same, $itm1, $itm2);
	@arr1 = trim_array_items(@arr1);
	for ($m = 0; $m < $sz; $m++) {
		my $ttg = $done[$m][0];
		###if ($tg eq $ttg) {
		if (lc($tg) eq lc($ttg)) {
			my $ttt = $done[$m][1];
			my @arr2 = split(';',$ttt);
			my $al2 = scalar @arr2;
			@arr2 = trim_array_items(@arr2);
			$same = 0;
			for ($n = 0; $n < $al1; $n++) {
				$itm1 = $arr1[$n];	# for each attribute in passed tag text
				my @arr3 = trim_array_items(split(':',$itm1));	# split attribute 'margin:0'
				my $al3 = scalar @arr3;
				for ($p = 0; $p < $al2; $p++) {	# now of done itme
					$itm2 = $arr2[$p];	# extract tag text item, split on ';'
					my @arr4 = trim_array_items(split(':',$itm2));	# get split on ':';
					my $al4 = scalar @arr4;
					if ($al3 == $al4) {
						# if two splits are the same
						for ($q = 0; $q < $al3; $q++) {
							# try to correct case, at least on some, like color
							if ($q == 1) {
								if (substr($arr3[$q],0,1) eq '#') {
									$arr3[$q] = lc($arr3[$q]);
								}
								if (substr($arr4[$q],0,1) eq '#') {
									$arr4[$q] = lc($arr4[$q]);
								}
							}
							if ($arr3[$q] ne $arr4[$q]) {
								last;
							}
						}
						if ($q == $al3) {
							$same++;
						}
					}
				}
			}
			if ($same == $al1) {
				return "repeat";
			}
		}	# tags are equal
	}
	return "";
}

sub set_line_col {
	my ($ch1, $colm1, $lnnum1) = @_;
	$$colm1++;
	if ($ch1 eq "\n") {
		$$colm1 = 0;
		$$lnnum1++;
	}
}

# expect /\s*\w\s*:\s*.+;{0,1}/ repeated
sub nice_tag_text {
	my ($tt) = shift;
	$tt = trim_all($tt);
	my $len = length($tt);
	# got for a simple thing ...
	my @arr = split(';',$tt);
	my $asz = scalar @arr;
	my $ntt = '';
	for (my $k = 0; $k < $asz; $k++) {
		my $ti = trim_all($arr[$k]);
		$ntt .= "\n" if length($ntt);
		$ntt .= " $ti;";
	}
	return $ntt;
}

sub parse_args {
    my (@av) = @_;
    while (@av) {
        my $arg = $av[0];
        if ($arg =~ /^-/) {
            prt("Only input is input CSS file to sort...\n");
            exit(1);
        }
        $in_file = $arg;
        shift @av;
    }
}

# eof - sortcss.pl
