#!/usr/bin/env ruby

$indent = "  "

def make_tuple_conv(cnt)
  type_names = ('A'..'Z').to_a[0...cnt]
  indices = (0...cnt).to_a
  comma_tn = type_names.join(",")
  getters = type_names.map { |n|
              "g#{n}: TupleGetter[#{n}]"
            }.join(",\n#{$indent}")
  gvalues = type_names.map { |n| "g#{n}" }.join(", ")
  typed_args = type_names.zip(indices).map { |n,ni|
                 "g#{n}.get(tup, #{ni})"
               }.join(",\n#{$indent}       ")

  %Q|\n#{$indent}case class TupleConverter#{cnt}[#{comma_tn}](#{getters}) extends TupleConverter[Tuple#{cnt}[#{comma_tn}]] {
#{$indent}    def apply(te : TupleEntry) = {
#{$indent}      val tup = te.getTuple
#{$indent}      Tuple#{cnt}(#{typed_args})
#{$indent}    }
#{$indent}    def arity = #{cnt}
#{$indent}}
#{$indent}implicit def tuple#{cnt}Converter[#{comma_tn}](implicit
#{$indent}#{getters}): TupleConverter[Tuple#{cnt}[#{comma_tn}]] = TupleConverter#{cnt}(#{gvalues})
|
end

def make_setter(cnt)
  underscores = (["_"]*cnt).join(",")
  type_names = ('A'..'Y').to_a[0...cnt]
  comma_tn = type_names.join(",")
  head = %Q|\n#{$indent}case class TupleSetter#{cnt}[Z <: Tuple#{cnt}[#{underscores}]]() extends TupleSetter[Z] {
#{$indent}  override def apply(arg: Z) = {
#{$indent}    val tup = Tuple.size(#{cnt})
#{$indent}    |
  middle = (1..cnt).map {|c| "tup.set(#{c-1}, arg._#{c})" }.join("\n#{$indent}    ")
  tail = %Q|
#{$indent}    tup
#{$indent}  }
#{$indent}  override def arity = #{cnt}
#{$indent}}
#{$indent}implicit def tup#{cnt}Setter[Z <: Tuple#{cnt}[#{underscores}]]: TupleSetter[Z] = TupleSetter#{cnt}[Z]()|
  head + middle + tail
end

# case TupleSetterN() => Some(obj.TupleConverterN(TupleGetter.Casting(), ...))
def make_converter_from_setter_case(cnt, obj)
  i3 = $indent * 3
  i4 = $indent * 4
  casting = "TupleGetter.Casting()"
  casting_args = (1..cnt).map { |c| casting }.join(",\n#{i4}")

  "#{i3}case TupleSetter#{cnt}() => Some(#{obj}.TupleConverter#{cnt}(#{casting_args}))"  
end

# def converterFromSetter[A](ts: TupleSetter[A], gtc: GeneratedTupleConverter): Option[TupleConverter[A]] =#
#  (ts match {
#    ...
#    case _ => None
#  }).asInstanceOf[Option[TupleConverter[A]]]

def make_converter_from_setter
  ["#{$indent}def converterFromSetter[A](ts: TupleSetter[A], gtc: GeneratedTupleConverters): Option[TupleConverter[A]] =",
   "#{$indent}#{$indent}(ts match {",
   (1..22).map { |c| make_converter_from_setter_case(c, "gtc") }.to_a,
   "#{$indent}#{$indent}#{$indent}case _ => None",
   "#{$indent}}).asInstanceOf[Option[TupleConverter[A]]]"].flatten.join("\n")
end

puts "// following were autogenerated by #{__FILE__} at #{Time.now} do not edit"
puts %q|package com.twitter.scalding
import cascading.tuple.Tuple
import cascading.tuple.TupleEntry

trait GeneratedTupleConverters extends LowPriorityTupleConverters {
|

(1..22).each { |c| puts make_tuple_conv(c) }
puts "}"
puts "trait GeneratedTupleSetters extends LowPriorityTupleSetters {"
(1..22).each { |c| puts make_setter(c) }

puts ""
puts make_converter_from_setter
puts ""

puts "}"
puts "// end of autogenerated"
