#!/usr/bin/env ruby

while (index = ARGV.index('-I'))
  _, path = ARGV.slice!(index, 2)
  $LOAD_PATH << path
end

require 'optparse'

require 'agoo'

usage = %{
Usage: agoo_stubs [options] <graphql_schema>

version #{Agoo::VERSION}

Generates Ruby files that match the types in the GraphQL schema file specified.

Example:

  agoo_stubs --module Data --dir lib schema.graphql

}

@verbose = false
@single = nil
@dir = nil
@mod = nil

@opts = OptionParser.new(usage)
@opts.on('-h', '--help', 'show this display')                            { puts @opts.help; Process.exit!(0) }
@opts.on('-v', '--verbose', 'verbose output')                            { @verbose = true }
@opts.on('-s', '--single PATH', String, 'path to file for all stubs')    { |s| @single = s }
@opts.on('-d', '--dir PATH', String, 'directory to write files into')    { |d| @dir = d }
@opts.on('-m', '--module STRING', String, 'module for the stub classes') { |m| @mod = m }

@files = @opts.parse(ARGV)

if @files.empty?
  puts "A schema file must be specified."
  puts @opts.help
  Process.exit!(0)
elsif 1 < @files.size
  puts "Only one schema file can be specified."
  puts @opts.help
  Process.exit!(0)
end

Agoo::GraphQL.schema(nil) {
  Agoo::GraphQL.load_file(@files[0])
}

def write_type_stub(f, t, depth)
  indent = '  ' * depth
  f.puts "#{indent}# #{t.description}" unless t.description.nil?
  f.puts "#{indent}class #{t.name}"
  t.fields.each { |field|
    next unless field.args.nil?
    f.puts "#{indent}  # #{field.description}" unless field.description.nil?
    f.puts "#{indent}  attr_accessor :#{field.name}"
  }
  f.puts
  f.puts "#{indent}  def initialize"
  f.puts "#{indent}  end"
  t.fields.each { |field|
    next if field.args.nil?
    f.puts
    f.puts "#{indent}  # #{field.description}" unless field.description.nil?
    f.puts "#{indent}  # args: #{field.args.map { |a| a.name }.join(', ')}"
    f.puts "#{indent}  def #{field.name}(args={})"
    f.puts "#{indent}  end"
  }
  f.puts "#{indent}end"
end

def calc_filepath(t)
  path = @dir
  path += '/' + @mod.split('::').map { |m| m.downcase }.join('/') unless @mod.nil?
  path + '/' + t.name.downcase + '.rb'
end

def assure_parent_dir(path)
  dirs = path.split('/')[0..-2]
  dirs.each_index { |i|
    path = dirs[0..i].join('/')
    Dir.mkdir(path) unless Dir.exist?(path)
  }
end

def write_type_stub_file(t)
  path = calc_filepath(t)
  assure_parent_dir(path)
  File.open(path, 'w') { |f|
    f.puts "# #{t.name} class for schema file #{@files[0]}"
    f.puts
    depth = 0
    unless @mod.nil?
      @mod.split('::').each { |m|
	f.puts("#{'  ' * depth}module #{m}")
	depth += 1
      }
      f.puts
    end
    write_type_stub(f, t, depth)
    f.puts
    unless @mod.nil?
      @mod.split('::').size.times {
	depth -= 1
	f.puts("#{'  ' * depth}end")
      }
    end
  }
end

@out = nil

if @dir.nil?
  if @single.nil?
    @out = STDOUT
  else @single
    @out = File.new(@single, 'w')
  end
end

unless @out.nil?
  @out.puts "# Classes for schema file #{@files[0]}"
  @out.puts
  depth = 0
  unless @mod.nil?
    @mod.split('::').each { |m|
      @out.puts("#{'  ' * depth}module #{m}")
      depth += 1
    }
    @out.puts
  end
  Agoo::GraphQL.sdl_types.sort { |a,b| a.name <=> b.name }.each { |t|
    write_type_stub(@out, t, depth)
    @out.puts
  }
  unless @mod.nil?
    @mod.split('::').size.times {
      depth -= 1
      @out.puts("#{'  ' * depth}end")
    }
  end
  @out.close unless STDOUT == @out
else
  Agoo::GraphQL.sdl_types.each { |t|
    write_type_stub_file(t)
  }
  unless @mod.nil?
    path = @dir
    path += '/' + @mod.split('::').map { |m| m.downcase }.join('/')
    assure_parent_dir(path)
    m = path.split('/')[-1]
    path += '.rb'

    File.open(path, 'w') { |f|
      f.puts "# Classes for schema file #{@files[0]}"
      f.puts
      Agoo::GraphQL.sdl_types.sort { |a,b| a.name <=> b.name }.each { |t|
	f.puts "require_relative '#{m}/#{t.name.downcase}'"
      }
    }
  end
end
