module BinData::Int

def bytes_per_word(nbits)

def bytes_per_word(nbits)
  (nbits % 64).zero? ? 8 :
  (nbits % 32).zero? ? 4 :
  (nbits % 16).zero? ? 2 :
                       1
end

def create_clamp_code(nbits, signed)

def create_clamp_code(nbits, signed)
  if signed == :signed
    max = (1 << (nbits - 1)) - 1
    min = -(max + 1)
  else
    max = (1 << nbits) - 1
    min = 0
  end
  "val = (val < #{min}) ? #{min} : (val > #{max}) ? #{max} : val"
end

def create_int2uint_code(nbits)

def create_int2uint_code(nbits)
  "val &= #{(1 << nbits) - 1}"
end

def create_read_assemble_code(nbits, endian, signed)

def create_read_assemble_code(nbits, endian, signed)
  bits_per_word = bytes_per_word(nbits) * 8
  nwords        = nbits / bits_per_word
  idx = (0 ... nwords).to_a
  idx.reverse! if (endian == :big)
  parts = (0 ... nwords).collect do |i|
            if i.zero?
              "ints.at(#{idx[i]})"
            else
              "(ints.at(#{idx[i]}) << #{bits_per_word * i})"
            end
          end
  assemble_str = parts.join(" + ")
end

def create_read_code(nbits, endian, signed)

def create_read_code(nbits, endian, signed)
  unpack_str   = create_read_unpack_code(nbits, endian, signed)
  assemble_str = create_read_assemble_code(nbits, endian, signed)
  read_str = "(#{unpack_str} ; #{assemble_str})"
  if need_conversion_code?(nbits, signed)
    "val = #{read_str} ; #{create_uint2int_code(nbits)}"
  else
    read_str
  end
end

def create_read_unpack_code(nbits, endian, signed)

def create_read_unpack_code(nbits, endian, signed)
  nbytes = nbits / 8
  "ints = io.readbytes(#{nbytes}).unpack('#{pack_directive(nbits, endian, signed)}')"
end

def create_to_binary_s_code(nbits, endian, signed)

def create_to_binary_s_code(nbits, endian, signed)
  # special case 8bit integers for speed
  return "(val & 0xff).chr" if nbits == 8
  bits_per_word = bytes_per_word(nbits) * 8
  nwords        = nbits / bits_per_word
  mask          = (1 << bits_per_word) - 1
  vals = (0 ... nwords).collect do |i|
           i.zero? ? "val" : "val >> #{bits_per_word * i}"
         end
  vals.reverse! if (endian == :big)
  array_str = "[" + vals.collect { |val| "#{val} & #{mask}" }.join(", ") + "]" # TODO: "& mask" is needed to work around jruby bug
  pack_str  = "#{array_str}.pack('#{pack_directive(nbits, endian, signed)}')"
  if need_conversion_code?(nbits, signed)
    "#{create_int2uint_code(nbits)} ; #{pack_str}"
  else
    pack_str
  end
end

def create_uint2int_code(nbits)

def create_uint2int_code(nbits)
  "(val >= #{1 << (nbits - 1)}) ? val - #{1 << nbits} : val"
end

def define_class(name, nbits, endian, signed)

def define_class(name, nbits, endian, signed)
  unless BinData.const_defined?(name)
    BinData.module_eval <<-END
      class #{name} < BinData::BasePrimitive
        Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
      end
    END
  end
  BinData.const_get(name)
end

def define_methods(int_class, nbits, endian, signed)

def define_methods(int_class, nbits, endian, signed)
  raise "nbits must be divisible by 8" unless (nbits % 8).zero?
  int_class.module_eval <<-END
    def assign(val)
      #{create_clamp_code(nbits, signed)}
      super(val)
    end
    def do_num_bytes
      #{nbits / 8}
    end
    #---------------
    private
    def sensible_default
      0
    end
    def value_to_binary_string(val)
      #{create_clamp_code(nbits, signed)}
      #{create_to_binary_s_code(nbits, endian, signed)}
    end
    def read_and_return_value(io)
      #{create_read_code(nbits, endian, signed)}
    end
  END
end

def need_conversion_code?(nbits, signed)

def need_conversion_code?(nbits, signed)
  signed == :signed and not pack_directive_signed?(nbits, signed)
end

def pack_directive(nbits, endian, signed)

def pack_directive(nbits, endian, signed)
  bits_per_word = bytes_per_word(nbits) * 8
  nwords        = nbits / bits_per_word
  if (nbits % 64).zero?
    d = (endian == :big) ? 'Q>' : 'Q<'
  elsif (nbits % 32).zero?
    d = (endian == :big) ? 'L>' : 'L<'
  elsif (nbits % 16).zero?
    d = (endian == :big) ? 'S>' : 'S<'
  else
    d = 'C'
  end
  if pack_directive_signed?(nbits, signed)
    (d * nwords).downcase
  else
    d * nwords
  end
end

def pack_directive_signed?(nbits, signed)

def pack_directive_signed?(nbits, signed)
  signed == :signed and [64, 32, 16, 8].include?(nbits)
end