#!/usr/bin/perl -w

use POSIX;
# Copyright 2004: Marcell Gal, Jason Russell

# this script helps grouping the components
# 1) We have very detailed categorisation in a
# manually maintained categories.txt file.
# 1.3) warn for duplicates (category or part)
# 1.4) some local replacement hacks here
# 2) Also got some component info generated from the SCH+PCB editor 
# 3) We consider some rules, and make 5 groups.
# 4) We print the data for each group, and some more....

# ========================
# handle pinheader 1x0? and 2x0? 

# list components in unknown category
# TODO: read replacement hacks from file
# 
# TODO: handle stock and suggest "list-order.txt"
# TODO: rotate some resistors 180 degree for nicer result, optically

# Done: allow command line argument or $ENV{}
usage() unless $ARGV[0];
while (@ARGV){
    my $flag = shift(@ARGV);
    my $value = shift(@ARGV);
    $args{$flag} = $value;
}

$categories_file = defined($args{'-c'}) ? $args{'-c'} : 'categories.txt';
$pnp_file =        defined($args{'-p'}) ? $args{'-p'} : 'megasquirtavr.pnp';

# the final rules and output we want is disjunct grouping like:

# N_* is dropped
# some other components are dropped depending on value (1G, 0uF)
# list-drop.txt		(not needed: whatever the filters dropped)

# R_* category goes to rescue kit
# list-rescue.txt	(rescue kit: category that start with R_)

# throughole goes to manual
# list-manual.txt	(eg. throughole that we want to put in manually)

# SMD top goes ...
# list-top.txt		(PNP-d)

# SMD bottom goes ...
# list-bottom.txt	(PNP-d)

# ----- end of disjunct groups, THAT's all :-)

# some helper files that are just union. Note that file concat is not possible,
# since each of the above files are sorted by component values
# list-pnp.txt		(PNP-d: union of top and bottom. This shouldn't be needed, but..)

# list-all.txt	(the union of all the above, 
# preferrable with a note of the relevant /see above/ group for each component)

# DONE: it would be nice to have the componentcount 
# for each component values in each file
# (it's OK below the components, putting the count above would require 
# some caching (writing after processing is done)

# ======================================
# parse the main input files:
parsecat($categories_file);

# we need the part middle not the last pad, so:
$pack_offset->{C1206} = [1477, 0];
$pack_offset->{C1210} = [1395, 0];
$pack_offset->{R0805} = [855, 0]; # I measured 936 first time ???

$pack_offset->{C0805} = [855, 0];
$pack_offset->{SMB} = [-2201, 0];	# negative !!! 2000..2002
$pack_offset->{SOT23} = [0, 1101];	# 1100 ?

# some repetitions with different keys:
$pack_offset->{1206} = $pack_offset->{C1206};
$pack_offset->{1210} = [1584, 0];	# SHHHIIT !!!
$pack_offset->{'0805'} = $pack_offset->{R0805};
# =============================
# low-priority TODO: move this to parameter file
$board_comment_header = "\# GenBoard v3.2 r244_mg \n";
	
# coordinates of fiducial points (y,x format, as - interestingly - all other coords
$Fiducial0->{Top} 	=	[2540, 90679];
$Fiducial0->{Bottom} =	$Fiducial0->{Top};
$Fiducial1->{Top}	=	[156462, 7877];
$Fiducial1->{Bottom}=	[158243, 6604];

if(1 || undef){
	# $Offset->{Top} 		= 	[-156462, -7877];
	# $Offset->{Bottom} 	= 	[-158243, -6604];
	$Offset->{Top} 		= 	[ -2540, -90679];
	$Offset->{Bottom} 	= 	$Offset->{Top};

} else {
	$Offset->{Top} 		= 	[0,0];
	$Offset->{Bottom} 	= 	[0,0];
}

# this is done at the very end, for their convenience (mostly + numbers)
$mult->{Top} 		= 	[ 1, -1];
$mult->{Bottom} 	= 	[ 1, -1];

$valreplace->{HIP9011} = 'TPIC8101DW';
$valreplace->{IXDN404SI} = 'MC33151D';	# replaces IXDI404SI since inverting
$valreplace->{AD596} = 'AD597';
#valreplace->{'2.49K'} = '2.7k';
$valreplace->{'2.49k'} = '2.7k';
$valreplace->{'100nF'} = '0.1uF';
$valreplace->{'100n'} = '0.1uF';
$valreplace->{'150k'} = '1G';	# SCH-TODO: change R112 in schematic
$valreplace->{'1.6M'} = '1M';	# SCH-TODO: change R87 R89 in schematic

$packreplace->{C0805} = '0805';
$packreplace->{C0805K} = '0805';
$packreplace->{C1210} = '1210';
$packreplace->{C1206} = '1206';

#========================
# although 4V cannot raise to 16V in any case, Sly wanted this way:
$partvalreplace->{C50} = '1uF';
$partvalreplace->{C52} = '1uF';

$partvalreplace->{C70} = '10uF'; # done in sch, remove this after ULP-ing
$partpackreplace->{C70} = '1206';	# still needed

$partvalreplace->{C80} = '1uF';
$partpackreplace->{C80} = '1206';
$partpackreplace->{C95} = '1206';
$partpackreplace->{C82} = '1206';

$partvalreplace->{MAIN5V} = 'MC33269DT5';

# ===============================
# "manual" verification of the transformation for some random components:

my $top; my $bot;
$top->{R82}=[28697, 47756];
$top->{R50}=[19420, 57409];
$top->{C57}=[53094, 41420];
$top->{C56}=[52470, 26678];
$top->{C70}=[67837, 3875];
$top->{R137}=[13852, 14475];
$top->{C52}=[19287, 26144];
$top->{R100}=[11804, 41776];
$top->{C60}=[57414, 27435];
$top->{R48}=[67970, 40128];
$top->{C34}=[82046, 66272];
$top->{R92}=[98793, 52064];
$top->{D7}=[109071, 90169];
$top->{C12}=[139959, 95013];
$top->{C45}=[43573, 35704];
$top->{C36}=[20320, 54860]; 	# ^ Anita, V mcell
$top->{R77}=[18550, 54860];
$top->{C68}=[58940, 4460];
$top->{D25}=[106930, 63756];
$top->{D4}=[53085, 94477];	# SOT23 # 20.


# $bot->{L4}=[20071, 45962];
$bot->{C83}=[12935, 69598];
$bot->{C82}=[8231, 58319];	# ?? looks [8250, 58300] on mcell's computer and eagle
$bot->{C94}=[11812, 49552];
$bot->{C75}=[37337, 25398];
$bot->{R149}=[47998, 45222];
$bot->{C84}=[37682, 84511];
$bot->{R174}=[73173, 85581];
$bot->{R55}=[78251, 43940];
$bot->{R113}=[67828, 29240];
$bot->{C96}=[11940, 55370];	# ^ Anita, V mcell
$bot->{C73}=[7624, 51057];
$bot->{C80}=[32260, 56900];
$bot->{C81}=[11690, 52830];	# 13.

$Verify->{Bottom} = $bot;
$Verify->{Top} = $top;

# ========================

parsepnp($pnp_file);	# this also runs the grouping

my $command;
$command = {};
#--$command->{compactlist} = 1;
#--$command->{filename} = $compact_file;
$command->{warnmissing} = 1;

# first group everything (but no report printing!):
#-- makelist($command, [sort keys %$o]);

# -------------------
# now RUN the REPORTS:
$cmd = {
	value_style => 'detailed',
	component_style => 'detailed'
};	# global hash, maybe used later

my $f;
$f = 'drop';
report($f, [$f]);

$f = 'rescue';
report($f, [$f]);

$f = 'manual';
report($f, [$f]);

$f = 'top';
report($f, [$f]);

$f = 'bottom';
report($f, [$f]);

report('pnp', ['top','bottom']);

report('all', ['drop','rescue','manual','top','bottom']);


$f = 'crazy';
report($f, [$f]);

$cmd->{component_style} = undef;
report('buy', ['rescue','manual','top','bottom']);

report_bycomp('comp',['top','bottom']);

print STDERR "no_offset_for_type: ". (join ("\n",keys %$no_offset_for_type) ) ."\n";
#==================
sub board_comment
{
	# after made once, it never changes:
	return $board_comment if defined $board_comment;
	
	$board_comment = $board_comment_header
	. '# printed_at: '. ctime(time());

	my $k;
	foreach $k ($Fiducial0, $Fiducial1){
		$board_comment .= fiducial_comment($k);
	}


	$board_comment .= other_comment("Offset", $Offset) ;
	$board_comment .= other_comment("Mult", $mult) ;
 
	# "\# BoardCoordOffset= (->{top}->[0], $Offset->{top}->[1]\n\n";
	return $board_comment;
}

# well, moving the zip together,  makes this redundant...
sub other_comment
{
	my $text = shift;
	my $href = shift;

	my $board_comment = '';
	foreach $layer (keys %$href){
		my $aref = $href->{$layer}; 
		print STDERR "Dimension overflow :-) \n" if defined $aref->[2];
		my ($x, $y) = ( @$aref);

		$board_comment .= '# '. $text . "_" . $layer
		.'= ['
		.$x
		.','
		.$y
		."\]\n";
	} # foreach
	return $board_comment;
}

sub fiducial_comment
{
	my $fidu = shift;
	my $board_comment = '';
	my $layer;
	foreach $layer (keys %$fidu){
		my $aref = $fidu->{$layer}; 
		# hehe, always 2 dimensions...	
		print STDERR "Fiducial Dimension overflow :-) \n" if defined $aref->[2];
		my ($x, $y) = finalconvert($layer, @$aref);

		$board_comment .= '# Fiducial '. $layer
		.'= ['
		.$x
		.','
		.$y
		."\]\n";
	} # foreach
	return $board_comment;
}

sub verify
{
	my $layer = shift; # only necessary to check the "Verify" data itself
	my $comp = shift;
	my $x = shift;
	my $y = shift;
	
	if( ! exists $Verify->{$layer}->{$comp} ){
		return;
	}

	my $expected = $Verify->{$layer}->{$comp};
	my ($ex, $ey) = ($expected->[0] , $expected->[1] );

	# error in 0.01 mm:
	my $dx = 0.1 * ($ex - $x);
	my $dy = 0.1 * ($ey - $y);
	
	my $distsqr = $dx *$dx + $dy*$dy;
	
	$verifycount++;
	$errsqrsum += $distsqr;
	
	print STDERR "VERIFY $verifycount: $layer $comp  distsqr=($distsqr) errsqrsum=$errsqrsum\n"
		. ($distsqr > 100 ? " expected ($ex, $ey) got ($x, $y) " : "")
		. "\n\n";

}

# 
# =========================
sub rotate
{
	my $angle = shift;
	my $aref = shift;
	
	if(!defined $aref){
		return (0, 0);
	}
	my ($x, $y) = ($aref->[0], $aref->[1]);

	my $logmsg = "rotate($angle,$x,$y)=>" ;
	# primary school where are you...
	my $radian = $angle * 3.14159265358 / 180;
	
	($x, $y) = ($x*cos($radian) - $y*sin($radian), 
		$x*sin($radian) + $y*cos($radian));
	print STDERR $logmsg . "($x,$y)\n";
	return ($x, $y);
}
sub CoordConvert
{
	my $layer = shift;
	my $pck = shift;
	my $angle = shift;
	my $comp = shift;
	my $x = shift;
	my $y = shift;
	
	my $logmsg = "CoordConvert $comp $angle $pck at $layer($x,$y)=>" ;

	if( ! exists $pack_offset->{$pck}){
		# print STDERR "\nno_offset_for_type $pck\n"; 
		$no_offset_for_type->{$pck} ++;
	}
	
	# small offset to find the _center_ of the part:
	my ($xa, $ya) = rotate( $layer eq 'Bottom' ?  0- $angle:
		$angle -180,		 
		$pack_offset->{$pck});
	
	$x += $xa;	$y += $ya;
	
	verify($layer, $comp, $x, $y);
	($x, $y) = finalconvert($layer, $x, $y);
	print STDERR $logmsg . "($x,$y)\n";
	return ($x, $y);

}

# FINAL operations for their convenience:
sub finalconvert{
	my $layer = shift;
	my $x = shift;
	my $y = shift;

	if(! exists $mult->{$layer}){
		print STDERR "Fuck, expected Bottom or Top, not $layer\n";
		return ($x, $y);
	}
	
	# OFFSET:
	my $offs = $Offset->{$layer};
	$x += $offs->[0];
	$y += $offs->[1];
	
	# SCALE:
	my $m = $mult->{$layer};
	
	$x *= $m->[0];
	$y *= $m->[1];
	# we round to 0.01 mm:
	$x = int(($x + 5) * 0.1); $y = int(($y + 5) * 0.1);
	
	return ($x, $y);
}

# ======
# traverses a category-list. CURRENTLY NOT USED
sub makelist
{
my $cmd = shift;
my $catlist = shift;

my $k;
# mydebug($catlist);
foreach $k (@$catlist){
	my $aref = $o->{$k};	# reference to category array
	my $component;
	foreach $component (@$aref){
        my $partinfo = $bycomp->{$k};
		if(exists $cmd->{warnmissing}){
			add2collection(\%all, $component, $k);
		} # warnmissing
		add2deep($byvalues, $component);	
	}
}

} # sub makelist()

# ================
sub open_fil
{
	my $f = shift;
	my $outdir = "pnpout";
	if( ! -d "$outdir" ){
		mkdir("$outdir");
	};
	my $fil = "$outdir/list-$f.txt";

	open DETAILEDLIST, ">$fil" || die "could not open $fil: $!";
	print DETAILEDLIST board_comment();
	return $fil;
}
# ------------------
sub report
{
	my $f = shift;
	my $groups = shift;	# array reference
	my $totalcnt = 0;
	my $typecnt = 0;
# let's see the groups in a hash (what is the nicer way? Well: grep.)
	my $interesting={}; my $gr;
	foreach $gr (@$groups){
		$interesting->{$gr} = 1;
	}
	print "writing output: ".open_fil($f)
		.' ('. join(',',@$groups).")\n";
		
# we need detailed output listed per values
	my $vp;
	# order by (value, package) vector (prepared in parsepnp)
	foreach $vp (sort (keys %$byvalpack)){	# each value
	
		my $cnt = 0;
		my $str = '';
		my $href = $byvalpack->{$vp};
		my $val = $valpack->{$vp}->{val};
		my $pck = $valpack->{$vp}->{pck};
		
		my $comp;
		foreach $comp (keys %$href){
		    # if (exists($bycomp->{$k}->{value}) && ($bycomp->{$k}->{value} eq $val))
			$gr = $bycomp->{$comp}->{group};
			if(exists $interesting->{$gr}){
				$str .= sideserial($gr, $bycomp->{$comp}) . '|'. component_data($comp);
				$cnt++;
				# we don't know pincount, though PNP price is effected :-(
			}
		}
		
		if($cnt > 0){
			$typecnt++;
			$totalcnt += $cnt;
		# TODO: don't print the empty ones
			print DETAILEDLIST "\n\[ $vp \]";
			
#	This should work, what's wrong?:
#			print DETAILEDLIST "\[ val=$val ";
#			print DETAILEDLIST "pack=$pck \]\n";
			print DETAILEDLIST " \# count=$cnt\n"
			. (defined $cmd->{component_style} ? $str : '')
			. "\n";
			
		}
	} 
	
	print DETAILEDLIST "\n\n\# totalcount=$totalcnt\n\# typecount=$typecnt\n"
	# close DETAILEDLIST;
} # report()

# separate component serial number for each side
sub sideserial
{
	my $side = shift;
	my $compref = shift;
	
	if(defined $compref->{sideserial}){
		return $compref->{sideserial};	# cache it to be consistent
	}
	
	$compref->{sideserial} = ++ $sideserial_h->{$side};
	return $compref->{sideserial};
}
# ---------------
# sort by component names (not by values)
sub report_bycomp
{
	my $f = shift;
	my $groups = shift;	# array reference
	
	print "writing output: ".open_fil($f)
		.' ('. join(',',@$groups).")\n";
		
	my $comp;
	# serial number that starts over for each side:
	my $ser = 1;
	foreach $comp (sort keys %$bycomp){
		
		my $b = $bycomp->{$comp};
		my $gr = $b->{group};
	
		# if(length(grep $gr, @$groups)>0)  # zzz why wrong?
		if($gr eq 'top' || $gr eq 'bottom')
		{
			print DETAILEDLIST $ser.'|' . component_data($comp);
			$ser ++;
		}
	}
}
# --------------
# TODO: filter out automagically
# DONE: throughole automatically out from PNP list
# DONE: 1G, C..=0 automatically out
# TOP and BOTTOM side to separate files optionally
#     Should be elsewhere probably &report()
# DONE: R2512 footprint out

# duplication check DONE ??
# move the to-drop regexps (like /2512/ to similar place as packreplace
sub component_group
{
    my $b = shift;	# not component name, but object (hash)

	return 'crazy' if ! defined $b->{cat};
	return 'drop' if $b->{cat} =~ /^N\_/;

	return 'crazy' if ! defined $b->{comp};
	my $comp = $b->{comp};	# component name
	
    if ($comp =~ /^SJ\d+/){	# jumper
	    return 'drop';	# buy them from Al & Grippo
	}
	
    if ($comp =~ /^R\d+/){	# resistor
	if ($b->{value} eq "1G"){
	    print "Forget $comp because value = $b->{value}\n";
	    return 'drop';
	}

	if ($b->{pck} =~ /2512/){
   	    print "Forget $comp because pack = $b->{pck}\n";
	    return 'drop';
	}
	}	# if resistor

    if ($comp =~ /^C\d+/){	# capacitor
		return 'drop' if ($b->{value} =~ /^0(.F)?$/);
    }
	
	# If the part makes it this far, we might want it
    # return 'rescue' if ($b->{pck} eq "SMB");
	return 'rescue' if $b->{cat} =~ /^R\_/;
	
	# FIASCO: 10uF 1210 out of stock:
	# return 'rescue' if $b->{pck} =~ /1210/ && $b->{value} =~ /^10uF/;
	
	# we want it on the board, but maybe not picknplaced!
    return 'manual' if ($b->{mount} eq "ThruHole");

	# the sugar parts: 
	return 'top' if $b->{layer} eq 'Top';
    return 'bottom' if $b->{layer} eq 'Bottom';
	
	# what the hell?
	return 'crazy';
}	# component_group()
# ===================
# find some reasonable replacement for undefined components:
sub standard_comps
{
	my $comp = shift;
	my $v = shift;
	my $pck = shift;
	
	# do not touch reasonable values:
	
	return ($comp, '1G', $pck) if(($v eq 'T') && ($comp =~ /^R\d+/));
	
	# some hacks:
	if(($comp =~ /^C\d+/) && ($v eq '1uF') &&  $pck =~ /1210/){
		return ($comp, '1uF', '1206');	# voltage is more than enough
	}
	
	# and the rest is clean:
	return ($comp, $v, $pck) if (defined $v && $v !~ /^\s*$/);

	if ($comp =~ /^D\d+/){	# diode
		$v = 'BAV99' if $pck eq 'SOT23';
		$v = 'LL4148' if $pck eq 'SOD80C';
	}
	
	if($pck eq 'SO14'){
		$v = 'LM1815M';	# what else could it be? :-)
	}
	
	return ($comp, $v, $pck);
}
# ========================
# This will filter on any property in the hash. value, pack, layer, mount, etc.
# not used...
sub filter
{
    # Example: filter(R89,layer,Top) 
    # Will return 1 if R89->layer = Top
    my $component = shift;
    my $property = shift;
    my $request = shift;
    return 1 if ($bycomp->{$component}->{$property} eq $request);
}

# ==============
# not used
sub filter_is_in_categories
{
	my $component = shift;
	# ouch, @categories is an array, not a hash
	# use grep ? fix syntax
	return 1 if grep($component, @categories);
	return undef;
}

# ----------------------------
# returns the value of a component as known in eagle (0.22u)
sub valueof
{
	my $component = shift;
	my $v = $bycomp->{$component}->{value};
# TODO: hash microF and uF to same
# TODO: move component value consolidation here (it's inlined)
	return "SILLY_VALUE" if ! defined $v;
	return $v;
}
# --------- 
# add component to collection and warn if already added
sub add2collection
{
	my $collection = shift;
	my $component = shift;
	my $k = shift;
	if(defined $collection->{$component}){
		print STDERR "Warning: \[$k\]; $component previously defined in collection \[$collection->{$component}\]\n";
	} else {
		$collection->{$component}=$k;	# just check that it is there
	}
}
# ---------------------------
# add collection->value->component and warn if already added
sub add2deep
{
	my $collection = shift;
	my $component = shift;
	my $value = valueof($component);

	$collection->{$value}->{$component} = 1;
}

sub noduplicates
{
	my $duplhash = shift;
	my $entity = shift;

	if(exists $duplhash->{$entity}){
		print STDERR "oops, duplicate: $entity"; 
	} else {
		$duplhash->{$entity} ++;	
	}
}
#===============
sub parsecat
{
	my $filen = shift;

	open CAT, "<$filen" or die("Can not open $filen: $!\n");

	my $comment;
	my $l;
	while($l=<CAT>){
		chomp $l;
		next if($l =~ /^\s*$/); # skip empty lines
		if($l =~ /^\s*\#(.*)/){
			$comment .= $1;
		} elsif($l =~ /^\s*\[(\w+)\](.*)/){ # yes: [category] componentlist
			my $cat = $1;
			noduplicates($category_noduplicates, $cat);
			
			my $complist = $2;
			my @a = split /\s+/,$complist;
			# make the whole category available:
			$o->{$cat}=[@a];
			
			# make category info _directly_ available for each component
			# see component_category()
			my $k;
			foreach $k (@a){
				noduplicates($part_noduplicates, $k);
				$component_category->{$k} = $cat;
			}
			
			# memorize category $comment
			$catcomment->{$cat}=$comment;
			
			# and clean it:
			$comment = '';
		} else {
			my $len = length($l);
			print "malformed line($len): $l\n";
			# TODO: allow component list wrap?
		}
	}

	close CAT;
}

# ------------------------------
# TODO: make category info _directly_ available for each component
sub component_category
{
	my $component = shift;
	return $component_category->{$component};
}

# ===========
sub replace
{
	my $replacehash = shift;
	my $what = shift;
	my $default = shift;
	
	if(defined $replacehash && exists $replacehash->{$what} ){
		return $replacehash->{$what};
	}
	
	return $default if defined $default;
	return $what;	# return unchanged
}
#===============
#; num [ id] [   pack ] [            part value ] [  X ] [  Y ] ANG  mountingstyle   Layer
# [  2]  AGND       1X01                            45273 124426  90 ThruHole Top

sub parsepnp
{
	my $filen = shift;

	my $v_prev = 'this cannot be';
	open CAT, "<$filen" or die("Can not open $filen: $!\n");

	my $comment;
	my $l;
	while($l=<CAT>){
		chomp $l;
		next if($l =~ /^\s*$/); # skip empty lines
		next if($l =~ /^\s*[\#\;](.*)/); # skip comments (# or ;)
		
# ; num [ id] [   pack ] [            part value ] [  X ] [  Y ] ANG          Layer
		my ($nu, $r) = ($l =~ /^\[\s*(\d+)\](.*)/);
		if(! defined $r){
			print STDERR "problem at line $l\n";
			next;
		}

		my @a = split /\s+/, $r;
		my $len = $#a;	# length-1 in fact (last index)
		# print "parsepnp 4 $len\n";
		# we accept 4 because some parts have stupid values like "22 .0" or "4 MHz"
		print "warning: line splitted to $len parts: $l\n" if ($len >4 && $len<=6);
		next if $len < 7;
		# $len==7 if no value for the component
		# $len==8 $a[7] is the latest, when all goes well

		shift @a; # something there (empty?)
		my $comp = shift @a;

		my $b;
		my $origpck  = shift @a;
		$pck = replace($partpackreplace, $comp, $origpck);	# is this useful?
		$pck = replace($packreplace, $pck);

		$b->{layer} = pop @a;
		$b->{mount} = pop @a;
		$b->{angle} = pop @a;
		my $y = pop @a;		# y is first
		my $x = pop @a;
		($b->{x}, $b->{y}) = CoordConvert($b->{layer}, $origpck, $b->{angle}, $comp, $x, $y);
		
		my $v = join '',@a;	# anything left in the middle is value (0..N sections)

		$v = replace($partvalreplace, $comp, $v);

		$v =~ s/(.*\d)[kK]$/$1k/; # Make all resistor k's the same
		$v =~ s/(.*\d[un])[fF]$/$1F/; # Make all uf to be uF
		$v =~ s/(.*\d[un])$/$1F/; # Add the F to u or n lazy caps
		
#		$track = 'R136';
#		print STDERR "zzzz1: $v :\n" if $comp eq $track;
		$v = replace($valreplace, $v);
#		print STDERR "zzzz2: $v :\n" if $comp eq $track;
		
		
		
		($comp, $v, $pck) = standard_comps($comp, $v, $pck);
		
		# store what we've got:
		$b->{comp} = $comp;
		$b->{value} = $v;
		$b->{pck} = $pck;
		$b->{cat} = component_category($comp);

		print "\'$v\'". ($v eq $v_prev ? ' ': "\n");
		$v_prev = $v;	# new component value

		$bycomp->{$comp} = $b;
		$byvalues->{$v}->{$comp} = $b;
		
		# value AND package are interesting for PNP, since different tray
		# (same value, but different package like 10uF 1206 and 1210):
		my $valpack = $v . "|" . $pck;
		$byvalpack->{$valpack}->{$comp} = $b;
		$valpack->{$valpack}->{val} = $v;	# I don't trust splitting of string
		$valpack->{$valpack}->{pck} = $pck;
		
		# the main task:
		my $group = component_group($b);
		$b->{group} = $group;
		$g->{$group}->{$comp}=$b;
	}

	close CAT;
}

# ===================
# return available data as String
sub component_data
{
	my $comp = shift;
	my $b = $bycomp->{$comp};
	my $st = '';
	if(!defined $b->{cat}){
		$b->{cat} = 'nocat';
		print STDERR "(nocat:$comp)";
	}
	
	$st = $b->{cat};
	
	return "$b->{value}|$b->{comp}|$b->{pck}|$b->{layer}|$b->{mount}|$b->{angle}|$b->{x}|$b->{y}|$st\n";
#	return "comp=$b->{comp}; pack=$b->{pck}; layer=$b->{layer}; mount=$b->{mount}; angle=$b->{angle}; x=$b->{x}; y=$b->{y}; val=$b->{value}\n";

}


sub usage
{
    print "Usage: -- OBSOLETE\n";
    print "\t$0 \n";
    print "\t\t-c <categories file>\t[Default: categories.txt]\n";
    print "\t\t-p <pnp file>    \t[Default: megasquirtavr.pnp]\n";
#   print "\t\t-s <compact file>\t[Default: compactlist.txt]\n";
    print "\n";
}
