# DO NOT REMOVE THIS COMMENT - PART OF TESTMODEL.
# Copyright (c)2006 Ross Bamford. See LICENSE.
$CURB_TESTING = true
require 'uri'

$TOPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
$EXTDIR = File.join($TOPDIR, 'ext')
$LIBDIR = File.join($TOPDIR, 'lib')
$:.unshift($LIBDIR)
$:.unshift($EXTDIR)

require 'curb'
require 'test/unit'
require 'fileutils'

$TEST_URL = "file://#{URI.escape(File.expand_path(__FILE__).tr('\\','/').tr(':','|'))}"

require 'thread'
require 'webrick'

# set this to true to avoid testing with multiple threads
# or to test with multiple threads set it to false
# this is important since, some code paths will change depending
# on the presence of multiple threads
TEST_SINGLE_THREADED=false

# keep webrick quiet
class ::WEBrick::HTTPServer
  def access_log(config, req, res)
    # nop
  end
end
class ::WEBrick::BasicLog
  def log(level, data)
    # nop
  end
end

#
# Simple test server to record number of times a request is sent/recieved of a specific 
# request type, e.g. GET,POST,PUT,DELETE
#
class TestServlet < WEBrick::HTTPServlet::AbstractServlet

  def self.port=(p)
    @port = p
  end

  def self.port
    (@port or 9129)
  end

  def self.path
    '/methods'
  end

  def self.url
    "http://127.0.0.1:#{port}#{path}"
  end

  def respond_with(method,req,res)
    res.body = method.to_s
    $auth_header = req['Authorization']
    res['Content-Type'] = "text/plain"
  end

  def do_GET(req,res)
    if req.path.match /redirect$/
      res.status = 302
      res['Location'] = '/foo'
    elsif req.path.match /not_here$/
      res.status = 404
    elsif req.path.match /error$/
      res.status = 500
    end
    respond_with("GET#{req.query_string}",req,res)
  end

  def do_HEAD(req,res)
    res['Location'] = "/nonexistent"
    respond_with("HEAD#{req.query_string}",req,res)
  end

  def do_POST(req,res)
    if req.query['filename'].nil?
      if req.body
        params = {}
        req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
      end
      if params and params['s'] == '500'
        res.status = 500
      else
        respond_with("POST\n#{req.body}",req,res)
      end
    else
      respond_with(req.query['filename'],req,res)
    end
  end

  def do_PUT(req,res)
    res['X-Requested-Content-Type'] = req.content_type
    respond_with("PUT\n#{req.body}",req,res)
  end

  def do_DELETE(req,res)
    respond_with("DELETE#{req.query_string}",req,res)
  end

  def do_PURGE(req,res)
    respond_with("PURGE#{req.query_string}",req,res)
  end

  def do_COPY(req,res)
    respond_with("COPY#{req.query_string}",req,res)
  end

  def do_PATCH(req,res)
    respond_with("PATCH\n#{req.body}",req,res)
  end

  def do_OPTIONS(req,res)
    respond_with("OPTIONS#{req.query_string}",req,res)
  end

end

module TestServerMethods
  def locked_file
    File.join(File.dirname(__FILE__),"server_lock-#{@__port}")
  end

  def server_setup(port=9129,servlet=TestServlet)
    @__port = port
    if @server.nil? and !File.exist?(locked_file)

      File.open(locked_file,'w') {|f| f << 'locked' }
      if TEST_SINGLE_THREADED
        rd, wr = IO.pipe
        @__pid = fork do
          rd.close
          rd = nil

          # start up a webrick server for testing delete 
          server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))

          server.mount(servlet.path, servlet)
          server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))

          trap("INT") { server.shutdown }
          GC.start
          wr.flush
          wr.close
          server.start
        end
        wr.close
        rd.read
        rd.close
      else
        # start up a webrick server for testing delete 
        @server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => File.expand_path(File.dirname(__FILE__))

        @server.mount(servlet.path, servlet)
        @server.mount("/ext", WEBrick::HTTPServlet::FileHandler, File.join(File.dirname(__FILE__),'..','ext'))
        queue = Queue.new # synchronize the thread startup to the main thread

        @test_thread = Thread.new { queue << 1; @server.start }

        # wait for the queue
        value = queue.pop
        if !value
          STDERR.puts "Failed to startup test server!"
          exit(1)
        end

      end

      exit_code = lambda do
        begin
          if File.exist?(locked_file)
            File.unlink locked_file
            if TEST_SINGLE_THREADED
              Process.kill 'INT', @__pid
            else
              @server.shutdown unless @server.nil?
            end
          end
          #@server.shutdown unless @server.nil?
        rescue Object => e
          puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}"
        end
      end

      trap("INT"){exit_code.call}
      at_exit{exit_code.call}

    end
  rescue Errno::EADDRINUSE
  end
end
