module Ethon::Curls::Options

def self.option(ftype,name,type,num,opts=nil)

def self.option(ftype,name,type,num,opts=nil)
  case type
  when :enum
    if opts.is_a? Array then
      opts=Hash[opts.each_with_index.to_a]
    elsif not opts.is_a? Hash then
      raise TypeError, "Ethon::Curls::Options #{ftype} #{name} Expected opts to be an Array or a Hash."
    end
  when :bitmask
    if opts.is_a? Array then
      if opts.last.is_a? Hash then
        hopts=opts.pop
      else
        hopts={}
      end
      opts.each_with_index do |v,i|
          next if v.nil?
          if i==0 then
            hopts[v]=0
          else
            hopts[v]=1<<(i-1)
          end
      end
      opts=hopts
    elsif not opts.is_a? Hash then
      raise TypeError, "Ethon::Curls::Options #{ftype} #{name} Expected opts to be an Array or a Hash."
    end
    opts[:all]=-1 unless opts.include? :all
    opts.each do |k,v|
      if v.is_a? Array then
        opts[k]=v.map { |b| opts[b] }.inject :|
      end
    end
  when :buffer
    raise TypeError, "Ethon::Curls::Options #{ftype} #{name} Expected opts to be an Array or a Hash." unless opts.is_a? Integer
  else
    raise ArgumentError, "Ethon::Curls::Options #{ftype} #{name} Expected no opts." unless opts.nil?
  end
  opthash=const_get(FOPTION_STRINGS[ftype])
  opthash[name] = { :type => type,
                    :opt => OPTION_TYPE_BASE[OPTION_TYPE_MAP[type]] + num,
                    :opts => opts }
end

def self.option_alias(ftype,name,*aliases)

def self.option_alias(ftype,name,*aliases)
  opthash=const_get(FOPTION_STRINGS[ftype])
  aliases.each { |a| opthash[a]=opthash[name] }
end

def self.option_type(type)

def self.option_type(type)
  cname = FOPTION_STRINGS[type]
  const_set(cname, {})
  define_method(OPTION_STRINGS[type]) do |rt|
    return Ethon::Curls::Options.const_get(cname).map { |k, v| [k, v[:opt]] } if rt == :enum
    Ethon::Curls::Options.const_get(cname)
  end
end

def set_option(option, value, handle, type = :easy)

Sets appropriate option for easy, depending on value type.
def set_option(option, value, handle, type = :easy)
  type = type.to_sym unless type.is_a?(Symbol)
  raise NameError, "Ethon::Curls::Options unknown type #{type}." unless respond_to?(OPTION_STRINGS[type])
  opthash=send(OPTION_STRINGS[type], nil)
  raise Errors::InvalidOption.new(option) unless opthash.include?(option)
  case opthash[option][:type]
  when :none
    return if value.nil?
    value=1
    va_type=:long
  when :int
    return if value.nil?
    va_type=:long
    value=value.to_i
  when :bool
    return if value.nil?
    va_type=:long
    value=(value&&value!=0) ? 1 : 0
  when :time
    return if value.nil?
    va_type=:long
    value=value.to_i
  when :enum
    return if value.nil?
    va_type=:long
    value = case value
    when Symbol
      opthash[option][:opts][value]
    when String
      opthash[option][:opts][value.to_sym]
    else
      value
    end.to_i
  when :bitmask
    return if value.nil?
    va_type=:long
    value = case value
    when Symbol
      opthash[option][:opts][value]
    when Array
      value.inject(0) { |res,v| res|opthash[option][:opts][v] }
    else
      value
    end.to_i
  when :string
    va_type=:string
    value=value.to_s unless value.nil?
  when :string_as_pointer
    va_type = :pointer
    s = ''
    s = value.to_s unless value.nil?
    value = FFI::MemoryPointer.new(:char, s.bytesize)
    value.put_bytes(0, s)
  when :string_escape_null
    va_type=:string
    value=Util.escape_zero_byte(value) unless value.nil?
  when :ffipointer
    va_type=:pointer
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? FFI::Pointer
  when :curl_slist
    va_type=:pointer
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? FFI::Pointer
  when :buffer
    raise NotImplementedError, "Ethon::Curls::Options option #{option} buffer type not implemented."
  when :dontuse_object
    raise NotImplementedError, "Ethon::Curls::Options option #{option} type not implemented."
  when :cbdata
    raise NotImplementedError, "Ethon::Curls::Options option #{option} callback data type not implemented. Use Ruby closures."
  when :callback
    va_type=:callback
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? Proc
  when :socket_callback
    va_type=:socket_callback
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? Proc
  when :timer_callback
    va_type=:timer_callback
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? Proc
  when :debug_callback
    va_type=:debug_callback
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? Proc
  when :progress_callback
    va_type=:progress_callback
    raise Errors::InvalidValue.new(option,value) unless value.nil? or value.is_a? Proc
  when :off_t
    return if value.nil?
    va_type=:int64
    value=value.to_i
  end
  if va_type==:long or va_type==:int64 then
      bits=FFI.type_size(va_type)*8
      tv=((value<0) ? value.abs-1 : value)
      raise Errors::InvalidValue.new(option,value) unless tv<(1<<bits)
  end
  send(FUNCS[type], handle, opthash[option][:opt], va_type, value)
end