#! ruby -Ks
#-- array_to_dataset.rb
#-- array_to_dataset.pi
#
# v.0.0.5 2004-09-22 *options -> options={}, require "rdb" -> require "rdb/rdb"
# v.0.0.4 2003-03-03 array_to_dataset,display_width
# v.0.0.3 2003-01-27 size_mini=14
# v.0.0.2 2003-01-21 Symbol=>FT_STRING, ret[index] = item.to_s, rescue
# v.0.0.1 2003-01-21 [ap-ext:0101] array_to_client_data_set

require "phi"
begin
  require "rdb/rdb"
rescue LoadError
  require "rdb"
end

module RDB
  def self.class_to_field_type(klass)
    { String => RDB::FT_STRING,
      Fixnum => RDB::FT_INTEGER,
      Bignum => RDB::FT_INTEGER,
      Float  => RDB::FT_FLOAT,
      Symbol => RDB::FT_STRING,
      Phi::DateTime => RDB::FT_DATE_TIME,  ## 2003-03-13(–Ø)
      NilClass => RDB::FT_STRING,
      Array  => RDB::FT_STRING,
      Struct  => RDB::FT_STRING,
      Struct::SearchRec => RDB::FT_STRING,
    }[klass] || klass
  end
end

def array_of_array_to_dataset(array,options={})
  names  = options.delete(:names){ [] } # (0)
  types  = options.delete(:types){ [] } # (1)
  sizes  = options.delete(:sizes){ [] } # (2)
  labels = options.delete(:labels){ [] }
  size_mini = options.delete(:size_mini){ 14 }
  a = array[0]
  array.each{|aa|
    aa.each_with_index{|item,index|
      sizes[index] = [ sizes[index] || size_mini, item.to_s.size].max
      names[index] ||= "field_#{index}"
      types[index] ||= item.class
    }
  }
  types = types.collect{|k| RDB.class_to_field_type(k) }
  ret = RDB::ClientDataSet.new
  sizes.each_with_index{ |s,i|
    begin
      if types[i] == RDB::FT_STRING
        ret.field_defs.add(names[i].to_s, types[i], sizes[i])
      else
        ret.field_defs.add(names[i].to_s, types[i])
      end
    rescue
      $!.message << "\nfail to add field_def: name='#{names[i]}', type='#{types[i]}'\n"
      raise
    end
  }
  begin
    ret.create_data_set
  rescue
    p :names,names
    raise
  end
  if labels != []
    ret.fields.each_with_index{|f,i|
      f.display_label = labels[i].to_s if labels[i]
    }
  end
  if sizes != []
    ret.fields.each_with_index{|f,i|
      f.display_width = sizes[i] if sizes[i]
    }
  end
  array.each{|a|
    ret.append
    a.each_with_index{|item,index|
      begin
        case item
        when Array
          ret[index] = item.inspect
        when Phi::DateTime
          ret[index] = item
        when Integer
          ret[index] = item
        else
          ret[index] = item.to_s
        end
      rescue
        $!.message << "\nfail to append field: name='#{names[index]}', data='#{item.to_s}'\n"
        raise
      end
    }
    ret.post
  }
  return ret
end

def array_of_hash_to_dataset( hashes, options={} )
  keys   = options[:keys]  ||= hashes[0].keys
  names  = options[:names] ||= keys
  array  = hashes.collect{|h| h.values_at(*keys) }
  array_of_array_to_dataset( array, options)
end

def array_of_struct_to_dataset( structs, options={} )
  keys   = options[:keys]  ||= structs[0].members
  names  = options[:names] ||= keys
  array  = structs.collect{|h| keys.collect{|k| h[k] rescue nil} }
  array_of_array_to_dataset( array, options)
end

##def hash_of_array_to_dataset( hashes , options={} )
##  array = hashes.sort.collect{|k,v|v}
##  array_of_array_to_dataset(array,options)
##end

##def hash_of_hash_to_dataset( hashes , options={} )
##  array = hashes.sort.collect{|k,v|v}
##  array_of_hash_to_dataset(array,options)
##end

def array_to_dataset(array,options={})
  array = array.sort.collect{|k,v|v} if array.is_a? Hash
  case array[0]
  when Struct ; return array_of_struct_to_dataset(array,options)
  when Hash   ; return array_of_hash_to_dataset(array,options)
  when Array  ; return array_of_array_to_dataset(array,options)
  else        ; raise "Array nor Hash .. #{array.inspect} .. #{array[0].class}"
  end
end

if __FILE__ == $0

  array_of_array = [
    [ 101 , 103 , 102 ],
    [ 301 , 303 , 302 ],
    [ 201 , 203 , 1111 ],
  ]
  p  "array_to_dataset(array_of_array).display"
  array_to_dataset(array_of_array).display
##       field_0        field_1        field_2
##-------------- -------------- --------------
##           101            103            102
##           301            303            302
##           201            203           1111

end

