#!/usr/bin/env ruby

# this line prevents other db adapters from being loaded (oci8 was
# causing some pain.)
unless Object.const_defined? :RAILS_CONNECTION_ADAPTERS
    RAILS_CONNECTION_ADAPTERS = []
end
RAILS_CONNECTION_ADAPTERS.replace %w[sqlite]

require 'delegate'
require 'optparse'
require 'stringio'
require 'rubygems'
require 'camping'

host = '0.0.0.0'
port = 3301

db = nil
homes = []
homes << File.join( ENV['HOME'], '.camping.db' ) if ENV['HOME']
homes << File.join( ENV['APPDATA'], 'Camping.db' ) if ENV['APPDATA']
homes.each do |db|
    break if File.exists?( db )
end


opts = OptionParser.new do |opts|
    opts.banner = "Usage: camping app1.rb, app2.rb..."
    opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
    opts.separator ""
    opts.separator "Specific options:"

    opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") do |h|
        host = h
    end

    opts.on("-p", "--port NUM", "Port for web server (defaults to #{port})") do |p|
        port = p
    end

    opts.on("-d", "--database FILE", "Database file (defaults to #{db})") do |d|
        db = d
    end

    opts.separator ""
    opts.separator "Common options:"
  
    # No argument, shows at tail.  This will print an options summary.
    # Try it and see!
    opts.on_tail("-h", "--help", "Show this message") do
        puts opts
        exit
    end
  
    # Another typical switch to print the version.
    opts.on_tail("--version", "Show version") do
        class << Gem; attr_accessor :loaded_specs; end
        puts Gem.loaded_specs['camping'].version
        exit
    end
end

opts.parse! ARGV
if ARGV.length < 1
    puts opts
    exit
end

Camping::Models::Base.establish_connection :adapter => 'sqlite3', :database => db

class CampingReloader
    attr_accessor :klass, :mtime, :mount

    def initialize(script)
        @script = script
        @mount = File.basename(script, '.rb')
        load_app
    end

    def load_app
        @mtime = File.mtime(@script)
        title = File.basename(@script)[/^(\w+)/,1]
        begin
            load @script
        rescue Exception => e
            puts "!! trouble loading #{title}: [#{e.class}] #{e.message}"
            @klass = nil
            return
        end

        @klass = Object.const_get(Object.constants.grep(/^#{title}$/i)[0]) rescue nil
        unless @klass.const_defined? :C
            puts "!! trouble loading #{title}: not a Camping app"
            @klass = nil
            return
        end
        @klass.create if @klass.respond_to? :create
        @klass
    end

    # Load the script, locate the module
    def reload_app 
        newtime = File.mtime( @script )
        return if @klass and @mtime and newtime <= @mtime

        k = @klass
        Object.instance_eval { remove_const k.name } if k
        load_app
    end

    def run(*a)
        reload_app
        if @klass
            @klass.run(*a) 
        else
            Camping.run(*a)
        end
    end

    def view_source
        File.read(@script)
    end
end

apps = ARGV.map { |script| CampingReloader.new(script) }
def apps.index_page
    welcome = "You are Camping"
    apps = self
    b = Markaby::Builder.new({}, {})
    b = b.instance_eval do
        html do
            head do
                title welcome
                style <<-END, :type => 'text/css'
                    body { 
                        font-family: verdana, arial, sans-serif; 
                        padding: 10px 40px; 
                        margin: 0; 
                    }
                    h1, h2, h3, h4, h5, h6 {
                        font-family: utopia, georgia, serif;
                    }
                END
            end
            body do
                h1 welcome
                p %{Good day.  These are the Camping apps you've mounted.}
                ul do
                    apps.each do |app|
                        next unless app.klass
                        li do
                            h3(:style => "display: inline") { a app.klass.name, :href => "/#{app.mount}" }
                            small { text " / " ; a "View Source", :href => "/code/#{app.mount}" }
                        end
                    end
                end
            end
        end
    end
    b.to_s
end

begin
    require 'mongrel'
    require 'mongrel/camping'
    class IndexHandler < Mongrel::HttpHandler
        def initialize(apps)
            @apps = apps
        end
        def process(req, res)
            res.start(200) do |head, out|
                out << @apps.index_page
            end
        end
    end
    class ViewSource < Mongrel::HttpHandler
        def initialize(app)
            @app = app
        end
        def process(req, res)
            res.start(200) do |head, out|
                head['Content-Type'] = 'text/plain'
                out << @app.view_source
            end
        end
    end
    config = Mongrel::Configurator.new :host => host do
        listener :port => port do
            apps.each do |app|
                uri "/#{app.mount}", :handler => Mongrel::Camping::CampingHandler.new(app)
                uri "/code/#{app.mount}", :handler => ViewSource.new(app)
            end
            uri "/", :handler => IndexHandler.new(apps)
            uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("")
            trap("INT") { stop }
            run
        end
    end
    config.join
rescue LoadError
    require 'webrick/httpserver'
    require 'camping/webrick'

    # Mount the root
    s = WEBrick::HTTPServer.new(:BindAddress => host, :Port => port)
    apps.each do |app|
        s.mount "/#{app.mount}", WEBrick::CampingHandler, app
        s.mount_proc("/code/#{app.mount}") do |req, resp|
            resp['Content-Type'] = 'text/plain'
            resp.body = app.view_source
        end
    end
    s.mount_proc("/") { |req, resp| resp.body = apps.index_page }

    # Server up
    trap(:INT) do
        s.shutdown
    end
    s.start
end
