ENV['SERVER'] = 'true'

require 'opal'
if RUBY_PLATFORM == 'java'
  require 'jubilee'
else
  require 'thin'
end

require 'rack'
require 'sass'
if RUBY_PLATFORM != 'java'
  require 'rack/sockjs'
  require 'eventmachine'
end
require 'sprockets-sass'
require 'listen'

require 'volt'
require 'volt/boot'
require 'volt/tasks/dispatcher'
require 'volt/tasks/task_handler'
require 'volt/server/component_handler'
if RUBY_PLATFORM != 'java'
  require 'volt/server/socket_connection_handler'
end
require 'volt/server/rack/component_paths'
require 'volt/server/rack/index_files'
require 'volt/server/rack/opal_files'
require 'volt/server/rack/quiet_common_logger'
require 'volt/page/page'

module Rack
  # TODO: For some reason in Rack (or maybe thin), 304 headers close
  # the http connection.  We might need to make this check if keep
  # alive was in the request.
  class KeepAlive
    def initialize(app)
      @app = app
    end

    def call(env)
      status, headers, body = @app.call(env)

      if status == 304 && env['HTTP_CONNECTION'].downcase == 'keep-alive'
        headers['Connection'] = 'keep-alive'
      end

      [status, headers, body]
    end
  end
end

module Volt
  class Server
    def initialize(root_path = nil)
      root_path ||= Dir.pwd
      Volt.root = root_path

      @app_path        = File.expand_path(File.join(root_path, 'app'))

      # Boot the volt app
      @component_paths = Volt.boot(root_path)

      setup_change_listener

      display_welcome
    end

    def display_welcome
      puts File.read(File.join(File.dirname(__FILE__), 'server/banner.txt'))
    end

    def setup_change_listener
      # Setup the listeners for file changes
      listener = Listen.to("#{@app_path}/") do |modified, added, removed|
        puts 'file changed, sending reload'
        SocketConnectionHandler.send_message_all(nil, 'reload')
      end
      listener.start
    end

    def app
      @app = Rack::Builder.new

      # Should only be used in production
      if Volt.config.deflate
        @app.use Rack::Deflater
        @app.use Rack::Chunked
      end

      @app.use Rack::ContentLength

      @app.use Rack::KeepAlive
      @app.use Rack::ConditionalGet
      @app.use Rack::ETag

      @app.use QuietCommonLogger
      @app.use Rack::ShowExceptions

      component_paths = @component_paths
      @app.map '/components' do
        run ComponentHandler.new(component_paths)
      end

      # Serve the opal files
      opal_files = OpalFiles.new(@app, @app_path, @component_paths)

      # Serve the main html files from public, also figure out
      # which JS/CSS files to serve.
      @app.use IndexFiles, @component_paths, opal_files

      component_paths.require_in_components

      # Handle socks js connection
      if RUBY_PLATFORM != 'java'
        SocketConnectionHandler.dispatcher = Dispatcher.new

        @app.map '/channel' do
          run Rack::SockJS.new(SocketConnectionHandler) # , :websocket => false
        end
      end

      @app.use Rack::Static,
               urls: ['/'],
               root: 'config/base',
               index: '',
               header_rules: [
                 [:all, { 'Cache-Control' => 'public, max-age=86400' }]
               ]

      @app.run lambda { |env| [404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found']] }

      @app
    end
  end
end