class PSD
  module Node
    # Collection of methods to help in traversing the PSD tree structure.
    module Ancestry
      # Returns the root node
      def root
        return self if is_root?
        return parent.root
      end

      # Is this node the root node?
      def root?
        self.is_a?(PSD::Node::Root)
      end
      alias :is_root? :root?

      # Returns all ancestors in the path of this node. This
      # does NOT return the root node.
      def ancestors
        return [] if parent.nil? || parent.is_root?
        return parent.ancestors + [parent]
      end

      # Does this node have any children nodes?
      def has_children?
        children.length > 0
      end

      # Inverse of has_children?
      def childless?
        !has_children?
      end

      # Returns all sibling nodes including the current node. Can also
      # be thought of as all children of the parent of this node.
      def siblings
        return [] if parent.nil?
        parent.children
      end

      def next_sibling
        return nil if parent.nil?
        index = siblings.index(self)
        siblings[index + 1]
      end

      def prev_sibling
        return nil if parent.nil?
        index = siblings.index(self)
        siblings[index - 1]
      end

      # Does this node have any siblings?
      def has_siblings?
        siblings.length > 1
      end

      # Is this node the only descendant of its parent?
      def only_child?
        siblings.length == 1
      end

      # Recursively get all descendant nodes, not including this node.
      def descendants
        children.map(&:subtree).flatten
      end

      # Same as descendants, except it includes this node.
      def subtree
        [self] + descendants
      end

      # Depth from the root node. Root depth is 0.
      def depth
        return ancestors.length + 1
      end

      def path(as_array = false)
        path = (ancestors.map(&:name) + [name])
        as_array ? path : path.join('/')
      end

      def method_missing(method, *args, &block)
        test = /^(.+)_(layers|groups)$/.match(method)
        if test
          m = self.respond_to?(test[1]) ? test[1] : "#{test[1]}s"
          self.send(m).select &method("#{test[2]}_only")
        else
          super
        end
      end

      private

      def layers_only(d); d.layer?; end
      def groups_only(d); d.group?(false); end
    end
  end
end
