# prototype.rb
#   $Id: prototype.rb,v 1.5 2000/11/18 18:48:40 keiko Exp $

require "util"
require "variable"

class Prototype
  INDENT = " "*4

  def indent(n=1)
    INDENT*n
  end

  def initialize(type_or_module, name, args, rtn)
    @vtype = (type_or_module == :module ? nil : type_or_module)
    @name = name
    @args = args
    @rtnval = rtn
    @lenvar = []
    @rtnlenvar =[]

    # check output array size
    chk = @rtnval.find_all{|o| o.ary}
    chk += @args.find_all{|o| (o.ary && !o.output?)}
    chk.each{|o|
      size = []
      o.ary.split(",").each do |i|
        case i
        when "*"
          size.push "DFLT_SIZE"
          next
        when /^\d+$/
          size.push i
          next
        when /^(\d+):(\d+)$/
          size.push ($2.to_i - $1.to_i + 1)
          next
        end

        wk = i.gsub(/[a-z]+[a-z0-9]*/){|j|
          sz = @args.find{|x| x.name == j}
          if sz
            sz.localvariable[1]
          else
            "DFLT_SIZE"
          end
        }

        case wk
        when /(.+):(.+)/
          ed = ($2 == "*" ? "DFLT_SIZE" : $2)
          size.push ( "(" + (ed + "-" + $1 + "+1").
                     gsub("--", "+").gsub("-0","").
                     gsub("[-]1[+]1", "").gsub("[+]1[+]1", "+2") +
                     ")" )
        else
          size.push wk
        end
      end
      o.setarysize(size)
      p ["Prototype:1", @name, o.ary, size, o.arysize] if $DEBUG
    }

    # set @lenvar
    @args.each{|i|
      j = i.ftnlen
      @lenvar.push j if j
    }

    # set @rtnlenvar
    if @vtype
      @rtnval.each{|i|
        j = i.ftnlen
        @rtnlenvar.push j if j
      }
    end

  end

  def to_s
    buf = head
    defvar = defvar()
    buf << defvar
    buf << initialization
    buf << call
    buf << result
    buf << tail
  end

  def head
    vstr = @args.find_all{|i| i.input?}.
      collect{|i| i.name}.unshift("obj").join(", ")
    buf = "static VALUE\n"
    buf << format("dcl_%s(%s)\n", @name, vstr)
    buf << format("%sVALUE %s;\n{\n", indent, vstr)
  end

  def defvar
    buf = ""
    vars = @args + (@vtype ? @rtnval : [])
    vars.each{|i|
      t, n = i.localvariable
      if /\*$/ =~ t
        buf << format("%s%s%s;\n", indent, t, n)
      else
        buf << format("%s%s %s;\n", indent, t, n)
      end
    }

    @rtnval.each{|i|
      buf << format("%s%s %s;\n", indent, "VALUE", i.name) if !i.input?
    }
    buf << "\n" unless buf.empty?
    buf
  end

  def initialization
    buf = ""
    @args.find_all{|i| i.input?}.each{|i|
      j = i.checktype
      buf << j.gsub(/^/, indent) if j
    }
    buf << "\n" unless buf.empty?

    @args.find_all{|i| i.input? && !i.ary}.each{|i|
      j = i.initialization
      buf << j.gsub(/^/, indent) if j
    }
    @args.find_all{|i| i.input? && i.ary}.each{|i|
      j = i.initialization
      buf << j.gsub(/^/, indent) if j
    }
    buf << "\n" unless buf.empty?

    wk = @rtnval.find_all{|i| !i.input?} +
         @args.find_all{|i| i.work?}
    wk.each{|i|
      j = i.allocworkingarea
      buf << j.gsub(/^/, indent) if j
    }
    buf << "\n" unless buf.empty?

    buf
  end

  def call
    buf = ""
    buf << indent
    if @vtype
      if ( @vtype =~ /CHARACTER|COMPLEX/)
        wk = [@rtnval[0].arg_name] + @rtnlenvar +
	  @args.collect{|i| i.arg_name} + @lenvar
      else
        buf << (@rtnval[0].prefix + @rtnval[0].name + " = ")
        wk = @args.collect{|i| i.arg_name} + @lenvar
      end
    else
      wk = @args.collect{|i| i.arg_name} + @lenvar
    end
    buf << format("%s_(%s);\n", @name, wk.join(", "))
    buf << "\n" unless buf.empty?
    buf
  end

  def result
    buf = ""
    if @vtype
      buf << indent + @rtnval[0].getresult
    else
      @rtnval.each{|i|
        buf << (indent + i.getresult)
      }
    end
    buf << "\n" unless buf.empty?

    # free area allocated at initialization
    @args.find_all{|i| i.input?}.each{|i|
      j = i.freecary
      buf << j.gsub(/^/, indent) if j
    }

    # free area allocated at allocworkingarea
    wk = @rtnval.find_all{|i| !i.input?} +
         @args.find_all{|i| i.work?}
    wk.each{|i|
      j = i.freeworkingarea
      buf << j.gsub(/^/, indent) if j
    }
    buf << "\n" unless buf.empty?

    if (@vtype || (@rtnval.size == 1))
      buf << indent +
             format("return %s;\n", @rtnval[0].name)
    elsif ( @rtnval.size == 0 )
      buf << indent + "return Qnil;\n"
    else
      vars = @rtnval.collect{|i| i.name}
      buf << indent +
             format("return rb_ary_new3(%d, %s);\n",
                    vars.size, vars.join(", "))
    end
    buf << "\n" unless buf.empty?
    buf
  end

  def tail
    "}\n"
  end
end
