require "numru/gave/draw"
require "numru/dcl"


if defined?(DCL.gtk_version)
  GAVE_GTK_VERSION = DCL.gtk_version[0]
else
  GAVE_GTK_VERSION = 1
end



if GAVE_GTK_VERSION == 1
  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 GAVE_GTK_VERSION == 2
  def save_image_file(filename)
    pixbuf = Gdk::Pixbuf.from_drawable(nil, @pixmap, 0, 0, -1, -1)
    pixbuf.save(filename, "png")
  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
  alias :_initialize :initialize
  def initialize(n)
    _initialize(n)
    @pixmap = nil
  end
  alias :_clear :clear
  def clear
    _clear
    @vxmin=0.2; @vxmax=0.8
    @vymin=0.2; @vymax=0.8
    @frame = Mouse_Events.new(@height,@width)
  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

  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 @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
        if @tone_i.nil?
          min = @gphys.val.min
          max = @gphys.val.max
          dm = 0
        else
          min,dm = @tone_i
          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::#{@tone}(gphys0.val.transpose(#{x},#{y}))", __FILE__,__LINE__)
        else
          @fig.figure_set("DCL::#{@tone}(gphys0.val)", __FILE__,__LINE__)
        end
      end
      if @cont
        if @cont_i.nil?
          min = @gphys.val.min
          max = @gphys.val.max
          dm = 0
        else
          min,dm = @cont_i
          max = min+dm*(50-1)
        end
        if min!=max then
          @fig.figure_set("DCL::udgcla(#{min}, #{max}, #{dm} )", __FILE__,__LINE__)
        end
      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__)
		if @map_draw
		  @fig.figure_set('DCL::umpmap("coast_world")', __FILE__,__LINE__)
	   end
      @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
      lon,lat,rot = @map_pole
      @fig.frame_set("DCL::grfrm", __FILE__,__LINE__)
      @fig.frame_set("DCL::grsvpt(vxmin,vxmax,vymin,vymax)", __FILE__,__LINE__)
      @fig.frame_set("DCL::grswnd(xmin,xmax,ymin,ymax)", __FILE__,__LINE__)
      @fig.frame_set("DCL::grsmpl(#{lon},#{lat},#{rot})", __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)
    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 GAVE_GTK_VERSION == 1
        @head = "require \"gtk\""
      elsif GAVE_GTK_VERSION == 2
        @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.nil?
        @var += "gphys#{n} = GPhys::IO.open(filename,varname)\n"
      elsif filetype=="druby"
        @var += "require \"drb/drb\"\n"
        @var += "DRb.start_service\n"
        @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 is 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
