#--
# ===========================================================================
# Copyright (c) 2005-2012 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
# =========================================================================== 
#++
#


require 'rio/fwd'
#require 'rio/impl/path'
require 'rio/ops/create'
require 'rio/ops/construct'
module RIO
  module Ops
    module Path
      autoload :Create, 'rio/ops/create'
    end
  end
end

module RIO
  module Ops #:nodoc: all
    module Path
      module Test
        def blockdev?(*args) fs.blockdev?(self.to_s,*args) end
        def chardev?(*args) fs.chardev?(self.to_s,*args) end
        def directory?(*args) fs.directory?(self.to_s,*args) end
        def exist?(*args) fs.exist?(self.to_s,*args) end
        def file?(*args) 
          fs.file?(self.to_s,*args) 
        end
        def pipe?(*args) fs.pipe?(self.to_s,*args) end
        def socket?(*args) fs.socket?(self.to_s,*args) end
        def symlink?(*args) fs.symlink?(self.to_s,*args) end
        alias :dir? :directory?
        def open?() not self.closed? end
        def closed?() self.ioh.nil? end
      end
      module Status
        include Test
        def fnmatch(*args) fs.fnmatch(self.to_s,*args) end
        def fnmatch?(*args) fs.fnmatch?(self.to_s,*args) end
        def ftype(*args) fs.ftype(self.to_s,*args) end
        def stat(*args) fs.stat(self.to_s,*args) end
        def atime(*args) fs.atime(self.to_s,*args) end
        def ctime(*args) fs.ctime(self.to_s,*args) end
        def mtime(*args) fs.mtime(self.to_s,*args) end
        def executable?(*args) fs.executable?(self.to_s,*args) end 
        def executable_real?(*args) fs.executable_real?(self.to_s,*args) end 
        def readable?(*args) fs.readable?(self.to_s,*args) end 
        def readable_real?(*args) fs.readable_real?(self.to_s,*args) end 
        def writable?(*args) fs.writable?(self.to_s,*args) end 
        def writable_real?(*args) fs.writable_real?(self.to_s,*args) end
        def sticky?(*args) fs.sticky?(self.to_s,*args) end 
        def owned?(*args) fs.owned?(self.to_s,*args) end 
        def grpowned?(*args) fs.grpowned?(self.to_s,*args) end 
        def setgid?(*args) fs.setgid?(self.to_s,*args) end 
        def setuid?(*args) fs.setuid?(self.to_s,*args) end
        def size(*args) fs.size(self.to_s,*args) end 
        def size?(*args) fs.size?(self.to_s,*args) end 
        def zero?(*args) fs.zero?(self.to_s,*args) end
        def root?(*args) fs.root?(self.to_s) end

      end
      module URI
        def abs(base=nil)
          if base.nil?
            nrio = new_rio(rl.abs)
            nrio
          else
            #new_rio(rl,{:base => ensure_rio(base).abs.to_uri}).abs
            brio = ensure_rio(base)
            new_rio(rl.abs(ensure_rio(base).to_s))
          end
        end
        def absolute?
          #p "ops/path.rb absolute?"
          rl.absolute?
        end
        alias :abs? :absolute?
        #def abs?
        #  p "ops/path.rb abs?"
        #  rl.abs?
        #end
        def route_from(other)
          this_uri = rl.uri.abs
          other_uri = ensure_rio(other).rl.uri.abs
          new_rio(this_uri.route_from(other_uri))
        end
        def rel(other=nil)
          if other.nil?
            base = self.abs.dirname
          else
            new_rio(self.rl.abs.route_from(ensure_rio(other).rl.abs))
          end
          base = other.nil? ? self.abs : ensure_rio(other).dup
          base += '/' if base.directory? and base.to_s[-1] != '/'
          route_from(base)
        end
        def route_to(other)
          new_rio(rl.abs.route_to(ensure_rio(other).rl.abs))
        end
        def merge(other)
          new_rio(rl.merge(ensure_rio(other).rl))
        end
        def base()
          #p "BASE: " + rl.class.to_s
          new_rio(rl.base())
        end
        def setbase(b)
          rl.base(b)
          self
        end
        extend Forwardable
        extend Fwd
        fwd :rl,:scheme,:host,:user,:password
        fwd :rl,:query,:fragment,:userinfo
        fwd :rl,:typecode
        fwd :rl,:authority

      end
      module Query
        def normalize
          rtn_rio {
            uri.normalize
          }
        end

        def expand_path(*args)
          args[0] = args[0].to_s unless args.empty?
          new_rio(fs.expand_path(self.to_s,*args))
        end
        def extname(*args) 
          en = fs.extname(rl.path_no_slash,*args) 
          (en.empty? ? nil : en)
        end
        def splitpath()
          require 'rio/to_rio'
          sparts,bparts = self.rl.split
          parts = []
          (0...sparts.length).each do |n|
            parts << new_rio(sparts[n], {:base => bparts[n]})
          end
          parts.extend(ToRio::Array)
          #parts.each do |part|
          #  p "part=#{part.uri} abs=#{part.abs.uri}"
          #end
          # map to rios and extend the array with to_array
          #parts.map { |arl| new_rio(arl) }.extend(ToRio::Array)
          #parts.map { |arl| new_rio(arl) }
        end
        def basename(*args)
          unless args.empty?
            ex = args[0] || self.extname
            self.ext(ex)
          end
           new_rio(rl.basename(self.ext?))
        end
        def filename()
          new_rio(rl.filename)
        end
        def dirname(*args)
          new_rio(uri.dirname)
        end

        def sub(re,arg)
          rtn_rio { 
            softreset
            uri.ref.sub(re,arg.to_s)
          }
          #new_rio(softreset.to_s.sub(re,arg.to_s))
        end
        def gsub(re,arg)
          new_rio(softreset.to_s.gsub(re,arg.to_s))
        end

        def +(arg)
          new_rio(softreset.to_str + ensure_rio(arg).to_str)
        end

        private

        def _path_with_basename(arg)
          old =  rl.path
          old[0,old.length-basename.length-ext?.length]+arg.to_s+ext?
        end
        def _path_with_filename(arg)
          old =  rl.path
          old[0,old.length-filename.length]+arg.to_s
        end
        def _path_with_ext(ex)
          old =  rl.path_no_slash
          old[0,old.length-ext?.length]+ex
        end
        def _path_with_dirname(arg)
          old =  rl.path_no_slash
          arg.to_s + old[-(old.length-dirname.length+1),old.length]
        end
      end
      module Change
        def sub!(*args)
          rl.path = rl.path.sub(*args)
          softreset
        end
        
        def rename(*args,&block)
          if args.empty?
            cxx('rename',true,&block)
          else
            rtn = must_exist.rename(*args)
            return rtn.each(&block) if block_given?
            rtn
          end
        end
        def rename?() cxx?('rename') end
        def norename(arg=false,&block) nocxx('rename',&block) end
        
        def filename=(arg)
          if cx['rename']
            must_exist.filename = arg
          else
            uri.filename = arg.to_s
            softreset
          end
        end
        
        def basename=(arg)
          if cx['rename']
            must_exist.basename = arg
          else
            uri.ext = cx['ext'] if cx.has_key?('ext')
            uri.basename = arg.to_s
            softreset
          end
        end
        
        def extname=(arg)
          #p callstr('extname=',arg)
          
          if cx['rename']
            must_exist.extname = arg
          else
            uri.ext = cx['ext'] if cx.has_key?('ext')
            uri.extname = arg.to_s
            softreset
          end
        end
        
        def dirname=(arg)
          if cx['rename']
            must_exist.dirname = arg
          else
            uri.dirname = arg.to_s
            softreset
          end
        end
        
      end
    end 
  end
end
module RIO
  module Ops
    module Path
      module Empty
        include Ops::Path::Create
        include Ops::Path::URI
        include Ops::Construct
      end
      module ExistOrNot
        def symlink(d) 
          rtn_self { 
            dst = self.ensure_rio(d)
            dst /= self.filename if dst.directory?
            if self.abs?
              fs.symlink(self,dst) 
            else
              #p "symlink(#{dst.route_to(self)},#{dst})"
              fs.symlink(dst.route_to(self).to_s,dst.to_s) 
            end
            dst.reset
          } 
        end
      end
      module NonExisting
        include Test
        include Create
        include ExistOrNot
        # Rio does not consider an attempt to remove something that does not exist an error. 
        # Rio says "You called this method because you didn't want the thing to exist on the 
        # file system. After the call it doesn't exist. You're welcome"
        # 
        def delete!() softreset  end
        def delete() softreset  end
        def rmdir() softreset  end
        def rmtree() softreset  end
        def rm() softreset  end
        
      end
      module Str
        include Status
        include Query
        include Create        
        include URI
        include ExistOrNot
      end
    end
    
  end
end