# Copyright (C) 2003, 2004 Gregoire Lejeune <gregoire.lejeune@free.fr>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

module Asp
  class Response
    require 'asp/common'
#    require 'asp/compile'
    
    attr_accessor :charset,:contentType,:expires,:expiresAbsolute,:bEnd
  
    public
    
    # Writes data directly to the output without any character conversion
    def binaryWrite( data )
      $stdout.binmode
      print data
    end

    # Writes a specified string to the output
    def write( data )
      print data
    end
    
    # Adds a new HTTP header and a value to the HTTP response
    def addHeader( name, value )
      @hxHeader.store( name, value )
    end
  
    # Adds a string to the end of the server log entry
    def appendToLog( message )
      @oApacheRequest.server.log_error( "[Ruby/ASP] [%d] %s", $$, message )
    end
  
    # Stops processing a script, and returns the current result
    def end
      @bEnd = 1
      goto :e
    end
  
    # ...
    def include( filename, *args )
      funcname = "i__"+rand( Time::new.to_i ).to_s+"__"+rand( Time::new.to_i ).to_s
      code = @oAspHandler.compile(filename)
      ##code = compile(filename)
      totalcode = "def " + funcname + "( args=nil )\n" + code + "\nend\n"
    
      @oAspHandler.load_code( totalcode, nil )
      if args.length == 0 
        xEvalData = "@oAspHandler." + funcname + "( nil )"
        eval( xEvalData.untaint ).untaint
      else
        xEvalData = "@oAspHandler." + funcname + "( args )"
        eval( xEvalData.untaint ).untaint
      end
    end

    alias :writeFile :include

    # ...
    def trapInclude( filename, *args )
      funcname = "i__"+rand( Time::new.to_i ).to_s+"__"+rand( Time::new.to_i ).to_s
      code = @oAspHandler.compile(filename)
      ##code = compile(filename)
      totalcode = "def " + funcname + "( args=nil )\n" + code + "\nend\n"

      if args.length == 0 
        o = Asp::Eval::new( totalcode, funcname, nil )
      else
        o = Asp::Eval::new( totalcode, funcname, args )
      end
      
      return o.get
    end
  
    # Redirects the user to a different URL
    def redirect( url )
      self.addHeader( "location", url )
      @oApacheRequest.status = 302
    end
    
    # Sets a cookie value. If the cookie does not exist, it will be created, and take the value that is specified
    #   $Response.cookies('name', 'value');
    #   --> Set-Cookie: name=value; path=/
    # 
    #   $Response.cookies("Test", "data1", "test value");
    #   $Response.cookies("Test", "data2", "more test");      
    #   $Response.cookies("Test", "Expires", &HTTP::Date::time2str(time+86400)); 
    #   $Response.cookies("Test", "Secure", 1);               
    #   $Response.cookies("Test", "Path", "/");
    #   $Response.cookies("Test", "Domain", "host.com");
    #   --> Set-Cookie:Test=data1=test%20value&data2=more%20test; \
    #              expires=Fri, 23 Apr 1999 07:19:52 GMT; \
    #              path=/; domain=host.com; secure
    def cookies( name, key, value=nil )
      if @hvCookies[name].nil? == true
        @hvCookies[name] = Hash::new
      end
      
      if value.nil? == false 
        @hvCookies[name][key] = String( value )
      else
        # we are assigning cookie with name the value of key
        @hvCookies[name]['Value'] = String( key )
      end 
    end

    # Specifies whether to buffer the page output or not
    def buffer
      ## -> bool
    end

    # Gets or sets a value indicating whether to buffer output and send it after the entire page is finished processing.
    def bufferOutput
      ## -> bool
    end

    # Gets the caching policy (expiration time, privacy, vary clauses) of a Web page.
    def cache
      ## -> System.Web.HttpCachePolicy
    end

    # Sets whether a proxy server can cache the output generated by ASP or not
    def cacheControl
      ## -> string
    end

    # Gets or sets the HTTP character set of the output stream.
    def contentEncoding
      ## -> System.Text.Encoding
    end

    # Gets or sets a wrapping filter object used to modify the HTTP entity body before transmission.
    def filter
      ## -> stream
    end

    # Gets a value indicating whether the client is still connected to the server.
    def isClientConnected
      ## -> bool
    end

    # Enables output of text to the outgoing HTTP response stream.
    def output
      ## -> System.IO.TextWriter
    end

    # Enables binary output to the outgoing HTTP content body.
    def outputStream
      ## -> stream
    end
  
    alias :redirectLocation :redirect

    # Specifies the value of the status line returned by the server
    def status
      ## -> string
    end

    # Gets or sets the HTTP status code of the output returned to the client.
    def statusCode
      ## -> int
    end
  
    # Gets or sets the HTTP status string of the output returned to the client.
    def statusDescription
      ## -> string
    end

    # Gets or sets a value indicating whether to send HTTP content to the client.
    def suppressContent
      ## -> bool
    end

    # Makes the validity of a cached item dependent on other items in the cache.
    def addCacheItemDependencies( lCacheKeys )
      ## -> void
    end

    # Makes the validity of a cached item dependent on another item in the cache.
    def addCacheItemDependency( xCacheKey )
      ## -> void
    end

    # Adds a group of file names to the collection of file names on which the current response is dependent.
    def addFileDependencies( lFilenames )
      ## -> void
    end

    # Adds a single file name to the collection of file names on which the current response is dependent.
    def addFileDependency( xFilename )
      ## -> void
    end

    # TODO
    def appendCookie( cookie )
      ## -> void
    end

    # Adds an HTTP header to the output stream.
    def appendHeader( xName, xValue )
      ## -> void
    end

    # Adds a session ID to the virtual path if the session is using cookieless session state and returns the combined path. If cookieless session state is not used, ApplyAppPathModifier returns the original virtual path.
    def applyAppPathModifier( xVirtualPath )
      ## -> string
    end

    # Clears any buffered HTML output
    def clear( )
      ## -> void
    end

    alias :clearContent :clear

    # Clears all headers from the buffer stream.
    def clearHeaders( )
      ## -> void
    end

    # Closes the socket connection to a client.
    def close( )
      ## -> void
    end

    # Sends buffered HTML output immediately
    def flush( )
      ## -> void
    end

    # Appends a value to the PICS label response header
    def pics( xValue )
      ## -> void
    end

    # Static method removes from the cache all cached items associated with the specified path.
    def removeOutputCacheItem( xPath )
      ## -> void
    end

    # TODO
    def setCookie( oHttpCookie )
      ## -> void
    end

    ## -- Reserved for Ruby/ASP internal use --
    def getHeader
      xTime = nil
      if @expires != nil
        xTime = Asp::Common::time2str( Time::now + @expires )
      elsif @expiresAbsolute != nil
        xTime = Asp::Common::str2time( @expiresAbsolute )
      end
      if xTime != nil
        @hxHeader.store( "Expires", xTime )
      end
      return @hxHeader
    end
    
    def addCookiesHeader
      cookie_header = Array::new
      
      @hvCookies.each_key { |name| 
        value = Array::new
  
        #if name == "session-id"
        if name == Asp::Common::sessionID
          self.appendToLog( format( "You can't use %s for a cookie name since it is reserved for session management", Asp::Common::sessionID ) )
        else
          cookie = @hvCookies[name]
          cookie['Path'] ||= "/"
  
          cookie.each_pair { |k,v|
            case k.downcase
            when "expires"
              value[1] = "expires=#{v}"
            when "path"
              value[2] = "path=#{v}"
            when "domain"
              value[3] = "domain=#{v}"
            when "secure"
              value[4] = "secure"
            when "value"
              # we set the value later, nothing for now
            else
              if String( @hvCookies[name]['Value'].class ) != "String"
                if @hvCookies[name]['Value'].nil? == true
                  @hvCookies[name]['Value'] = Hash::new
                end
                @hvCookies[name]['Value'][k] = v
              end
            end
          }
 
          if String( @hvCookies[name]['Value'].class ) == "Hash"
            dict = Array::new
            @hvCookies[name]['Value'].each_pair { |k,v|
              dict.push( format( "%s=%s", Asp::Common::url_encode( k ), Asp::Common::url_encode( v ) ) )
            }
            value[0] = dict.join( "&" )
          else
            value[0] = Asp::Common::url_encode( @hvCookies[name]['Value'] )
          end
          value[0] = Asp::Common::url_encode( name )+"="+value[0]
          
          cookie = Array::new
          [0,1,2,3,4].each { |iCpt|
            next unless value[iCpt].nil? == false
            cookie.push( value[iCpt] )
          }
    
          cookie_header.push( cookie.join( "; " ) )
        end
      }

      return( cookie_header )
    end

    private 
    
#    def compile(filename)
#      open(filename.untaint) do |f|
#        return @compiler.compile(f.read)
#      end
#    end   

    def initialize( r, oAspError, aspHandler )
      @oApacheRequest = r
      @oAspError = oAspError
      @oAspHandler = aspHandler

      @charset = "iso-8859-1"
      @contentType = "text/html"
      @expires = nil
      @expiresAbsolute = nil
      @bEnd = 0
      @hxHeader = Hash::new
      @hvCookies = Hash::new

#      @compiler = AspCompiler.new
#      @compiler.put_cmd = 'print'
    end
  
  end

  class Eval
    def initialize( code, funcname, args=nil )
      code += "\n" + funcname + "( args )\n" 
      @data = ""
      keep = $defout
      $defout = self
      eval( code.untaint ).untaint
      $defout = keep
    end

    def write( x )
      @data += String( x )
    end
  
    def get
      @data
    end
  end

end
