=begin header
  draw.rb - a part of gave.rb
=end

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


module NumRu
  class VArray
    def mapping
      @mapping
    end
  end
  class SubsetMapping
    def maps
      @maps
    end
  end
  class VArrayComposite
    alias _initialize initialize
    alias :_slice []
    def initialize(*arg)
      _initialize(*arg)
      @mapping2 = SubsetMapping.new(shape_current,[])
    end
    def [](*arg)
      mapping = @mapping2.composite(SubsetMapping.new(shape_current, arg))
      obj = self._slice(*arg)
      obj.set_mapping(mapping) if obj.class==VArrayComposite
      obj
    end
    def mapping
      @mapping2
    end
    def set_mapping(map)
      @mapping2 = map
    end
  end
end


include NumRu


if DCL.gtk_version[0]==1 then
  require "gdk_imlib"
  def save_image_file(filename)
    geom = @pixmap.get_geometry
    mask = Gdk::Bitmap.new(@draw_area.window, geom[2], geom[3])
    imlib = Gtk::imlib.new(@pixmax, mask)
    imlib.save(filename)
  end
  module Gdk
    class Window
      def size; get_geometry[2..3]; end
      def draw_drawable(*arg); draw_pixmap(*arg); end
    end
    class Event
      BUTTON_PRESS_MASK = Gdk::BUTTON_PRESS_MASK
      BUTTON_MOTION_MASK = Gdk::BUTTON_MOTION_MASK
      BUTTON_RELEASE_MASK = Gdk::BUTTON_RELEASE_MASK
    end
  end
elsif DCL.gtk_version[0]==2 then
  def save_image_file(filename)
    pixbuf = Gdk::Pixbuf.from_drawable(nil, @pixmap, 0, 0, -1, -1)
    pixbuf.save(filename, "png")
  end
end


class Draw_data
  attr_accessor :gphys, :gphys2
  attr_accessor :cont, :tone
  attr_accessor :vector
  attr_accessor :transpose
  attr_accessor :type, :map_type
  attr_accessor :title
#  attr_accessor :missing_value
  attr_reader :axis_x, :axis_y
  def initialize
    @axis_x = Axis.new
    @axis_y = Axis.new
    clear
  end
  def clear_var
    @gphys = nil
    @gphys2 = nil
    @transpose = false
    @axis_x.clear
    @axis_y.clear
  end
  def clear
    clear_var
    @title = nil
    @type = LINE
    @map_type = nil
  end
  class Axis
    attr_accessor :min, :max
    attr_accessor :title, :unit
    attr_accessor :log
    def initialize
      clear
    end
    def clear
      @title=""
      @unit=""
      @log=false
    end
  end
end


class Mouse_Events
  MOVE = 1
  RESIZE = 2
  attr_writer :height, :width
  def initialize(height,width)
    @height=height
    @width=width
  end
  def length; @height<@width ? @height : @width; end
  def xoffset; (@width-length)/2; end
  def yoffset; (@height-length)/2; end
  def pressed(px,py,l,r,b,t)
    @move = false
    @left,@right,@bottom,@top, = l,r,b,t
    @axis = where(px,py,l,r,b,t)
    if @axis then
      if @axis==4 then
	@move=MOVE
	@bpx,@bpy, = px,py
	p "begin move" if $DEBUG
      else
	@move=RESIZE
 	p "begin resize" if $DEBUG
      end
    else
      @move = false
    end
  end
  def released(px,py)
    print "released:",px,",",py,"\n" if $DEBUG
    if @move==MOVE then
      @move=false
      p "end move" if $DEBUG
      dx=px-@bpx
      dy=py-@bpy
      [@left+dx,@right+dx,@bottom+dy,@top+dy]
    elsif @move==RESIZE then
      @move=false
      p "end resize" if $DEBUG
      l,r,b,t, = @left,@right,@bottom,@top
      @left=nil;@right=nil;@bottom=nil;@top=nil
      @bpx=nil;@bpy=nil
      set(px,py,l,r,b,t)
    end
  end
  def draged(w,px,py)
    if @move==MOVE then
      gc = w.style.fg_gc(w.state)
      x = (@left+px-@bpx)*length+xoffset
      y = (1-@top-py+@bpy)*length+yoffset
      xl = length*(@right-@left)
      yl = length*(@top-@bottom)
      w.window.draw_rectangle(gc, false, x, y, xl, yl)
    elsif @move==RESIZE then
      l,r,b,t, = set(px,py,@left,@right,@bottom,@top)
      gc = w.style.fg_gc(w.state)
      x = l*length+xoffset
      y = (1-t)*length+yoffset
      xl = length*(r-l)
      yl = length*(t-b)
      w.window.draw_rectangle(gc, false, x, y, xl, yl)
    end
  end
  def where(px,py,l,r,b,t)
    width = 0.01
    if (px>l-width)&&(px<r+width)&&(py>b-width)&&(py<t+width) then
      if (px-l).abs < width then 0
      elsif (px-r).abs < width then 1
      elsif (py-b).abs < width then 2
      elsif (py-t).abs < width then 3
      else 4
      end
    else
      false
    end
  end
  def move?
    @move
  end
  def set(px,py,l,r,b,t)
    diff=0.05
    lo,ro,bo,to, = [l,r,b,t]
    if @axis==0 then
      if px<diff then
	lo=diff
      elsif px>(r-diff) then
	lo=r-diff
      else
	lo=px
      end
    elsif @axis==1 then
      if px>(1-diff) then
	ro=1-diff
      elsif px<(l+diff) then
	ro=l+diff
      else
	ro=px
      end
    elsif @axis==2 then
      if py<diff then
	bo=diff
      elsif py>(t-diff) then
	bo=t-diff
      else
	bo=py
      end
    elsif @axis==3 then
      if py>(1-diff) then
	to=1-diff
      elsif py<(b+diff) then
	to=b+diff
      else
	to=py
      end
    end
    [lo,ro,bo,to]
  end
end


class Draw
  def initialize(n)
    @winno = n
    @pixmap = nil
    @height=400
    @width=400
    @rect = Array.new(4)
    clear
  end
  def clear
    @type = nil
    @title = nil
    @vxmin=0.2; @vxmax=0.8
    @vymin=0.2; @vymax=0.8
#    @miss=nil
    @x_title=""; @x_unit=""
    @y_title=""; @y_unit=""
    @fig = Fig.new
    @frame = Mouse_Events.new(@height,@width)
    @opened = false
  end
  def show
    @window.show if defined?(@window)&&(!@window.nil?)
  end
  def draw_x(draw_data)
    @xmin = draw_data.axis_x.min
    @xmax = draw_data.axis_x.max
    @ymin = draw_data.axis_y.min
    @ymax = draw_data.axis_y.max
    @gphys = draw_data.gphys
    @gphys2 = draw_data.gphys2
    @mapping = draw_data.gphys.data.mapping.slicer
#    @miss = draw_data.missing_value
    @title = draw_data.title
    @transpose = draw_data.transpose
    @logx = draw_data.axis_x.log
    @logy = draw_data.axis_y.log
    @x_title = draw_data.axis_x.title
    @x_unit = draw_data.axis_x.unit
    @y_title = draw_data.axis_y.title
    @y_unit = draw_data.axis_y.unit
    @type = draw_data.type
    @maptype = draw_data.map_type
    @cont = draw_data.cont
    @tone = draw_data.tone
    @vector = draw_data.vector
    @fig.var_set(@gphys.funcs,@mapping,0)
    @fig.var_set(@gphys2.funcs,@mapping,1) if @type == VECTOR
    @fig.title_set(@x_title,@x_unit,@y_title,@y_unit, __FILE__,__LINE__)
    if @window.nil?
      open_graphics
    else
      draw(@draw_area)
    end
    @window.show
  end
  def open_graphics
    set_env
    @draw_area = Gtk::DrawingArea.new
    @draw_area.set_size_request(@width,@height)
    @draw_area.signal_connect("expose-event"){|w,p| expose_event(w,p) }
    @draw_area.signal_connect("configure-event"){|w,p|
      configure_event(w)
      @width =  w.allocation.width; @frame.width=@width
      @height = w.allocation.height; @frame.height=@height
    }
    @draw_area.signal_connect("button-press-event"){|w,p|
      if p.button==1 then
	px = (p.x-@frame.xoffset)/@frame.length
	py = 1 - (p.y-@frame.yoffset)/@frame.length
	print "press:",px,",",py,"\n" if $DEBUG
	@frame.pressed(px,py,@vxmin,@vxmax,@vymin,@vymax)
      else
	w.get_toplevel.hide
      end
    }
    @draw_area.signal_connect("button-release-event"){|w,p|
      px = (p.x-@frame.xoffset)/@frame.length
      py = 1 - (p.y-@frame.yoffset)/@frame.length
      if @frame.move?
	@vxmin,@vxmax,@vymin,@vymax, = @frame.released(px,py)
	draw(w)
      end
    }
    @draw_area.signal_connect("motion-notify-event"){|w,p|
      px = (p.x-@frame.xoffset)/@frame.length
      py = 1 - (p.y-@frame.yoffset)/@frame.length
      if @frame.move?
	expose_event(w)
	@frame.draged(w,px,py)
      end
    }
    @draw_area.set_events(Gdk::Event::BUTTON_PRESS_MASK|Gdk::Event::BUTTON_MOTION_MASK|Gdk::Event::BUTTON_RELEASE_MASK)
    aspect_frame = Gtk::AspectFrame.new(nil,0,0,1,true)
    aspect_frame.add(@draw_area)
    @window = Gtk::Window.new(TOPLEVEL).set_resizable(true)
    @window.signal_connect("delete-event"){|w,p| w.hide; true}
    @window.signal_connect("destroy"){exit}
    @window.signal_connect("key-press-event"){|w,e|
      key = Gdk::Keyval.to_name(e.keyval)
      p key+" key press" if $DEBUG
      if key=="q" then
	w.hide
      elsif key=="space" || key=="Return" then
	$main.draw_next
      end
    }
    @window.add(aspect_frame.show_all)
    names = @gphys.funcs[0]
    @window.set_title("#{@winno}: #{names[2]}@#{names[0]}")
    @window.show
  end
  def save_code(filename)
    file = File.open(filename,"w")
    file.print(@fig.head+"\n\n")
    file.print(@fig.var+"\n\n")
    file.print(@fig.open+"\n\n")
    file.print(@fig.draw+"\n\n")
    file.print(@fig.close+"\n\n")
    file.close
  end
  def save_image(filename)
    begin
      save_image_file(filename)
      p "save image to #{filename}"
    rescue
      dialog = Gtk::Dialog.new.set_title("Error Message")
      dialog.set_modal(true)
      dialog.vbox.pack_start(Gtk::Label.new($!))
      ok_button=Gtk::Button.new("OK")
      ok_button.signal_connect("clicked"){dialog.destroy}
      dialog.action_area.pack_start(ok_button)
      dialog.show_all
    end
  end

  private
  def set_env
    DCL::swlset("LWAIT", false )
    @fig.parm_set('DCL::sglset("LCNTL", false )', __FILE__,__LINE__)
    @fig.parm_set('DCL::udlset("LMSG", false )', __FILE__,__LINE__)
    @fig.parm_set('DCL::uglset("LMSG", false )', __FILE__,__LINE__)
    @fig.parm_set('DCL::gllset("LMISS", true )', __FILE__,__LINE__)
  end

  private
  def axis
    @fig.figure_set('DCL::ussttl(x_title, x_unit, y_title, y_unit)', __FILE__,__LINE__)
    @fig.figure_set('DCL::usdaxs', __FILE__,__LINE__)
    @fig.figure_set('DCL::sglset("LCLIP", false )', __FILE__,__LINE__)
    i=0
    @gphys.lost_axes.each{|info|
      @fig.figure_set("i=#{i}", __FILE__,__LINE__)
      @fig.figure_set("DCL::sgtxzr(vxmax+0.01,vymax-0.03*i,\"#{info}\",0.02,0,-1,1)", __FILE__,__LINE__)
      i+=1
    }
  end

  private
  def draw_1d(title)
    @fig.figure_set('DCL::sgplzu(gphys0.coord(0).val, gphys0.val, 1, 3 )', __FILE__,__LINE__)
    axis
    @fig.figure_set("title = \"#{title}\"", __FILE__,__LINE__)
    @fig.figure_set('DCL::uxsttl("t", title, 0 )', __FILE__,__LINE__)
  end

  private
  def draw_2d(title,type)
#    if type==MAP then
#      tmp = NArray.sfloat(@axisx.length+1)
#      tmp[0..-2] = @axisx
#      tmp[-1] = @axisx[-1] + (@axisx[-1]-@axisx[0])/(@axisx.length-1)
#      @axisx = tmp
#      @var.reshape!(@axisx.length-1,@axisy.length)
#      tmp = NArray.sfloat(@axisx.length, @axisy.length)
#      tmp[0..-2,true] = @var
#      tmp[-1,true] = @var[0,true]
#      @var = tmp
#    end
    if @transpose then
      x = 1
      y = 0
    else
      x = 0
      y = 1
    end
    @fig.figure_set("DCL::uwsgxa(gphys0.coord(#{x}).val)", __FILE__,__LINE__)
    @fig.figure_set("DCL::uwsgya(gphys0.coord(#{y}).val)", __FILE__,__LINE__)
    @fig.figure_set('DCL::ueitlv', __FILE__,__LINE__)

    if type == CONTOUR || type == MAP
      if @tone.nil?
        min = @gphys.val.min
        max = @gphys.val.max
        dm = 0
      else
        min,dm = @tone
        max = min+dm*(50-1)
      end
      if min!=max then
        @fig.figure_set("DCL::uegtla(#{min}, #{max}, #{dm} )", __FILE__,__LINE__)
      end
      if @transpose
        @fig.figure_set("DCL::uetone(gphys0.val.transpose(#{x},#{y}))", __FILE__,__LINE__)
      else
        @fig.figure_set("DCL::uetone(gphys0.val)", __FILE__,__LINE__)
      end
      if @cont.nil?
        min = @gphys.val.min
        max = @gphys.val.max
        dm = 0
      else
        min,dm = @cont
        max = min+dm*(50-1)
      end
      if min!=max then
        @fig.figure_set("DCL::udgcla(#{min}, #{max}, #{dm} )", __FILE__,__LINE__)
      end
      if @transpose
        @fig.figure_set("DCL::udcntz(gphys0.val.transpose(#{x},#{y}))", __FILE__,__LINE__)
      else
        @fig.figure_set("DCL::udcntz(gphys0.val)", __FILE__,__LINE__)
      end
    elsif type == VECTOR
      if @vector
        @fig.figure_set("DCL::uglset('lnrmal', false)", __FILE__,__LINE__)
        @fig.figure_set("DCL::ugrset('xfact1', #{@vector[0]})", __FILE__,__LINE__)
        @fig.figure_set("DCL::ugrset('yfact1', #{@vector[1]})", __FILE__,__LINE__)
      end
      if @transpose
        @fig.figure_set("DCL::ugvect(gphys0.val.transpose(#{x},#{y}), gphys1.val.transpose(#{x},#{y}))", __FILE__,__LINE__)
      else
        @fig.figure_set("DCL::ugvect(gphys0.val, gphys1.val)", __FILE__,__LINE__)
      end
    end
    if type==CONTOUR || type==VECTOR then
      axis
      @fig.figure_set('DCL::uzrset("ROFFXT", 0.06)', __FILE__,__LINE__)
      @fig.figure_set("title = \"#{title}\"", __FILE__,__LINE__)
      @fig.figure_set('DCL::uxsttl("t", title, 0 )', __FILE__,__LINE__)
    elsif type==MAP then
      @fig.figure_set("DCL::umpglb", __FILE__,__LINE__)
      @fig.figure_set('DCL::sglset("LCLIP", false )', __FILE__,__LINE__)
      @fig.figure_set("title = \"#{title}\"", __FILE__,__LINE__)
      @fig.figure_set("DCL::sgtxzv((vxmax+vxmin)/2,vymax+0.02,title,0.02,0.0,0,2)", __FILE__,__LINE__)
      @fig.figure_set('DCL::sglset("LCLIP", true )', __FILE__,__LINE__)
    end
  end

  private
  def draw(drawing_area)
    @fig.window_set(@xmin,@xmax,@ymin,@ymax, __FILE__,__LINE__)
    @fig.viewport_set(@vxmin,@vxmax,@vymin,@vymax, __FILE__,__LINE__)
    @fig.frame_clear
    @fig.figure_clear
    if @type == MAP then
      itr = @maptype
      @fig.frame_set("DCL::grfrm", __FILE__,__LINE__)
      @fig.frame_set("DCL::grsvpt(vxmin,vxmax,vymin,vymax)", __FILE__,__LINE__)
      @fig.frame_set("DCL::grsmpl(0.0,90.0,0.0)", __FILE__,__LINE__)
      @fig.frame_set("DCL::grstrn(#{itr})", __FILE__,__LINE__)
      @fig.frame_set("DCL::umpfit", __FILE__,__LINE__)
      @fig.frame_set("DCL::grstrf", __FILE__,__LINE__)
    else
      itr = 1
      itr += 2 if @logx; itr += 1 if @logy
      @fig.frame_set("DCL::grfrm", __FILE__,__LINE__)
      @fig.frame_set("DCL::grswnd(xmin,xmax,ymin,ymax)", __FILE__,__LINE__)
      @fig.frame_set("DCL::grsvpt(vxmin,vxmax,vymin,vymax)", __FILE__,__LINE__)
      @fig.frame_set("DCL::grstrn(#{itr})", __FILE__,__LINE__)
      @fig.frame_set("DCL::grstrf", __FILE__,__LINE__)
    end
    @fig.figure_set('DCL::sglset("LCLIP", true )', __FILE__,__LINE__)
    if @type == LINE then
      draw_1d(@title)
    elsif @type==CONTOUR || @type==MAP || @type==VECTOR then
      draw_2d(@title,@type)
      # draw map if @type==MAP
    end
    DCL::zgsdrw(drawing_area)
    DCL::zgspmp(@pixmap)
    reset
    gphys0 = @gphys
    gphys1 = @gphys2 if @type==VECTOR
    codes = @fig.draw_eval
    for j in 0...codes.length
      eval *codes[j]
    end
    expose_event(drawing_area)
  end

  private
  def reset
    wsxmn,wsxmx,wsymn,wsymx,fact = DCL::swqrct
    DCL::stswrc(wsxmn,wsxmx,wsymn,wsymx)
    DCL::slinit(wsxmx-wsxmn,wsymx-wsymn,fact)
  end
    
  def expose_event(widget,event=nil)
    if ! @pixmap.nil?
      gc = widget.style.fg_gc(widget.state)
      if event.nil?
	size = widget.window.size
        x0, y0 = [0, 0]
        width, height = size
      else
	area = event.area
        x0, y0 = [area.x, area.y]
        width, height = [area.width, area.height]
      end
      widget.window.draw_drawable(gc, @pixmap, x0, y0, x0, y0, width, height)
    end
    false
  end

  private
  def configure_event(widget)
    size = widget.window.size
    if (size[0]>0 && size[1]>0) then
      @pixmap = Gdk::Pixmap.new(widget.window, size[0], size[1], -1)
      @pixmap.draw_rectangle(widget.style.white_gc, true, 0, 0, size[0], size[1])
    end
    DCL::zgsdrw(widget)
    DCL::zgspmp(@pixmap)
    if !@opened then
      DCL::sgopn(4)
      @opened = true
    else
      reset
    end
    draw(widget)
    true
  end

  private
  class Fig
    attr_reader :head, :var, :open, :close
    def initialize
      @head = ""
      @var = ""
      @open = ""
      @window = Array.new
      @viewport = Array.new
      @title = Array.new
      @parm = Array.new
      @frame = Array.new
      @figure = Array.new
      @close = ""
      if DCL.gtk_version[0]==1 then
        @head = "require \"gtk\""
      elsif DCL.gtk_version[0]==2 then
        @head = "require \"gtk2\""
      end
      @head = <<"END"
require "numru/gphys"
require "numru/dcl"

include NumRu
END
      @open = "DCL::gropn(1)"
      ymin=@ymin; ymax=@ymax
      vxmin=@vxmin; vxmax=@vxmax
      vymin=@vymin; vymax=@vymax
      @window[0] = "xmin = axisx[0]"
      @window[1] = "xmax = axisx[-1]"
      @window[2] = "ymin = 0.0"
      @window[3] = "ymax = 1.0"
      @viewport[0] = "vxmin = 0.2"
      @viewport[1] = "vxmax = 0.8"
      @viewport[2] = "vymin = 0.2"
      @viewport[3] = "vymax = 0.8"
      @title[0] = 'x_title = ""'
      @title[1] = 'x_unit = ""'
      @title[2] = 'y_title = ""'
      @title[3] = 'y_unit = ""'
      @close = "DCL::grcls"
    end
    def var_set(funcs,mapping,n)
      @var = "" if n==0
      filename, filetype, varname = funcs[0]
      if filename.length==1
        @var += "filename = \"#{filename[0]}\"\n"
      else
        @var += "filename = [\"#{filename.join('", "')}\"]\n"
      end
      @var += "varname = \"#{varname}\"\n"


      if filetype=="netcdf"
        @var += "gphys#{n} = GPhys::NetCDF_IO.open(filename,varname)\n"
      elsif filetype=="grads"
        @var += "gphys#{n} = GPhys::GrADS_IO.open(filename,varname)\n"
      elsif filetype=="druby"
        @var += "class NArray\n"
        @var += "  def self._load(o) to_na(*Marshal::load(o)).ntoh end\n"
        @var += "end\n"
        @var += "ary = filename.split('/')\n"
        @var += "host = 'druby://'+ary[2]\n"
        @var += "path = ary[3..-1].join('/')\n"
        @var += "tree = DRbObject.new(nil,host)\n"
        @var += "gphys#{n} = tree.data(path)\n"
      else
        raise "filetype isn't invalid"
      end

      if funcs.length!=0
	funcs[1..-1].each{|func|
	  @var += "gphys#{n} = gphys#{n}.cut(#{func[2].join(",")})\n"
	  @var += "gphys#{n} = gphys#{n}.#{func[0]}(#{func[1]})\n"
	}
      end
      str = mapping.collect{|a|
        if NArray===a
          "NArray[#{a.to_a.join(",")}]"
        else
          a
        end
        }.join(",")
      @var += "gphys#{n} = gphys#{n}[#{str}]\n"
    end
    def window_set(xmin,xmax,ymin,ymax, file,line)
      @window[0] = "xmin = #{xmin}"
      @window[1] = "xmax = #{xmax}"
      @window[2] = "ymin = #{ymin}"
      @window[3] = "ymax = #{ymax}"
      @w_context = [file,line]
    end
    def viewport_set(vxmin,vxmax,vymin,vymax, file,line)
      @viewport[0] = "vxmin = #{vxmin}"
      @viewport[1] = "vxmax = #{vxmax}"
      @viewport[2] = "vymin = #{vymin}"
      @viewport[3] = "vymax = #{vymax}"
      @v_context = [file,line]
    end
    def title_set(xt,xu,yt,yu, file,line)
      @title[0] = "x_title = \"#{xt}\""
      @title[1] = "x_unit = \"#{xu}\""
      @title[2] = "y_title = \"#{yt}\""
      @title[3] = "y_unit = \"#{yu}\""
      @t_context = [file,line]
    end
    def parm_clear
      @parm.clear
    end
    def parm_set(str,file,line)
      @parm.push([str,file,line])
    end
    def frame_clear
      @frame.clear
    end
    def frame_set(str,file,line)
      @frame.push([str,file,line])
    end
    def figure_clear
      @figure.clear
    end
    def figure_set(str,file,line)
      @figure.push([str,file,line])
    end
    def draw_parm
    end
    def draw
      [@window, @viewport, @title].collect{|a|
        a.join("\n")
      }.join("\n\n")+"\n\n"+
      [@parm, @frame, @figure].collect{|a|
        a.collect{|c| c[0]}.join("\n")
      }.join("\n\n")
    end
    def draw_eval
      codes = Array.new
      @window.each{|c| codes.push [c, nil, *@w_context]}
      @viewport.each{|c| codes.push [c, nil, *@v_context]}
      @title.each{|c| codes.push [c, nil, *@t_context]}
      [@parm, @frame, @figure].each{|a|
        a.each{|c| codes.push [c[0], nil, c[1], c[2]]}
      }
      codes
    end
  end
end
