#
# gtkglut.rb:
# Sort of GLUT-compatible library on top of Ruby/GtkGKExt.
# It does only work for simple cases, you should really use Ruby/GtkGKExt
# instead. If you really want to use GLUT, use yoshi's Ruby/GLUT.
#
# But it can be an interesting introduction for those who only know GLUT.
# It can show them that using GLUT is quite a bad idea ;o),
# that it must be using lots of global variables...
#
# I am sure there is a better way to do most things, but I do not know Gtk+
# and Ruby well enough. You are free to improve it :o)
#
# If you really want to use it, put "require 'gtkglut'" instead of
# "require 'glut'" at the beginning of your file.
# You can then (try to) use it as yoshi's Ruby/GLUT binding.
#
# written by Vincent Isambart <isambart@netcourrier.com>
#
require 'gtkglext'

module GLUT
    RGB     = Gdk::GLConfig::MODE_RGB
    RGBA    = Gdk::GLConfig::MODE_RGBA
    SINGLE  = Gdk::GLConfig::MODE_SINGLE
    DOUBLE  = Gdk::GLConfig::MODE_DOUBLE
    DEPTH   = Gdk::GLConfig::MODE_DEPTH
    ACCUM   = Gdk::GLConfig::MODE_ACCUM
    ALPHA   = Gdk::GLConfig::MODE_ALPHA
    STENCIL = Gdk::GLConfig::MODE_STENCIL

    @@window_size = [300, 300]
    @@window_pos  = [100, 100]

    def GLUT.Init(argc = 1, argv = [])
        Gtk::GL.init
    end

    def GLUT.InitWindowPosition(x, y)
        @@window_pos = [x, y]
    end

    def GLUT.InitWindowSize(w, h)
        @@window_size = [w, h]
    end

    def GLUT.InitDisplayMode(mode)
        @@glconfig = Gdk::GLConfig.new(mode)
        raise "can't initialize OpenGL" if !@@glconfig
    end

    def GLUT.SwapBuffers
        @@drawing_area.gl_drawable.swap_buffers
    end

    def GLUT.PostRedisplay
        @@drawing_area.queue_draw
    end

    def GLUT.method_missing(id, *args)
        name = id.id2name
        if name[0..3] == 'Wire'
            Gdk::GL.send("draw_#{name.downcase[4..-1]}", false, *args)
        elsif name[0..4] == 'Solid'
            Gdk::GL.send("draw_#{name.downcase[5..-1]}", true,  *args)
        else
            raise NoMethodError, "undefined method `#{name}' for GLUT:Module"
        end
    end
    
    def GLUT.CreateWindow(name = "gtkglext")
        # Top-level window
        @@window = Gtk::Window.new
        # Perform the resize immediately
        @@window.resize_mode = Gtk::RESIZE_IMMEDIATE
        # Get automatically redrawn if any of their children
        # changed allocation.
        @@window.reallocate_redraws = true
        @@window.signal_connect("delete_event") do
            Gtk.main_quit
            true
        end
        @@window.title = name

        @@window.set_events(@@window.events      |
            Gdk::Event::BUTTON_MOTION_MASK       |
            Gdk::Event::POINTER_MOTION_HINT_MASK |
            Gdk::Event::BUTTON_PRESS_MASK)

        # VBox
        vbox = Gtk::VBox.new
        @@window.add(vbox)
        vbox.show

        # Drawing area for drawing OpenGL scene
        @@drawing_area = Gtk::DrawingArea.new
        # Set OpenGL-capability to the widget
        @@drawing_area.set_gl_capability(@@glconfig)
        vbox.pack_start(@@drawing_area)
        @@drawing_area.show

        # The following is a big hack!
        # Do not do it in a Ruby/GtkGKExt program!
        # As with GLUT the OpenGL initializations are done between a
        # CreateWindow and a MainLoop, we must show the window (or else,
        # we can't get the context) and do a gl_begin.
        # In a real Ruby/GtkGLExt program, the OpenGL initialization stuff
        # should be in a signal_connect_after("realize") event callback,
        # which is called for the first display of the window,
        # during a window.show (and not after the window.show as I do here)
        @@window.show
        glcontext  = @@drawing_area.gl_context
        gldrawable = @@drawing_area.gl_drawable

        raise "cannot activate OpenGL" if !gldrawable.gl_begin(glcontext)
    end

    def GLUT.MainLoop
        @@drawing_area.gl_drawable.gl_end
        @@drawing_area.set_size_request(*@@window_size)
        Gtk.main
    end

    UP   = false
    DOWN = true

    LEFT_BUTTON   = 1
    MIDDLE_BUTTON = 2
    RIGHT_BUTTON  = 3

    def GLUT.MouseFunc(mouse)
        if mouse
            @@window.signal_connect("button_press_event")   { |w, e|
                mouse.call(e.button, DOWN, e.x, e.y) }
            @@window.signal_connect("button_release_event") { |w, e|
                mouse.call(e.button, UP  , e.x, e.y) }
        else
            @@window.signal_connect("button_press_event")   { |w, e| }
            @@window.signal_connect("button_release_event") { |w, e| }
        end
    end

    @@idle_num = nil
    def GLUT.IdleFunc(idle)
        Gtk.idle_remove(@@idle_num) if @@idle_num
        @@idle_num = nil
        @@idle_num = Gtk.idle_add { idle.call() } if idle
    end

    def GLUT.KeyboardFunc(keyboard)
        if keyboard
            @@window.signal_connect_after("key_press_event") do |i, e|
                keyval = nil
                case e.keyval
                    when Gdk::Keyval::GDK_Escape then keyval = 27
                    else keyval = e.keyval
                end
                # TODO: get the cursor pos
                keyboard.call(keyval, 0, 0)
            end
        else
            @@window.signal_connect_after("key_press_event") { |i, e| }
        end
    end

    def GLUT.DisplayFunc(display)
        if display
            @@drawing_area.signal_connect("expose_event") do |w, e|
                glcontext  = w.gl_context
                gldrawable = w.gl_drawable

                gldrawable.gl_begin(glcontext) do
                    display.call
                    true
                end
            end
        else
            @@drawing_area.signal_connect("expose_event") { |w, e| }
        end
    end

    def GLUT.ReshapeFunc(reshape)
        if reshape
            @@drawing_area.signal_connect("configure_event") do |w, e|
                glcontext  = w.gl_context
                gldrawable = w.gl_drawable

                gldrawable.gl_begin(glcontext) do
                    reshape.call(w.allocation.width, w.allocation.height)
                    true
                end
            end
        else
            @@drawing_area.signal_connect("configure_event") { |w, e| }
        end
    end
end
