#! /usr/local/bin/perl

# http://www.isbn.org/standards/home/isbn/international/hyphenation-instructions.asp
# http://en.wikipedia.org/wiki/International_Standard_Book_Number

if (! @ARGV) {
	@ARGV = (
		'0-306-40615-2',
		'978-0-306-40615-7',
		'1234567890',
		'0123456789',
		'01234567890',
		);
}

for $arg (@ARGV) {
	$arg =~ s/^978-//;
	$checkDigit = &checkDigit($arg);
	printf "%-20s ($checkDigit)	" . hyphenate($arg) . "\n", $arg;
}

sub hyphenate {
	local ($isbn) = (@_);
	$isbn =~ s/[ -]//g;                      # remove spaces and hyphens
	local ($groupid) = substr($isbn, 0, 1);  # get the first digit
	local ($hyloc);                          # hyphen location
	if ($groupid eq '0') {
		if ($hyloc = checkRange($isbn, 2, '00', '19')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 3, '200', '699')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 4, '7000', '8499')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 5, '85000', '89999')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 6, '900000', '949999')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 7, '9500000', '9999999')) {
			return insertHyphens($isbn, $hyloc);
		}
	} elsif ($groupid eq '1') {
		if ($hyloc = checkRange($isbn, 2, '00', '09')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 3, '100', '399')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 4, '4000', '5499')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 5, '55000', '86979')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 6, '869800', '998999')) {
			return insertHyphens($isbn, $hyloc);
		}
		if ($hyloc = checkRange($isbn, 7, '9990000', '9999999')) {
			return insertHyphens($isbn, $hyloc);
		}
	}
	return $isbn;
}

sub checkRange {
	local ($isbn, $len, $low, $high) = (@_);
	local ($start) = substr($isbn, 1, $len);
	# print "checkRange: start=$start low=$low high=$high\n";
	if ($start ge $low && $start le $high) {
		return $len+1;
	} else {
		return 0;
	}
}

sub insertHyphens {
	local ($isbn, $hyloc) = (@_);
	return substr($isbn, 0, 1) . '-'
		. substr($isbn, 1, $hyloc-1) . '-'
		. substr($isbn, $hyloc, 10-$hyloc-1) . '-'
		. substr($isbn, 9);
}

sub checkDigit {
	local ($isbn) = (@_);
	$isbn =~ s/[ -]//g;
	# print "$isbn\n",
	local (@isbn) = split(//, $isbn);
	local ($sum) = 0;
	local ($weight);
	local ($pos) = 0;
	local ($digit) = '?'; # will be returned
	if (@isbn == 10 || @isbn == 9) {
		for $pos (0 .. 8) {
			$weight = 10 - $pos;
			$sum += $isbn[$pos] * $weight;
			# print "	$pos	$isbn[$pos]	$weight	$sum\n";
		}
		$digit = 11 - $sum % 11;
		$digit = 'X' if $digit == 10;
	} elsif (@isbn == 13 || @isbn == 12) {
		for $pos (0 .. 11) {
			$weight = ($pos & 1) ? 3 : 1;
			$sum += $isbn[$pos] * $weight;
			# print "	$pos	$isbn[$pos]	$weight	$sum\n";
		}
		$digit = 10 - ($sum % 10);
		$digit = 0 if $digit == 10;
	}
	return $digit;
}

sub goodCheckDigit {
	local ($isbn) = (@_);
	local ($digit) = checkDigit($isbn);
	return $isbn =~ /$digit$/i; 
}
