=begin
= WebUnit::Response
  Copyright(C) 2001 yuichi TAKAHASHI, Narushima Hironori.
  $Id: response.rb,v 1.1.1.1 2003/01/21 11:50:40 yuichi Exp $
=end

require 'net/http'

module WebUnit

  class Response

    include Utils

    @@host = nil
    @@port = nil
    @@http = nil

    attr_reader :url, :method, :response, :body, :code
    attr_reader :tree, :links, :forms, :tables, :frames, :images
    attr_accessor :username, :password

=begin
--- Response.get(url,data=nil)
      return a Response.
=end
    def self::get( url, data=nil, up=nil )
      r = self.new
      r.username = up[0] if up
      r.password = up[1] if up
      r.init_http( url, "GET", data )
    end

=begin
--- Response.post(url,data=nil)
      return a Response.
=end
    def self::post( url, data=nil, up=nil )
      r = self.new      
      r.username = up[0] if up
      r.password = up[1] if up
      r.init_http( url, "POST", data )
    end

=begin
--- Response.html( html)
      return a Response.
=end

    def self::html( html, url=nil )
      html = "<html>#{html}</html>" unless html =~ %r!^<html>!
      self.new.init_html( html, url )
    end

    def self::reset
      begin
        @@http.finish if @@http
      rescue IOError
        @@http = nil
      end
      @@host = nil
      @@port = nil
      @@http = nil
    end

    def init_html( html, url )
      @url = url
      @method = 'html'
      @body = html
      feed
      self
    end

	def do_connect(host, port)
      if @@host != host or @@port != port
        @@host = host
        @@port = port
        begin
          @@http.finish if @@http
        rescue IOError
          @@http = nil
        end
        @@http = Net::HTTP::new( host, port )
      end
	end

    def init_http( url, method, data=nil, retried=false, multipart=false)
      url = $URLBASE + url unless url =~ %r!://!
      @url = url
      @method = method
      header = {}
	  
      prot,host,port,path = parse_url( url )
	  do_connect( host, port)
	  
      header.update( Cookies::header( path ) )
      if @username != nil && @password != nil
        header['authorization'] =
        'Basic ' + ["#{@username}:#{@password}"].pack('m').gsub(/\s+/, '')
      end
	  
      begin
	    if multipart
	        header['Content-type'] = "multipart/form-data,boundary=#{BOUNDARY}"
			
			#puts data
			
	        @response = @@http.post2( path, data.gsub("\n", "\r\n"), header)
		else
          case @method.upcase
          when "GET" then
		    data
            path << '?' + data if data
            puts path if $DEBUG
            @response = @@http.get2( path, header )
          when "POST" then
            if header['Content-type'] == nil
              header['Content-type'] = "application/x-www-form-urlencoded"
            end
            @response = @@http.post2( path, data, header )
          end
		end
      rescue Errno::EPIPE
        @@host = nil
        raise if retried
        return self.init_http( url, method, data, true, multipart)
      end
	  
      @body = @response.body
      @code = @response.code
      p @response['set-cookie'] if $DEBUG
      Cookies::update( @response['set-cookie'] )
      case @response['content-type']
      when %r!text/html!
        feed
      when %r!text/xml!
        return DomWalker::new( @response.body )
      end
	  
      raise HttpNotFound if @code == "404"
      self
    end

    def feed
      @forms = []
      @tables = []
      @links = []
      @frames = []
      @images = []
	  
      p = Parser.new
      @tree = p.feed( self )
      p.close
      freeze
    end

    def add_link( link )
      @links.push( link )
    end

    def add_form( form )
      @forms.push( form )
    end

    def add_frame( frame )
      @frames.push( frame )
    end

    def add_table( table )
      @tables.push( table )
    end

    def add_image( image )
      @images.push( image )
    end

    def freeze
      @forms.each do |f|
        f.freeze
      end
    end

=begin
--- Response#title
      return title.
=end

    def title
      @tree.find( 'title' )[0].data
    end

=begin
--- Response#find( tag )
      same as Htmlelem#find.
=end

    def find( tag )
      @tree.find( tag )
    end

=begin
--- Response#search( str )
      same as Htmlelem#search.
=end

    def search( str )
      @tree.search( str )
    end

=begin
--- Response#readlink( str )
      same as Htmlelem#find.
=end

    def readlink( str )
      @tree.readlink( str )
    end

=begin
--- Response#click( str )
      at first Response#readlink, and then, Form#submit.
=end

    def click( str, x=nil, y=nil )
      begin
        @tree.readlink( str )
      rescue ElemNotFound
        matched = 0
        @tree.find( 'input' ).each do |i|
          if i.class == InputSubmit
            matched = i.name if i.name == str || i.value == str
          elsif i.class == InputImage
            matched = i.name if i.name == str
          end
        end
        if matched == 0
          raise ElemNotFound
        else
          submit( matched, x, y )
        end
      end
    end

=begin
--- Response#submit( button=nil,x=nil,y=nil)
      short for Response.forms[0].submit.
=end

    def submit( button=nil, x=nil, y=nil )
      @forms[0].submit( button, x, y )
    end

    def push( button=nil )
      $stderr.puts "no longer support Response#push, use Response#submit"
      submit
    end

=begin
--- Response#param
      short for Response.forms[0].params
=end

    def params
      @forms[0].params
    end

=begin
--- Response#image
=end

    def image
      self.images[0]
    end

=begin
--- Response#link
      short for Response.links[0]
=end

    def link
      @links[0]
    end

=begin
--- Response#form
      short for Response.forms[0]
=end

    def form
      @forms[0]
    end

=begin
--- Response#table
      short for Response.tables[0]
=end

    def table
      @tables[0]
    end

=begin
--- Response#frame
      short for Response.frames[0]
=end

    def frame
      @frames[0]
    end

=begin
--- Response#redirect
=end

    def redirect
      if @code == '302'
        @links[0].read
      else
        raise BadOperation
      end
    end

=begin
--- Response#opens
=end

    def opens
      a = []
      re_script = %r|<!--(.*?)// *-->|m
      re_open = %r|open\( *['"]([^'"]*)['"], *['"]([^'"]*)['"] *\)|
      body_rest = response.body
      while re_script =~ body_rest
        body_rest = $'
        rest = $1
        while re_open =~ rest
          ah = {}
          ah['file'] = $1
          ah['target'] = $2
          ah['url'] = complete_url( ah['file'], @url )
          a << JSciriptOpenObject::new( ah )
          rest = $'
        end
      end
      a
    end

    def js_open_objs
      $stderr.puts "no longer support Response#js_open_objs, use Response#opens"
      opens
    end

=begin
--- Response#open
      short for Response.opens[0]
=end

    def open
      opens[0]
    end

=begin
--- Response#pbody
=end

    def pbody
      puts "\n" + '-' * 72
      puts self.format
      puts '-' * 72
    end

=begin
--- Response#format
=end

    def format
      w = MemWriter.new
      p = HTMLParser.new( AbstractFormatter.new( w ) )
      p.feed( @body )
      p.close
      w.to_s
    end

  end

  class MemWriter < NullWriter

    def initialize
      @body = ''
      @maxcol = 72
      super()
      reset
    end

    def reset
      @col = 0
      @atbreak = false
    end

    def to_s
      @body.squeeze( "\n" )
    end

    def send_paragraph(blankline)
      @body << ("\n" + "\n"*blankline)
      @col = 0
      @atbreak = false
    end

    def send_line_break
      @body << ("\n")
      @col = 0
      @atbreak = false
    end

    def send_hor_rule #(*args, **kw)
      @body << ("\n")
      @body << ('-'*@maxcol)
      @body << ("\n")
      @col = 0
      @atbreak = false
    end

    def send_literal_data(data)
      @body << (data)
      i = data.rindex("\n")
      if i
        @col = 0
        data = data[i+1..-1]
        #data = string.expandtabs(data)
        @col = @col + data.length
        @atbreak = false
      end
    end

    def send_flowing_data(data)
      return if not data
      atbreak = (@atbreak || (/^\s/ =~ data))
      col = @col
      maxcol = @maxcol
      for word in data.split
        if atbreak
          if col + word.length >= maxcol
            @body << ("\n")
            col = 0
          else
            @body << (' ')
            col = col + 1
          end
        end
        @body << (word)
        col = col + word.length
        atbreak = true
      end
      @col = col
      @atbreak = (/\s$/ =~ data)
    end

  end

end
