#! @RUBY@
class Detemplate
  def initialize()
    @debug = nil
    @templConfigIdent = "template types"
    @prefix = "SC_"
    # Config is a Hash whose keys are strings and values are vectors 
    @config = Hash.new
    @outputDir = ""
  end

# This creates a helpful .h file that includes all of the generated
# files for a section.  Sections are defined in the config file:
# detemplate.conf

  def createHeaderForSection(section)
    fname = @prefix+section+'.gen.h'
    file = open(fname,"w")
    if @config['template types'] == nil
      puts("No template types!")
      return
    end
    file.puts("// File generated by #{$0}")
    mName = @prefix+section+'_gen_h_seen'
    file.puts("#ifndef "+mName)
    file.puts("#define "+mName)
    file.puts("")
    @config[@templConfigIdent].each do |ttype|
      @config[section].each do |qType|
	file.puts(
		  "#include \"scutil/"+createTemplTypeFileName(ttype, qType)[0]+"\""
		  )
      end
    end
    file.puts("#endif // "+mName)
    file.puts("")
  end

# Create a Makefile include that define two symbols: GENSRC and GENHDR
# for the generated source and headers respectively.

  def createMakeincludeForAll(oFile)
    src = []
    hdr = []
    
    @config.each_key do |section|
      next if section == @templConfigIdent
      @config[@templConfigIdent].each do |tType|
	@config[section].each do |qType|
	  f = createTemplTypeFileName(tType, qType)
	  src.push(f[1])
	  hdr.push(f[0])
	end
      end
    end
    oFile.puts 'GENSRC = \\'
    src.each do |line|
      oFile.puts line+'\\'
    end
    oFile.puts
    oFile.puts
    oFile.puts 'GENHDR = \\'
    hdr.each do |line|
      oFile.puts line+'\\'
    end
    oFile.puts
    oFile.puts
  end

# Read and parse the configuration file.

  def readConfigFile(name)
    section = "nil"
    file = open(name, "r")
    file.each_line do |line|
      line.strip!
      next if line =~ /^\#/ # get rid of comments
      next if line =~ /^\s$/ # purge blank line
      next if line == ""
      puts("line="+line) if @debug
      if line =~ /\[(.*)\]/ # new section
	puts("1="+$1) if @debug
	section = $1
	@config[section] = Array.new
	puts("section = "+section+" config = "+@config[section].to_s) if @debug
      else
	puts("section = "+section+" line = "+line) if @debug
	@config[section].push(line)
      end
    end
    if @config[@templConfigIdent] == nil
      puts("Warning: No [template types] section");
    end
  end

# Create the standard suffix from a type

  def mangleSuffix(inType) 
    qType = String.new(inType)
    qType.gsub!(/::/, '_o_')
    qType.gsub!(/\s/, '_') # any amount of white space is converted to an '_'
    qType.gsub!(/\*/, 'p')
    qType = '_'+qType
    qType
  end

# Create the standard output file names in the form of a 2 dimensional
# array.

  def createTemplTypeFileName(inTemplType, inType) 
    qType = String.new(inType)
    templType = String.new(inTemplType)
    [templType+mangleSuffix(qType)+'.gen.h', templType+mangleSuffix(qType)+
      '.gen.cpp']
  end

  def mangleFilesForSection(section)
    if @debug 
      @config[@templConfigIdent].each do |tType|
	@config[section].each do |qType|
	  puts("tType = "+tType+" type = "+qType+" section = "+section)
	end
      end
    end
      @config[@templConfigIdent].each do |tType|
	@config[section].each do |qType|
	  mangleTemplateFile(tType, qType);
      end
    end
    if @debug 
      @config[@templConfigIdent].each do |tType|
	@config[section].each do |qType|
	  puts("last tType = "+tType+" qType = "+qType+" section = "+section)
	end
      end
    end
  end

# This mangles the source template file tType into a hardwired class
# using the type: qType.

  def mangleTemplateFile(tType, qType)
    dt = DetemplateFile.new

    params = [ [tType+".h", tType+".cpp"],
      'TObject',
      mangleSuffix(qType),
      qType,
      tType+"_seen",
      tType+mangleSuffix(qType)+"_seen",
      tType,
      createTemplTypeFileName(tType, qType)
      ]

    dt.setParams(params)
    dt.setOutputDirectory(@outputDir)
    dt.mangleFiles()
  end

# Mangle all of the template files with all of the types mentioned in
# the configuration file.

  def mangleAllFiles()
    # do all sections except the @templConfigIdent themselves.
    @config.each_key do |section|
      next if section == @templConfigIdent
      mangleFilesForSection(section)
    end
  end

  def createAllSectionHeaderFiles()
    @config.each_key do |section|
      next if section == @templConfigIdent
      createHeaderForSection(section)
    end
  end

    def setOutputDirectory(dir)
      @outputDir = dir
    end

end # Detemplate

class DetemplateFile
  def initialize()
    @debug = nil
    @outputDir = ""
  end
  def setParams(params)
    @inFile = params[0]
    @templateParam = params[1]
    @classSuffix = params[2]
    @templateParamValue = params[3]
    @ifdefMacro = params[4]
    @newdefMacro = params[5]
    @theClass = params[6]
    @outFile = params[7]
    if @debug
      puts("inFile = "+"["+@inFile.to_s+"]")
      puts("templateParam = "+@templateParam)
      puts("classSuffix = "+@classSuffix)
      puts("templateParamValue = "+@templateParamValue)
      puts("ifdefMacro = "+@ifdefMacro)
      puts("newdefMacro = "+@newdefMacro)
      puts("theClass = "+@theClass)
      puts("outFile = "+"["+@outFile.to_s+"]")
    end
    setupFileMangle()
  end

def setOutputDirectory(dir) 
  @outputDir = dir
end

  def setupFileMangle() 
# These are all the substitutions we do.  They are done in order.
    @vec = [
      [Regexp.new('^#include \"SCVectorInstances.*') , ""],
      [Regexp.new('^#define TBASECLASS.*') , ""],
      [Regexp.new('SCVector.h') , 'SCVector'+@classSuffix+'.gen.h'],
      [Regexp.new('SCVectEnum.h') , 'SCVectEnum'+@classSuffix+'.gen.h'],
      [Regexp.new('SCEnumeration.h') , 'SCEnumeration'+@classSuffix+'.gen.h'],
      [Regexp.new('Iterated.h') , 'Iterated'+@classSuffix+'.gen.h'],
      [Regexp.new(@theClass+'([^<,_])') , @theClass+@classSuffix+'\1'],
      [Regexp.new('<'+@templateParam+'>') , @classSuffix],
      [Regexp.new(@ifdefMacro) , @newdefMacro],
      [Regexp.new(@templateParam) , @templateParamValue],
      [Regexp.new('^#pragma.*') , ''],
      [Regexp.new('^template<.*') , '']
      # and so on ...
    ]
    @vec.each do |el|
      reg = el[0]
      subst = el[1]
      if @debug 
	puts("regexp = "+reg.source+" subst = "+subst)
      end
    end
  end

  # Perform all of the substitutions in iFile and output the result to
  # oFile.

  def transmute(iFile, oFile)
    # for each |key, value|
    match = nil
    iFile.each do |line|
      @vec.each do |el|
	reg = el[0]
	subst = el[1]
	if @debug 
	  puts("reg = "+reg.source)
	end
	if @debug && (match = reg =~ line)	
	  puts("Before: "+line)
	end
	line.gsub!(reg, subst)
        if @debug && match	
	  puts("After:  "+line)
	  puts("Substitution: "+subst+" for Regexp = "+reg.source)
	end
      end
      oFile.puts(line)
    end
  end

# This mangles both the header and C++ template/type pair into
# hardwired classes.

  def mangleFiles()
    for i in [0, 1]
      if @outputDir != ""
	out = @outputDir+"/"+@outFile[i]
      else
	out = @outFile[i]
      end
      inn = @inFile[i]
      iFile = open(inn,"r")
      oFile = open(out, "w")
      if @debug 
	puts("outfile:"+@outFile[i])
      end
      transmute(iFile, oFile)
    end
  end
end

# A test program

class Tester 
# Create the suffix from a type
  def mangleSuffix(inType) 
    qType = String.new(inType)
    qType.gsub!(/::/, '_o_')
    qType.gsub!(/\s/, '_') # any amount of white space is converted to an '_'
    qType.gsub!(/\*/, 'p')
    qType = '_'+qType
    qType
  end

  def createTemplTypeFileName(inTemplType, inType) 
    qType = String.new(inType)
    templType = String.new(inTemplType)
    [templType+mangleSuffix(qType)+'.gen.h', templType+mangleSuffix(qType)+
      '.gen.cpp']
  end

  def test0
    dt = DetemplateFile.new

    tType = 'SCVectEnum'
    qType = 'Gizzard::UPortStatus *'
    params = [ [tType+".h", tType+".cpp"],
      'TObject',
      mangleSuffix(qType),
      qType,
      tType+"_seen",
      tType+mangleSuffix(qType)+"_seen",
      tType,
      createTemplTypeFileName(tType, qType)
    ]

    dt.setParams(params)
    
    dt.transmute($stdin, $stdout)
  end
  def test1
    puts('ARGV = '+ARGV.to_s)
    dt = Detemplate.new()
    dt.readConfigFile("detemplate.config")
    dt.mangleFilesForSection("Gizzard_UPortStatus_p")
    dt.createHeaderForSection("Gizzard_UPortStatus_p")
  end
  def test2
    dt = Detemplate.new()
    dt.readConfigFile("detemplate.config")
    dt.mangleAllFiles()
    dt.createAllSectionHeaderFiles()
    mkIncl = open("gen.Makeinclude", "w")
    dt.createMakeincludeForAll(mkIncl)
  end
  def test3
    dt = Detemplate.new()
    dt.readConfigFile("detemplate.config")
    dt.createMakeincludeForAll($stdout)
  end
end

# This mangles all the files for all the types mentioned in detemplate.conf

usage = "#{$0} --outputDir <directory>"
@outputDir = ""
ARGV.each_index do |i|
  if "--outputDir" =~ ARGV[i]
    @outputDir = ARGV[i+1]
  end
end
dt = Detemplate.new()
if @outputDir != ""
  dt.setOutputDirectory(@outputDir)
end
dt.readConfigFile("detemplate.conf")
dt.mangleAllFiles()
dt.createAllSectionHeaderFiles()
mkIncl = open("gen.Makeinclude", "w")
dt.createMakeincludeForAll(mkIncl)
