# variable.rb - Attribute specific modules
# $Id: variable.rb,v 1.6 2000/11/28 04:38:40 keiko Exp $

require "util"
require "def"

module Input
  PREFIX = "i_"
  def input?; true; end
end

module Output
  PREFIX = "o_"
  def output?; true; end
end

module Working
  PREFIX = "w_"
  def work?; true; end
end

module InputOutput
  PREFIX = "io_"
  def input?; true; end
  def output?; true; end
end

def Variable(name, vtype, attr, ary, charlen)
  vt = vtype.capitalize
  at = case attr
       when "i"
	 "Input"
       when "o"
	 "Output"
       when "io"
	 "InputOutput"
       when "t"
	 "Working"
       end
  ar = ary ? "Array" : ""
  klass = eval(vt + at + ar + "Variable")
  klass.new(name, ary, charlen)
end

class Variable
  def initialize(name, ary, *arg)
    @name = name
    @ary = ary
    @arysize = nil
    @aryrank = nil
  end

  attr_reader :name;
  attr_reader :ary;
  attr_reader :aryrank;

  def prefix
    self.type::PREFIX
  end

  def vartype
    self.type::VARTYPE
  end

  def arg_name
    "&" + prefix + name
  end

  def localvariable
    [vartype, prefix+name]
  end

  def ftnlen
    nil
  end

  def checktype
    "/* checktype: not implemented for #{name} (#{type}) */\n"
  end

  def initialization
    "/* initialization: not implemented for #{name} (#{type}) */\n"
  end

  def allocworkingarea
    nil
  end

  def getresult
    "/* getresult: not implemented for #{name} (#{type}) */\n"
  end

  def freecary
    nil
  end

  def freeworkingarea
    nil
  end

  def input?;  false; end
  def output?; false; end
  def work?;   false; end

end

## Type specific modules

module DefaultType
  def basic_r2c(r, c)
    "/* not implemented */"
  end

  def basic_c2r(r, c)
    "/* not implemented */"
  end
end

module CharacterType
  include DefaultType

  VARTYPE = "char *"

  def initialize(name, ary, *arg)
    super
    if arg[0]
      @charlen = (arg[0] == "*") ? "DFLT_SIZE" : arg[0]
    else
      @charlen = "1"
    end
  end

  def basic_r2c(r, c)
    %Q$#{c} = STR2CSTR(#{r});\n$
  end

  def basic_r2c_copy(r, c)
    type = self.vartype.gsub(/\s+\*$/, "")
    %Q$#{c} = ALLOCA_N(#{type}, strlen(STR2CSTR(#{r}))+1);\n$ +
    %Q$strcpy(#{c}, STR2CSTR(#{r}));\n$
  end

  def basic_c2r(r, c)
    %Q$#{r} = rb_str_new2(#{c});\n$
  end
end

module IntegerType
  include DefaultType

  VARTYPE = "integer"

  def basic_r2c(r, c)
    %Q$#{c} = NUM2INT(#{r});\n$
  end

  def basic_c2r(r, c)
    %Q$#{r} = INT2NUM(#{c});\n$
  end
end

module RealType
  include DefaultType

  VARTYPE = "real"

  def basic_r2c(r, c)
    %Q$#{c} = (#{VARTYPE})NUM2DBL(#{r});\n$
  end

  def basic_c2r(r, c)
    %Q$#{r} = rb_float_new((double)#{c});\n$
  end
end

module ComplexType
  include DefaultType

  VARTYPE = "complex"
end

module LogicalType
  include DefaultType

  VARTYPE = "logical"

  def basic_r2c(r, c)
    %Q$#{c} = ((#{r} == Qnil)||(#{r} == Qfalse)) ? FALSE_ : TRUE_;\n$
  end

  def basic_c2r(r, c)
    %Q$#{r} = (#{c} == FALSE_) ? Qfalse : Qtrue;\n$
  end
end

## Basic Variables

class CharacterVariable < Variable
  include CharacterType

  def arg_name
    "" + prefix + name
  end

  def ftnlen
    if self.input?
      "(ftnlen)strlen(#{prefix+name})"
    else
      "(ftnlen)#{@charlen}"
    end
  end

  def checktype
    "" +
      %Q$if (TYPE(#{name}) != T_STRING) {\n$ +
      %Q$  #{name} = rb_funcall(#{name}, rb_intern("to_str"), 0);\n$ +
      %Q$}\n$
  end

  def initialization
    if self.input? && self.output?
      basic_r2c_copy(name, prefix+name)
    else
      basic_r2c(name, prefix+name)
    end
  end

  def allocworkingarea                  # kuro: +1 need ?
    type = vartype.gsub(/\s+\*$/, "")
    len = (@charlen + "+1").gsub(/^1[+]1/,"2")
    %Q$#{prefix+name}= ALLOCA_N(#{type}, (#{len}));\n$ +
    %Q$memset(#{prefix+name}, '\\0', #{len});\n$
  end

  def getresult
    basic_c2r(name, prefix+name)
  end

  def freeworkingarea
    nil
  end
end

class IntegerVariable < Variable
  include IntegerType

  def checktype
    "" +
      %Q$if ((TYPE(#{name}) != T_BIGNUM) || (TYPE(#{name}) != T_FIXNUM)) {\n$ +
      %Q$  #{name} = rb_funcall(#{name}, rb_intern("to_i"), 0);\n$ +
      %Q$}\n$
  end

  def initialization
    basic_r2c(name, prefix+name)
  end

  def getresult
    basic_c2r(name, prefix+name)
  end
end

class RealVariable < Variable
  include RealType

  def checktype
    "" +
      %Q$if (TYPE(#{name}) != T_FLOAT) {\n$ +
      %Q$  #{name} = rb_funcall(#{name}, rb_intern("to_f"), 0);\n$ +
      %Q$}\n$
  end

  def initialization
    basic_r2c(name, prefix+name)
  end

  def getresult
    basic_c2r(name, prefix+name)
  end
end

class ComplexVariable < Variable
  include ComplexType

end

class LogicalVariable < Variable
  include LogicalType

  def checktype
    nil
  end

  def initialization
    basic_r2c(name, prefix+name)
  end

  def getresult
    basic_c2r(name, prefix+name)
  end
end

### Character
class CharacterInputVariable < CharacterVariable
  include Input
end

class CharacterOutputVariable < CharacterVariable
  include Output
end

class CharacterInputOutputVariable < CharacterVariable
  include InputOutput
end

class CharacterWorkingVariable < CharacterVariable
  include Working
end

### Integer
class IntegerInputVariable < IntegerVariable
  include Input
end

class IntegerOutputVariable < IntegerVariable
  include Output
end

class IntegerInputOutputVariable < IntegerVariable
  include InputOutput
end

class IntegerWorkingVariable < IntegerVariable
  include Working
end

### Real
class RealInputVariable < RealVariable
  include Input
end

class RealOutputVariable < RealVariable
  include Output
end

class RealInputOutputVariable < RealVariable
  include InputOutput
end

class RealWorkingVariable < RealVariable
  include Working
end

### Complex
class ComplexInputVariable < ComplexVariable
  include Input
end

class ComplexOutputVariable < ComplexVariable
  include Output
end

class ComplexInputOutputVariable < ComplexVariable
  include InputOutput
end

class ComplexWorkingVariable < ComplexVariable
  include Working
end

### Logical
class LogicalInputVariable < LogicalVariable
  include Input
end

class LogicalOutputVariable < LogicalVariable
  include Output
end

class LogicalInputOutputVariable < LogicalVariable
  include InputOutput
end

class LogicalWorkingVariable < LogicalVariable
  include Working
end

## Array Variables

class ArrayVariable < Variable

  def setarysize(size)
    @arysize = size
    @aryrank = size.size
  end

  def arysize
    if (@aryrank == 1)
      if (@arysize[0].to_s !~ /\(/)
        "(" + @arysize[0].to_s + ")" 
      else
	@arysize[0].to_s
      end
    else
      "(" + @arysize.join("*").gsub(/\*1/, "") + ")"
    end
  end

  def aryshape
#    if (@aryrank == 1)
#      if (@arysize[0].to_s !~ /\(/)
#        "(" + @arysize[0].to_s + ")" 
#      else
#	@arysize[0].to_s
#      end
#    else
    "{"+ @arysize.join(", ").gsub(/\*1/, "")+"}"
#    end
  end

  def arg_name
    "" + prefix + name
  end

  def allocworkingarea
    type = vartype.gsub(/\s+\*$/, "")
    %Q$#{prefix+name}= ALLOCA_N(#{type}, #{arysize});\n$
  end

end

class CharacterArrayVariable < ArrayVariable
  include CharacterType

  def arysize
    "(" + (@arysize.join("*") + "*" + @charlen).gsub(/\*1/, "") + ")"
  end

  def ftnlen
    "(ftnlen)#{@charlen}"
  end

  def checktype
    "" +
      %Q$if (TYPE(#{name}) == T_STRING) {\n$ +
      %Q$  #{name} = rb_Array(#{name});\n$ +
      %Q$}\n$ +
      %Q$if (TYPE(#{name}) != T_ARRAY) {\n$ +
      %Q$  rb_raise(rb_eTypeError, "invalid type");\n$ +
      %Q$}\n$
  end

  def initialization
    %Q$#{prefix+name} = #{OBJ2CCHARARY}(#{name}, #{arysize}, #{@charlen});\n$
  end

  def allocworkingarea
    type = vartype.gsub(/\s+\*$/, "")
    %Q$#{prefix+name}= ALLOCA_N(#{type}, #{arysize});\n$ +
    %Q$memset(#{prefix+name}, '\\0', #{arysize});\n$
  end

  def getresult
    %Q$#{name} = #{CCHARARY2OBJ}(#{prefix+name}, #{arysize}, #{@charlen});\n$
  end

  def freecary
    %Q$#{FREECCHARARY}(#{prefix+name});\n$
  end

end

class IntegerArrayVariable < ArrayVariable
  include IntegerType

  VARTYPE += " *"

  def checktype
    "" +
      %Q$if ((TYPE(#{name}) == T_BIGNUM) || (TYPE(#{name}) == T_FIXNUM)) {\n$ +
      %Q$  #{name} = rb_Array(#{name});\n$ +
      %Q$}\n$ +
      %Q$/* if ((TYPE(#{name}) != T_ARRAY) && \n$ +
      %Q$       (rb_obj_is_kind_of(#{name}, cNArray) != Qtrue)) {\n$ +
      %Q$     rb_raise(rb_eTypeError, "invalid type");\n$ +
      %Q$   }  -- no check since obj2c*ary will do that */\n$
  end

  def initialization
    %Q$#{prefix+name} = #{OBJ2CINTEGERARY}(#{name});\n$
  end

  def getresult
    %Q${int array_shape[#{aryrank}] = #{aryshape};\n$ +
    %Q$     #{name} = #{CINTEGERARY2OBJ}(#{prefix+name}, #{arysize}, #{aryrank}, array_shape);\n$ +
    %Q$    }\n$
  end

  def freecary
    %Q$#{FREECINTEGERARY}(#{prefix+name});\n$
  end
end

class RealArrayVariable < ArrayVariable
  include RealType
  VARTYPE += " *"

  def checktype
    "" +
      %Q$if (TYPE(#{name}) == T_FLOAT) {\n$ +
      %Q$  #{name} = rb_Array(#{name});\n$ +
      %Q$}\n$ +
      %Q$/* if ((TYPE(#{name}) != T_ARRAY) && \n$ +
      %Q$       (rb_obj_is_kind_of(#{name}, cNArray) != Qtrue)) {\n$ +
      %Q$     rb_raise(rb_eTypeError, "invalid type");\n$ +
      %Q$   }  -- no check since obj2c*ary will do that */\n$
  end

  def initialization
    %Q$#{prefix+name} = #{OBJ2CREALARY}(#{name});\n$
  end

  def getresult
    %Q${int array_shape[#{aryrank}] = #{aryshape};\n$ +
    %Q$     #{name} = #{CREALARY2OBJ}(#{prefix+name}, #{arysize}, #{aryrank}, array_shape);\n$ +
    %Q$    }\n$
  end

  def freecary
    %Q$#{FREECREALARY}(#{prefix+name});\n$
  end
end

class ComplexArrayVariable < ArrayVariable
  include ComplexType

  VARTYPE += " *"

#  def checktype
#    "" +
#      %Q$if (TYPE(#{name}) == T_XXXXX) {\n$ +
#      %Q$  #{name} = rb_Array(#{name});\n$ +
#      %Q$}\n$ +
#      %Q$if (TYPE(#{name}) != T_ARRAY) {\n$ +
#      %Q$  rb_raise(rb_eTypeError, "invalid type");\n$ +
#      %Q$}\n$
#  end

#  def initialization
#    %Q$#{prefix+name} = #{OBJ2CCOMPLEXARY}(#{name});\n$
#  end

#  def getresult
#    %Q$#{name} = #{CCOMPLEXARY2OBJ}(#{prefix+name}, #{arysize}, "");\n$
#  end

#  def freecary
#    %Q$ #{FREECCOMPLEXARY}(#{prefix+name});\n$
#  end
end

class LogicalArrayVariable < ArrayVariable
  include LogicalType

  VARTYPE += " *"

  def checktype
    "" +
      %Q$#{name} = rb_Array(#{name});\n$
  end

  def initialization
    %Q$#{prefix+name} = #{OBJ2CLOGICALARY}(#{name});\n$
  end

  def getresult
    %Q${int array_shape[#{aryrank}] = #{aryshape};\n$ +
    %Q$     #{name} = #{CLOGICALARY2OBJ}(#{prefix+name}, #{arysize}, #{aryrank}, array_shape);\n$ +
    %Q$    }\n$
  end

  def freecary
    %Q$#{FREECLOGICALARY2}(#{prefix+name});\n$
  end
end

### Character
class CharacterInputArrayVariable < CharacterArrayVariable
  include Input
end

class CharacterOutputArrayVariable < CharacterArrayVariable
  include Output
end

class CharacterInputOutputArrayVariable < CharacterArrayVariable
  include InputOutput
end

class CharacterWorkingArrayVariable < CharacterArrayVariable
  include Working
end

### Integer
class IntegerInputArrayVariable < IntegerArrayVariable
  include Input
end

class IntegerOutputArrayVariable < IntegerArrayVariable
  include Output
end

class IntegerInputOutputArrayVariable < IntegerArrayVariable
  include InputOutput
end

class IntegerWorkingArrayVariable < IntegerArrayVariable
  include Working
end

### Real
class RealInputArrayVariable < RealArrayVariable
  include Input
end

class RealOutputArrayVariable < RealArrayVariable
  include Output
end

class RealInputOutputArrayVariable < RealArrayVariable
  include InputOutput
end

class RealWorkingArrayVariable < RealArrayVariable
  include Working
end

### Complex
class ComplexInputArrayVariable < ComplexArrayVariable
  include Input
end

class ComplexOutputArrayVariable < ComplexArrayVariable
  include Output
end

class ComplexInputOutputArrayVariable < ComplexArrayVariable
  include InputOutput
end

class ComplexWorkingArrayVariable < ComplexArrayVariable
  include Working
end

### Logical
class LogicalInputArrayVariable < LogicalArrayVariable
  include Input
end

class LogicalOutputArrayVariable < LogicalArrayVariable
  include Output
end

class LogicalInputOutputArrayVariable < LogicalArrayVariable
  include InputOutput
end

class LogicalWorkingArrayVariable < LogicalArrayVariable
  include Working
end

