#!/usr/bin/env ruby

# Run it like this:
#
#   ./codegen/multi_join_generator.rb > src/main/scala/com/twitter/scalding/typed/MultiJoin.scala

$indent = "  "

TYPES = ('B'..'Z').to_a

def make_multi_joins(joinType, arity)
  meth_name = if joinType == "join" then "apply"
             elsif joinType == "leftJoin" then "left"
             elsif joinType == "outerJoin" then "outer"
             else raise "unknown join " + joinType
             end
  
  flatten_meth_name = if joinType == "join" then "flattenNestedTuple"
                     elsif joinType == "leftJoin" then "flattenNestedTuple"
                     elsif joinType == "outerJoin" then "flattenNestedOptionTuple"
                     else raise "unknown join " + joinType
                     end
  
  types = TYPES[0..(arity - 1)]
  flat_type = types.join(", ")
  flat_type_options = types.map {|x| "Option[#{x}]"}.join(", ")
  out_type = if joinType == "join" then flat_type else flat_type_options end

  method_decl = "#{$indent}def #{meth_name}[KEY, A, #{flat_type}](a: CoGroupable[KEY, A], "
  inputs = types.map { |t|
    "#{t.downcase}: CoGroupable[KEY, #{t}]"
  }.join(", ")
  
  value_type = if joinType == "outerJoin" then "Option[A]" else "A" end
  
  puts method_decl + inputs + "): CoGrouped[KEY, (#{value_type}, #{out_type})] ="

  puts "#{$indent*2}a.#{joinType}(b)"

  types[1..-1].each { |t|
    puts "#{$indent*3}.#{joinType}(#{t.downcase})"
  }

  if arity > 1 then
    puts "#{$indent*3}.mapValues { tup => #{flatten_meth_name}(tup) }"
  end
end

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

/**
 * This is an autogenerated object which gives you easy access to
 * doing N-way joins so the types are cleaner. However, it just calls
 * the underlying methods on CoGroupable and flattens the resulting tuple
 */
object MultiJoin extends java.io.Serializable {
  import com.twitter.scalding.typed.FlattenGroup._

|

(1..21).each { |a|
  make_multi_joins("join", a)
  puts
}

(1..21).each { |a|
  make_multi_joins("leftJoin", a)
  puts
}

(1..21).each { |a|
  make_multi_joins("outerJoin", a)
  puts
}


puts "}"

puts "// end of autogenerated"
