# Web::Controller::PolyState
# Copyright(c) 2004 MoonWolf <moonwolf@moonwolf.com>
require 'web/escape'
require 'web/response'
require 'web/cookie'

module Web
  module Controller
    class PolyState
      def initialize
      end
      
      def setup(opt={})
        @opt = opt
        @states = {}
      end
      
      def teardown
      end
      
      def cookie_name
        'sessionid'
      end
      
      def cookie_path
        nil
      end
      
      def cookie_domain
        nil
      end

      def session_expires
        Time.now + 600
      end
      
      def run(req)
        begin
          state = 'default'
          session_id = req.cookies[cookie_name]
          if session_id
            session_id = session_id.value
            ps = Web::Persistent.new(session_id,@opt)
            ps.transaction {
              if ps['state']
                state = ps['state']
                if ps['expire']<Time.now
                  state = 'default'
                end
              end
              ps['state']  = state
              ps['expire'] = session_expires
            }
          else
            ps = Web::Persistent.new(nil,@opt)
            ps.transaction {
              ps['state']  = state
              ps['expire'] = session_expires
            }
            session_id = ps.persistent_id
          end
          
          rsp = Web::Response.new
          cookie         = Web::Cookie.new(cookie_name, session_id)
          cookie.path    = cookie_path    if cookie_path
          cookie.domain  = cookie_domain  if cookie_domain
          rsp = @states[state].run(req, rsp, ps, self)
          rsp.cookies[cookie_name] = cookie
          rsp
        rescue StandardError,ScriptError
          error($!.message + "\n" + $!.backtrace.join("\n"))
        end
      end

      def error(msg)
        rsp = Web::Response.new
        
        msg = h(msg).gsub(/\n/,"<br>")
        rsp.content_type = "text/html"
        rsp.write <<EOS
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>ERROR #{self.class}</title>
<link rel="INDEX"  href="./">
</head>
<body>
  <h1>ERROR #{self.class}</h1>
  <pre>#{msg}</pre>
</body>
</html>
EOS
        rsp
      end
      
      def h(str)
        Web::escapeHTML(str)
      end
      
      def u(str)
        Web::escape(str.to_s)
      end
      
      class State
        def run_mode(req)
          rm = req.get('rm')
          if rm == nil
            req.form.keys.each {|k|
              if k=~/^rm_/
                rm = $'
                break
              end
            }
          end
          rm ||= 'start'
          return rm
        end
        
        def run(req, rsp, ps, main)
          begin
            rm = 'do_' + run_mode(req)
            send(rm, req, rsp, ps, main)
            rsp
          rescue StandardError,ScriptError
            error($!.message + "\n" + $!.backtrace.join("\n"))
          end
        end

        def transit(ps,state)
          ps.transaction {
            ps['state'] = state
          }
        end
        
        def error(msg)
          rsp = Web::Response.new
          
          msg = h(msg).gsub(/\n/,"<br>")
          rsp.content_type = "text/html"
          rsp.write <<EOS
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>ERROR #{self.class}</title>
<link rel="INDEX"  href="./">
</head>
<body>
  <h1>ERROR #{self.class}</h1>
  <pre>#{msg}</pre>
</body>
</html>
EOS
          rsp
        end
        
        def h(str)
          Web::escapeHTML(str)
        end
        
        def u(str)
          Web::escape(str.to_s)
        end
        
        def qs(hash)
          url = '?'
          url << hash.collect {|key,value|
            u(key.to_s) << '=' << u(value.to_s)
          }.sort.join(';')
          h(url)
        end
        
        def get_param(req, name, type, must = true)
          raise ArgumentError if must && !req.has_key?(name)
          value = req[name]
          return nil if !value and !must
          value = value.read if value.respond_to?(:read)
          case type
          when :text
            value
          when :flag
            case value.trim.downcase
            when /\A(?:on|yes|y|true|1)\z/
              true
            when /\A(?:off|no|n|false|0)\z/
              false
            else
              raise ArgumentError
            end
          when :integer
            Integer(value)
          when Array
            raise ArgumentError unless type.include?(value)
            value
          when Hash
            type[value]
          when Regexp
            m = type.match(value)
            raise ArgumentError unless value
            m
          end
        end
      end # State
    end # PolyState
  end # Controller
end # Web
