class Game

    PHYSICS_FPS = 60.0
    PHYSICS_INTERVAL = 1.0 / PHYSICS_FPS
    
    WORLD_WIDTH = 64
    WORLD_HEIGHT = 16
    
    R0 = WORLD_WIDTH / (2.0 * Math::PI)
    R1 = R0 + 1.0
    
    VIEW_ROTATION_SPEED = 0.25 * Math::PI
    
    def initialize
        @leftover_dt = 0.0
    end
    
    def opengl_context_created
        @world = File.read('level.txt').gsub(/\n/, '').enum_for(:each_byte).map { |b|
            if b == ?. then
                nil
            else
                :wall
            end
        }
        @view_angle = 0.0
        
        glDepthFunc(GL_LEQUAL)
        glEnable(GL_DEPTH_TEST)
        
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
    end
    
    def opengl_context_resized(w, h)
        @width, @height = w, h
    end
    
    def display(dt)
        _update(dt)
        _draw()
    end
    
    def text_entered(text)
    end

    def key_pressed(keycode)
    end
    
    def key_released(keycode)
    end
    
    def mouse_pressed(button)
    end
    
    def mouse_released(button)
    end
    
    def mouse_moved_by(dx, dy)
    end
    
    def mouse_moved_to(x, y)
    end
    
    def _update(dt)
        @leftover_dt += dt
        while @leftover_dt >= PHYSICS_INTERVAL do
            @view_angle += VIEW_ROTATION_SPEED * PHYSICS_INTERVAL
            if @view_angle > Math::PI then
                @view_angle -= 2.0 * Math::PI
            end
            
            @leftover_dt -= PHYSICS_INTERVAL
        end
    end
    
    def _draw()
        glClearColor(1.0, 1.0, 0.0, 0.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(30.0, 8.0 / 5.0, 1.0, 100.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        gluLookAt(
            -R0 * Math.cos(@view_angle), 6.0, -R0 * Math.sin(@view_angle),
            Math.cos(@view_angle), 6.0, Math.sin(@view_angle),
            0.0, 1.0, 0.0)
        glLightfv(GL_LIGHT0, GL_POSITION, [ -1.0, -1.0, 0.0, 0.0 ].pack('f*'))
        
        angle_fraction = 2.0 * Math::PI / WORLD_WIDTH
        
        glBegin(GL_QUADS)
        0.upto(WORLD_HEIGHT - 1) do |_y|
            y = WORLD_HEIGHT - 1 - _y
            0.upto(WORLD_WIDTH - 1) do |x|
                i = x + _y * WORLD_WIDTH
                next unless @world[i]
                                
                angle0 = x * angle_fraction
                angle1 = angle0 + angle_fraction
                
                s0 = Math.sin(angle0)
                c0 = Math.cos(angle0)
                s1 = Math.sin(angle1)
                c1 = Math.cos(angle1)
                
                v = [
                    [ c0 * R0, y    , s0 * R0 ],
                    [ c1 * R0, y    , s1 * R0 ],
                    [ c1 * R0, y + 1, s1 * R0 ],
                    [ c0 * R0, y + 1, s0 * R0 ],
                    
                    [ c0 * R1, y    , s0 * R1 ],
                    [ c1 * R1, y    , s1 * R1 ],
                    [ c1 * R1, y + 1, s1 * R1 ],
                    [ c0 * R1, y + 1, s0 * R1 ],
                ].map { |v| v.pack('f*') }
                
                glNormal3f(-s0, 0.0, -c0)
                glVertex3fv(v[0])
                glVertex3fv(v[1])
                glVertex3fv(v[2])
                glVertex3fv(v[3])
                
                glNormal3f(0.0, 0.0, 0.0)
                glVertex3fv(v[4])
                glVertex3fv(v[0])
                glVertex3fv(v[3])
                glVertex3fv(v[7])
                
                glNormal3f(0.0, 0.0, 0.0)
                glVertex3fv(v[1])
                glVertex3fv(v[5])
                glVertex3fv(v[6])
                glVertex3fv(v[2])
                
                glNormal3f(0.0, 1.0, 0.0)
                glVertex3fv(v[3])
                glVertex3fv(v[2])
                glVertex3fv(v[6])
                glVertex3fv(v[7])
                
                glNormal3f(0.0, -1.0, 0.0)
                glVertex3fv(v[5])
                glVertex3fv(v[4])
                glVertex3fv(v[0])
                glVertex3fv(v[1])
            end
        end
        glEnd()
        
    end
    
end

$game = Game.new
