#--
# =============================================================================== 
# Copyright (c) 2005, Christopher Kleckner
# All rights reserved
#
# This file is part of the Rio library for ruby.
#
# Rio 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.
#
# Rio 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 Rio; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# =============================================================================== 
#++
#
# To create the documentation for Rio run the command
#  rake rdoc
# from the distribution directory. Then point your browser at the 'doc/rdoc' directory.
#
# Suggested Reading
# * RIO::Doc::SYNOPSIS
# * RIO::Doc::INTRO
# * RIO::Doc::HOWTO
# * RIO::Rio
#
# <b>Rio is pre-alpha software. 
# The documented interface and behavior is subject to change without notice.</b>


require 'rio/exception/state'
require 'rio/context'
require 'rio/context/methods'
require 'rio/ext'
require 'rio/symantics'
require 'rio/filter'

module RIO

                   
  module State #:nodoc: all
    # = State
    # the abstract state from which all are derived
    # this level handles 
    # * some basic house keeping methods
    # * the methods to communicate with the rio object
    # * the state changing mechanism
    # * and some basic error handling stubs
    class Base
      KIOSYMS = [:gets,:open,:readline,:readlines,:chop,:to_a,:putc,:puts,:print,:printf,:split,
                 :=~,:===,:==,:eql?,:sub,:sub!,:gsub,:gsub!,:load]
      @@kernel_cleaned ||= KIOSYMS.each { |sym| undef_method(sym) } 
    end 
    
    class Base
      attr_accessor :rl
      attr_accessor :ioh
      attr_accessor :try_state
      attr_accessor :cx

      attr_accessor :handled_by

      # Context handling
      include Cx::Methods

      include RIO::Ext::Cx

      
      def initialize()
        @rl = @cx = @ioh = nil
#        @handled_by = self.class.to_s
      end

      def initialize_copy(*args)
        #p callstr('initialize_copy',args[0].inspect)
        super
        @rl = @rl.clone unless @rl.nil?
        @cx = @cx.clone unless @cx.nil?
        @ioh = @ioh.clone unless @ioh.nil?
      end

      def self.new_r(riorl)
        new.init(riorl,Cx::Vars.new( { 'closeoneof' => true, 'closeoncopy' => true } ))
      end
      def clone_rio()
        cp = Rio.new(self.rl)
        cp.cx = self.cx
        cp.ioh = self.ioh.clone unless self.ioh.nil?
        cp
      end
      def init(riorl,cntx,iohandle=nil)
        @rl = riorl
        @cx = cntx
        @ioh = iohandle
#        raise Exception::FailedCheck.new(self) unless check?
        self
      end
      
      def self.new_other(other)
        new.copy_state(other)
      end

      def copy_state(other)
        init(other.rl,other.cx,other.ioh)
      end

      alias :ior :ioh
      alias :iow :ioh



      # Section: State Switching

      # the method for changing states
      # it's job is create an instance of the next state
      # and change the value in the handle that is shared with the rio object
      def become(new_class,*args)
        p "become : #{self.class.to_s} => #{new_class.to_s} (#{self.mode?})" if $trace_states
#
        return self if new_class == self.class

        begin
          try_state[new_class,*args]
        rescue Exception::FailedCheck => ex
          p "not a valid "+new_class.to_s+": "+ex.to_s+" '"+self.to_s+"'"
          raise
        end
      end

      def method_missing_trace_str(sym,*args)
        "missing: "+self.class.to_s+'['+self.to_url+']'+'.'+sym.to_s+'('+args.join(',')+')'
      end

      def method_missing(sym,*args,&block)
        p method_missing_trace_str(sym,*args) if $trace_states

        obj = when_missing(sym,*args)
        raise RuntimeError,"when_missing returns nil" if obj.nil?
        obj.__send__(sym,*args,&block) #unless obj == self
      end
      
      def when_missing(sym,*args) gofigure(sym,*args) end


      def base_state() Factory.instance.reset_state(@rl) end

      def softreset 
        #p "softreset(#{self.class}) => #{self.base_state}"
        cx['retrystate'] = nil
        become(self.base_state) 
      end
      def retryreset 
        #p "retryreset(#{self.class}) => #{self.base_state}"
        become(self.base_state) 
      end
      def reset
        softreset()
      end

      # Section: Error Handling
      def gofigure(sym,*args)
        cs = "#{sym}("+args.map(&:to_s).join(',')+")"
        msg = "Go Figure! rio('#{self.to_s}').#{cs} Failed"
        error(msg,sym,*args)
      end

      def error(emsg,sym,*args)
        require 'rio/state/error'
        Error.error(emsg,self,sym,*args)
      end

      def to_rl() self.rl.rl end


      extend Forwardable
      def_instance_delegators(:rl,:path,:to_s,:fspath,:urlpath,:length)

      def ==(other) @rl == other end
      def ===(other) self == other end
      def =~(other) other =~ self.to_str end
      def to_url() @rl.url end
      def to_uri() @rl.uri end
      alias to_str to_s

      def hash() @rl.to_s.hash end
      def eql?(other) @rl.to_s.eql?(other.to_s) end

      def stream?() false end

      # Section: Rio Interface
      # gives states the ability to create new rio objects
      # (should this be here???)
      def new_rio(arg0,*args,&block)
        #return arg0 if arg0.nil? # watch out for dir.read! if you remove this line
        Rio.rio(arg0,*args,&block)
      end
      def new_rio_cx(*args)
        n = new_rio(*args)
        n.cx = self.cx.bequeath
        n
      end
      def ensure_rio(arg0)
        return arg0 if arg0.kind_of?(::RIO::Rio)
        new_rio(arg0)
      end
      def ensure_cmd_rio(arg)
        case arg
        when ::String then new_rio(:cmdio,arg)
        when ::Fixnum then new_rio(arg)
        when Rio then arg.clone
        else ensure_rio(arg)
        end
      end

      include Symantics

      def callstr(func,*args)
        self.class.to_s+'['+self.to_url+']'+'.'+func.to_s+'('+args.join(',')+')'
      end

    end

  end

end # module RIO