#!/usr/bin/env ruby
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
require 'pathname'
require 'amqpgen'

#
# Run a set of code generation templates.
#
if ARGV.size < 3
  puts <<EOS
Usage: #{ARGV[0]} SRCDIR APIDIR SPEC.xml [ ... ] TEMPLATE.rb [ ... ]
or:    #{ARGV[0]} SRCDIR APIDIR SPEC.xml [ ... ] all [ makefragment.mk | makefragment.cmake ]

Parse all SPEC.xml files to create an AMQP model, run each TEMPLATE
putting the resulting files under SRCDIR, public API files in APIdir.
Prints a list of files generated to standard output.

If SRCDIR and APIDIR are '-'  then just prints file list without generating files.
EOS
  exit 1
end

# Create array of specs by version
def parse_specs(files)
  lists=Hash.new { |h,k| h[k]=[] }
  files.each { |f|
    spec=AmqpRoot.new(File.new(f))
    lists[spec.version] << spec
  }
  specs={}
  lists.each_pair { |k,l|
    specs[k] = l.size==1 ? l.first : AmqpRoot.new(*l.map { |s| s.xml})
  }
  return specs
end

gendir=File.dirname(__FILE__)

# Run selected templates
if ARGV.any? { |arg| arg=="all" }
  templates=Dir["#{gendir}/*/*.rb"]
else
templates=ARGV.grep(/\.rb$/)
  ARGV.each { |arg|
    d=File.join gendir,arg
    templates += Dir["#{d}/*.rb"] if File.directory? d
  }
end

$outdir=[ ARGV[0], ARGV[1] ]
$models=parse_specs(ARGV.grep(/\.xml$/))

templates.each { |t|
  ver=Pathname.new(t).dirname.basename.to_s.split('.')[-1]
  $amqp=$models[ver]
  if $amqp
    load t
  else
    puts "Warning: skipping #{t}, no spec file for version #{ver}."
  end
}

def cmake_continue(lines) lines.join(" \n    "); end
def make_continue(lines) lines.join(" \\\n    "); end

# Generate makefile
makefile=ARGV.grep(/.mk$/)[0]
cmakefile=ARGV.grep(/.cmake$/)[0]
if cmakefile || makefile
  srcdir,apidir=$outdir
  dir=Dir.getwd
  Dir.chdir File.dirname(__FILE__)
  generator_files=Dir["**/*.rb"] << File.basename(__FILE__)
  Dir.chdir dir
  rgen_generator=generator_files.map{ |f| "$(rgen_dir)/#{f}" }
  cmake_rgen_generator=generator_files.map{ |f| "${rgen_dir}/#{f}" }
  rgen_srcs=GenFiles.get.map{ |f| "#{GenFiles.public_api?(f) ? apidir : srcdir}/#{f}" }
  rgen_subdirs={}
  rgen_srcs.each { |src|
    if src.match(%r{(#{srcdir}|#{apidir})/qpid/([^/]+)/})
      subdir=$2
      rgen_subdirs[subdir] ||= []
      rgen_subdirs[subdir] << src
    end
  }
  if (makefile)
    File.open(makefile, 'w') { |out|
      out << <<EOS
# Generated makefile fragment.
# Including makefile defines $(rgen_dir) $(rgen_cmd) and $(specs).

rgen_generator=#{make_continue rgen_generator}
EOS
      rgen_subdirs.each_key { |subdir|
        out << "\nrgen_#{subdir}_srcs = #{make_continue(rgen_subdirs[subdir])}\n"
      }
      out << <<EOS
rgen_srcs=#{make_continue rgen_srcs}

# Header file install rules.
EOS
      ["amqp_0_10", "framing", "client/no_keyword","client", "broker"].each { |ns|
        dir="qpid/#{ns}"
        dir_ = dir.tr("/", "_")
        regex=%r|#{dir}/[^/]+\.h$|
        out << <<EOS
#{dir_}dir = $(includedir)/#{dir}
dist_#{dir_}_HEADERS = #{make_continue rgen_srcs.grep(regex)}

EOS
      } # each
    }   # File makefile
  end   # if (makefile)

  if (cmakefile)
    File.open(cmakefile, 'w') { |out|
      out << <<EOS
# Generated makefile fragment.
# Including makefile defines ${rgen_dir} ${rgen_cmd} and ${specs}.

set(rgen_generator #{cmake_continue cmake_rgen_generator})
EOS
      rgen_subdirs.each_key { |subdir|
        out << "\nset(rgen_#{subdir}_srcs #{cmake_continue(rgen_subdirs[subdir])})\n"
      }
      out << <<EOS
set(rgen_srcs #{cmake_continue rgen_srcs})

# Header file install rules.
EOS
      ["amqp_0_10", "framing", "client/no_keyword","client", "broker"].each { |ns|
        dir="qpid/#{ns}"
        dir_ = dir.tr("/", "_")
        regex=%r|#{dir}/[^/]+\.h$|
        out << <<EOS
set(#{dir_}dir  \${includedir}/#{dir})
set(dist_#{dir_}_HEADERS #{cmake_continue rgen_srcs.grep(regex)})

EOS
      } # each
    }   # File makefile
  end   # if (makefile)
end
