# Copyright (C) 2005 Christopher Cyll
#
# Alexandria 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 of the
# License, or (at your option) any later version.
#
# Alexandria 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 Alexandria; see the file COPYING.  If not,
# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

require 'alexandria/scanners'

module Alexandria
  module Scanners

    class CueCat

      def name()
        return "CueCat"
      end

      # Checks if data looks like cuecat input
      def match?(data)
        data.chomp!
        fields = data.split('.')
        return false if fields.size != 4
        return false if fields[2].size != 4
        return true
      end

      # Decodes CueCat input into ISBN
      # The following code is adapted from Skip Rosebaugh's public
      # domain perl implementation.
      def decode(data)
        data.chomp!
        fields = data.split('.')
        fields.shift # First part is gibberish
        fields.shift # Second part is cuecat serial number
        type, code = fields.map {|field| decode_field(field) }

        if type == 'IB5'
          type = 'IBN'
          code = code[0, 13]
        end
        
        return code if type == 'IBN'
                      
        raise "Don't know how to handle type #{type} (barcode: #{code})"
      end

      private
        
      def decode_field (encoded)
        seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
      
        chars   = encoded.split(//)
        values  = chars.map {|c| seq.index(c) }
        
        padding = pad(values)
        result  = calc(values)
        result  = result[0, result.length - padding]
        return result
      end
    
      def calc (values)
        result = ''
        while values.length > 0
          num = ((values[0] << 6 | values[1]) << 6 | values[2]) << 6 | values[3]
          result += ((num >> 16) ^ 67).chr
          result += ((num >> 8 & 255) ^ 67).chr
          result += ((num & 255) ^ 67).chr
          
          values = values[4, values.length]
        end
        return result
      end
      
      def pad (array)
        length = array.length % 4
      
        if length != 0
          raise "Error parsing CueCat input" if length == 1
                                               
          length = 4 - length
          length.times { array.push(0) }
        end
      
        return length
      end
    end

    # Register a cuecat scanner with the Scanner Registry
    Registry.push(CueCat.new())

  end
end
