require "numru/gphys"
require "numru/dcl"

module NumRu
   module GGraph

#      COMMON_OPTIONS =    # To be aliased:
#	 ["overplot",     # "no_new_fig"
#	  "keep_frame"]   # "no_new_frame"

      class Open
	 def initialize(iws=1)
	    @iws = iws
	    start(@iws)
	 end
	 def exec
	    start(@iws)
	 end
	 def iws=(iws)
	    @iws = iws
	 end
	 def start(iws)
	    DCL.gropn(iws)
	 end
	 class << self
	    alias open new
	 end
      end

      class Close
	 def initialize
	    exec
	 end
	 def exec
	    DCL.grcls
	 end
	 class << self
	    alias close new
	 end
      end

      class Frame
	 # grfrm
	 def initialize
	    start
	 end
	 def start
	    DCL.grfrm
	 end
	 alias exec start
      end

      class Fig
	 # grfig & grvpt

	 @@default_viewport = [0.2, 0.8, 0.2, 0.8]

	 def initialize(*viewport)
	    if viewport.length==0
	      viewport = @@default_viewport.dup
	    end
	    @viewport = viewport
	    start(*@viewport)
	 end
	 def exec
	    start(*@viewport)
	 end
	 def start(*viewport)
	    DCL.grfig
	    if viewport.length==4
	       DCL.grsvpt(*viewport)
	    else
	       raise ArgumentError, "# of ags must be 4"
	    end
	 end
      end

      class Axis_Auto
	 # Use USPACK

	 SPECIAL_OPTIONS = ["window","viewport","overplot","keep_frame",
	                    "itr","title"]

	 def initialize(x, y, itr=1, options=nil, &pre_exec)  # ummm
	    @x = x
	    @y = y
            @itr = itr
	    @options = options.dup
	    @pre_exec = pre_exec
	    draw(@x, @y, @itr, options, &@pre_exec)
	 end
	 def exec
	    draw(@x, @y, @itr, @options.dup, &@pre_exec)
	 end
	 def draw(x, y, itr=1, options=nil, &pre_exec)
	    if (window=options.delete("window"))
	       DCL.grswnd(*window)
	    else
	       DCL.usspnt([x.min,x.max],[y.min,y.max])
	       DCL.uspfit
	    end
	    DCL.grstrn(itr)
	    DCL.grstrf
	    pre_exec.call if pre_exec
	    title = options.delete("title")
	    subtitle = options.delete("subtitle")
	    interpret_options(options)
	    DCL.usdaxs
	    DCL.uzrset('roffxt', DCL.uzrget('roffxt') + DCL.usrget('soffylt'))
#		     + (1+DCL.uzrget('pad1'))*DCL.uzrget('rsizel1')  )
	    DCL.uxmttl('t',title,0) if title && title.length > 0
	    DCL.uxsttl('b',subtitle,0) if subtitle && subtitle.length > 0
	 end
	 def self.gphys2D(gphys, options=nil, &pre_exec)
	    raise ArgumentError,"not a GPhys" if ! gphys.is_a?(GPhys)
	    raise ArgumentError,"rank < 2" if gphys.rank < 2
	    name = gphys.data.get_att("long_name") || gphys.data.name
	    subttl = gphys.lost_axes.join(';')
	    xpos = gphys.axis(0).pos
	    ypos = gphys.axis(1).pos
	    x = xpos.val
	    y = ypos.val
	    xname = xpos.get_att("long_name") || xpos.name
	    yname = ypos.get_att("long_name") || ypos.name
	    gobjs = Array.new
	    options = Hash.new if ! options
	    options["title"] = name if !options["title"]
	    options["subtitle"] = subttl if !options["subtitle"] && subttl !=""
	    options["cxttl"] = xname if !options["cxttl"]
	    options["cyttl"] = yname if !options["cyttl"]
	    options["cxunit"] = xpos.get_att("units") if !options["cxunit"]
	    options["cxunit"].gsub!('_',' ') if options["cxunit"]
	    options["cyunit"] = ypos.get_att("units") if !options["cyunit"]
	    options["cyunit"].gsub!('_',' ') if options["cyunit"]
	    if ! options.delete("overplot")
	       gobjs.push( Frame.new ) if ! options.delete("keep_frame")
	       if (viewport=options.delete("viewport"))
		  gobjs.push( Fig.new(*viewport) )
	       else
		  gobjs.push( Fig.new )
	       end
	    end
	    itr = options.delete("overplot") || 1
	    gobjs.push( self.new(x, y, itr, options, &pre_exec ) )
	    gobjs
	 end

	 ######
	 private
	 def interpret_options(opts)
	    return if ! opts
	    opts.each{ |name, val|
	       case name
	       when /^l/i
		  DCL.uslstx(name,val)
	       when /^c/i
		  DCL.uscstx(name,val)
	       when /^[i-n]/i
		  DCL.usistx(name,val)
	       else
		  DCL.usrstx(name,val)
	       end
	    }
	 end
      end

      Axis_US = Axis_Auto    # alias

      class Contour
	 SPECIAL_OPTIONS = ["crange","cint","clevels","overplot","keep_frame"]
	 def initialize(nary, options=nil, &pre_exec)
	    @nary = nary
	    @options = options.dup
	    @pre_exec = pre_exec
	    draw(@nary, options, &@pre_exec)
	 end
	 def exec
	    draw(@nary, @options.dup, &@pre_exec)
	 end
	 def draw(nary, options=nil, &pre_exec)
	    pre_exec.call if pre_exec
	    interpret_options(options)
	    DCL.udcntz(nary)
	 end
	 def Contour.gphys(gphys, options=nil, axis_options=nil, &pre_exec)
	    raise ArgumentError,"not a GPhys" if ! gphys.is_a?(GPhys)
	    raise ArgumentError,"rank < 2" if gphys.rank < 2
	    gobjs = Array.new
	    options = Hash.new if ! options
	    if ! options.delete("overplot")
	       if options.delete("keep_frame")
		  axis_options["keep_frame"] = true
	       end
	       gobjs.push( Axis_Auto.gphys2D(gphys, axis_options) )
	    end
	    rubber = [] ; (gphys.rank-2).times{ rubber.push(0) }
	    v = gphys.data[true,true,*rubber].val
	    gobjs.push( self.new(v, options, &pre_exec) )
	    gobjs
	 end
	 ######
	 private
	 def interpret_options(opts)

	    return if ! opts

	    if (clevels=opts.delete("clevels"))
	       opts.delete("crange")  # ignored
	       opts.delete("cint")    # ignored
               raise "Sorry, yet to be implemented"
	       # for i in 0...clevels.length; DCL.udsclv(....) ...
	    elsif (crange = opts.delete("crange"))   # substitution, not "=="
               case crange
	       when Range
		  cmin = crange.first
		  cmax = crange.last
	       when Array
		  cmin = crange[0]
		  cmax = crange[1]
	       else
		  raise 'option "crange" must be a Range (cmin..cmax) or an Array ([cmin,cmax])'
	       end
	       cint = opts.delete("cint")
	       if !cint
		  nlev = ( opts["nlev"] || DCL.udpget("nlev") )
		  cint = -nlev
		  DCL.udgcla(cmin,cmax,cint)
	       end
	    end
	    opts.each{ |name, val|
	       case name
	       when /^l/i
		  DCL.udlstx(name,val)
	       when /^[i-n]/i
		  DCL.udistx(name,val)
	       else
		  DCL.udrstx(name,val)
	       end
	    }
	 end
      end

      ### < module functions > ###
      module_function

      def contour(*args)
	 Contour.gphys(*args)
      end
      def open(*args)
	 Open.open(*args)
      end
      def close
	 Close.close
      end

   end
end

#######################################################

if $0 == __FILE__
   include NumRu

   p "#######################"

   file = NetCDF.open("../../testdata/T.jan.nc")
   temp = GPhys::NetCDF_IO.open(file,"T")

   DCL.uzfact(0.6)
   gobjs = [
   GGraph.open(1)  ,
   GGraph.contour(temp.average(0))  ,
   GGraph.contour(temp[true,true,5])  ,
   GGraph.close  ]
   gobjs.flatten!
   #gobjs.each{|i| p i.class}
   #gobjs.each{|i| i.exec}
end
