module Vfs
  class Path < String
    def initialize path = '/', options = {}
      if options[:skip_normalization]
        super path
        @probably_dir = options[:probably_dir]
      else
        Path.validate! path
        path, probably_dir = Path.normalize_to_string path
        raise "invalid path '#{path}' (you are outside of the root)!" unless path
        super path
        @probably_dir = probably_dir
      end
    end

    def + path = ''
      path = path.to_s
      Path.validate! path, false

      if Path.absolute?(path)
        Path.normalize path
      elsif path.empty?
        self
      else
        Path.normalize "#{self}#{'/' unless self == '/'}#{path}"
      end
    end

    def parent
      self + '..'
    end

    def probably_dir?
      !!@probably_dir
    end

    def name
      unless @name
        root = self[0..0]
        @name ||= split('/').last || root
      end
      @name
    end

    class << self
      def absolute? path
        path =~ /^[\/~\/]|^\.$|^\.\//
      end

      def valid? path, forbid_relative = true, &block
        result, err = if forbid_relative and !absolute?(path)
          [false, "path must be started with '/', or '.'"]
        elsif path =~ /.+\/~$|.+\/$|\/\.$/
          [false, "path can't be ended with '/', '/~', or '/.'"]
        elsif path =~ /\/\/|\/~\/|\/\.\//
          [false, "path can't include '/./', '/~/', '//' combinations!"]
        # elsif path =~ /.+[~]|\/\.\//
        #   [false, "'~', or '.' can be present only at the begining of string"]
        else
          [true, nil]
        end

        block.call err if block and !result and err
        result
      end

      def normalize path
        path, probably_dir = normalize_to_string path
        unless path
          nil
        else
          Path.new(path, skip_normalization: true, probably_dir: probably_dir)
        end
      end

      def validate! path, forbid_relative = true
        valid?(path, forbid_relative){|error| raise "invalid path '#{path}' (#{error})!"}
      end

      def normalize_to_string path
        root = path[0..0]
        result, probably_dir = [], false

        parts = path.split('/')[1..-1]
        if parts
          parts.each do |part|
            if part == '..' and (root != '.' or (root == '.' and result.size > 0))
              return nil, false unless result.size > 0
              result.pop
              probably_dir ||= true
            else
              result << part
              probably_dir &&= false
            end
          end
        end
        normalized_path = result.join('/')

        probably_dir ||= true if normalized_path.empty?

        return "#{root}#{'/' unless root == '/' or normalized_path.empty?}#{normalized_path}", probably_dir
      end
    end

    # protected
    #   def delete_dir_mark
    #     path = path.to_s.sub(%r{/$}, '')
    #   end
    #
    #
    #   def root_path? path
    #     path =~ /^[#{ROOT_SYMBOLS}]$/
    #   end
    #
    #   def split_path path
    #     path.split(/#{ROOT_SYMBOLS}/)
    #   end
    #
    #   def dir_mark? path
    #     path =~ %r{/$}
    #   end
  end
end