package scanner;
# scanner.pm $Id$
# Yves Duret <yduret at mandrakesoft.com>
# Copyright (C) 2001 MandrakeSoft
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# pbs/TODO:
# - scsi mis-configuration
# - devfs use dev_is_devfs()
# - with 2 scanners same manufacturer -> will overwrite previous conf -> only 1 conf !!
# - lp: see printerdrake
# - install: prefix --> done

use standalone;
use common;
use detect_devices;
use log;


my $_sanedir = "$prefix/etc/sane.d";
my $_scannerDBdir = "$prefix$ENV{SHARE_PATH}/ldetect-lst";
$scannerDB = readScannerDB("$_scannerDBdir/ScannerDB");

sub confScanner {
    my ($model, $port) = @_;
    $port ||= detect_devices::dev_is_devfs() ? "$prefix/dev/usb/scanner0" : "$prefix/dev/scanner";
    my $a = $scannerDB->{$model}{server};
    #print "file:[$a]\t[$model]\t[$port]\n| ", (join "\n| ", @{$scannerDB->{$model}{lines}}),"\n";
    output("$_sanedir/$a.conf", (join "\n", @{$scannerDB->{$model}{lines}}));
    substInFile { s/\$DEVICE/$port/ } "$_sanedir/$a.conf";
    add2dll($a);
}

sub add2dll {
    return if member($_[0], chomp_(cat_("$_sanedir/dll.conf")));
    local *F;
    open F, ">>$_sanedir/dll.conf" or die "can't write SANE config in $_sanedir/dll.conf: $!";
    print F "$_[0]\n";
    close F;
}

sub detect {
    my ($i, $elem, @res) = (0, {});
    foreach (grep { $_->{driver} =~ /scanner/ } detect_devices::usb_probe()) {
	#my ($manufacturer, $model) = split '\|', $_->{description};
	#$_->{description} =~ s/Hewlett[-\s_]Packard/HP/;
	$_->{description} =~ s/Seiko\s+Epson/Epson/i;
	push @res, { port => "/dev/usb/scanner$i", val => { #CLASS => 'SCANNER',
							    #MODEL => $model,
							    #MANUFACTURER => $manufacturer,
							    DESCRIPTION => $_->{description},
							    #id => $_->{id},
							    #vendor => $_->{vendor},
							  } };
	++$i;
    }
    foreach (grep { $_->{media_type} =~ /scanner/ } detect_devices::getSCSI()) {
	   $_->{info} =~ s/Seiko\s+Epson/Epson/i;
	   push @res, { port => "/dev/sg", 
				 val => { DESCRIPTION => $_->{info} },
	   };
	   ++$i;
    }
    @res;
}


sub readScannerDB {
    my ($file) = @_;
    my ($card, %cards);

    my $F = common::openFileMaybeCompressed($file);

    my ($lineno, $cmd, $val) = 0;
    my $fs = {
        LINE => sub { push @{$card->{lines}}, $val },
	NAME => sub {
	    #$cards{$card->{type}} = $card if ($card and !$card->{flags}{unsupported});
	    $cards{$card->{type}} = $card if $card;
	    $val =~ s/Seiko\s+Epson/Epson/i;
	    $card = { type => $val };
	},
	SEE => sub {
	    $val =~ s/Seiko\s+Epson/Epson/i;
	    my $c = $cards{$val} or die "Error in database, invalid reference $val at line $lineno";

	    push @{$card->{lines}}, @{$c->{lines} || []};
	    add2hash($card->{flags}, $c->{flags});
	    add2hash($card, $c);
	},
	ASK => sub { $card->{ask} = $val },
	SERVER => sub { $card->{server} = $val },
	DRIVER => sub { $card->{driver} = $val },
	UNSUPPORTED => sub { $card->{flags}{unsupported} = 1 },
	COMMENT => sub {},
    };

    local $_;
    while (<$F>) { $lineno++;
	s/\s+$//;
	/^#/ and next;
	/^$/ and next;
	/^END/ and do { $cards{$card->{type}} = $card if $card; last };
	($cmd, $val) = /(\S+)\s*(.*)/ or next; #log::l("bad line $lineno ($_)"), next;
	my $f = $fs->{$cmd};
	$f ? $f->() : log::l("unknown line $lineno ($_)");
    }
    \%cards;
}

sub updateScannerDBfromUsbtable {
    substInFile { s/END// } "ScannerDB";
    local *F;
    open F, ">>ScannerDB" or die "can't write ScannerDB config in ScannerDB: $!";
    print F "# generated from usbtable by scannerdrake\n";
    foreach (cat_("$ENV{SHARE_PATH}/ldetect-lst/usbtable")) {
	my ($vendor_id, $product_id, $mod, $name) = chomp_(split /\s/,$_,4);
	next if $mod ne '"scanner"';
	$name =~ s/\"(.*)\"$/$1/;
	if (member($name, keys %$scanner::scannerDB)) {
	    print "#[$name] already in ScannerDB!\n";
	    next;
	}
	print F "NAME $name\nDRIVER usb\nCOMMENT usb $vendor_id $product_id\nUNSUPPORTED\n\n";
    }
    print F "END\n";
    close F;
}

sub updateScannerDBfromSane {
    my ($_sanesrcdir) = @_;
    substInFile { s/END// } "ScannerDB";

    local *Y;
    open Y, ">>ScannerDB" or die "can't write ScannerDB config in ScannerDB: $!";
    print Y "# generated from Sane by scannerdrake\n";
    # for compat with our usbtable
    my $sane2DB = { 
		   "Acer" => "Acer Peripherals Inc.",
		   "AGFA" => "AGFA-Gevaert NV",
		   "Agfa" => "AGFA-Gevaert NV",
		   "Epson" => "Epson Corp.",
		   "Fujitsu Computer Products of America" => "Fujitsu",
		   "HP" => sub { $_[0] =~ s/HP\s/Hewlett-Packard|/; $_[0] =~ s/HP4200/Hewlett-Packard|ScanJet 4200C/; $_[0] },
		   "Hewlett-Packard" => sub { $_[0] =~ s/HP 3200 C/Hewlett-Packard|ScanJet 3200C/; $_[0] },
		   "Kodak" => "Kodak Co.",
		   "Mustek" => "Mustek Systems Inc.",
		   "NEC" => "NEC Systems",
		   "Nikon" => "Nikon Corp.",
		   "Plustek" => "Plustek, Inc.",
		   "Primax" => "Primax Electronics",
		   "Siemens" => "Siemens Information and Communication Products",
		   "Trust" => "Trust Technologies",
		   "UMAX" => "Umax",
		   "Vobis/Highscreen" => "Vobis",
		  };
    
    opendir YREP, $_sanesrcdir or die "can't open $_sanesrcdir: $!";
    @files = grep /.*desc$/, readdir YREP;
    closedir YREP;
    foreach my $i (@files) {
	my $F = common::openFileMaybeCompressed("$_sanesrcdir/$i");
	print Y "\n# from $i";
	my ($lineno, $cmd, $val) = 0;
	my ($name, $intf, $comment,$mfg);
	my $fs = {
		  backend => sub { $backend = $val },
		  mfg => sub { $mfg = $val; $name = undef },#bug when a new mfg comes. should called $fs->{ $name }(); but ??
		  model => sub {
		      unless ($name) { $name = $val; next }
		      $name = (member($mfg, keys %$sane2DB)) ?
			(ref $sane2DB->{ $mfg}) ? $sane2DB->{ $mfg}($name) : "$sane2DB->{ $mfg }|$name" : "$mfg|$name";
		      if (member($name, keys %$scanner::scannerDB)) {
			  print "#[$name] already in ScannerDB!\n";
		      } else {
			  print Y "\nNAME $name\nSERVER $backend\nDRIVER $intf\n";
			  print Y "COMMENT $comment\n" if $comment;
			  $comment = undef; 
		      }
		      $name = $val;
		  },
		  interface => sub { $intf = $val },
		  comment => sub { $comment = $val },
		 };
	local $_;
	while (<$F>) { $lineno++;
		       s/\s+$//;
		       /^\;/ and next;
		       ($cmd, $val) = /:(\S+)\s*\"([^\;]*)\"/ or next; #log::l("bad line $lineno ($_)"), next;
		       my $f = $fs->{ $cmd};
		       $f ? $f->() : log::l("unknown line $lineno ($_)");
		   }
	$fs->{model}(); # the last one
    }
    print Y "\nEND\n";
    close Y;
}

1; #