//===- InputTree.h --------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_INPUTTREE_H_
#define MCLD_INPUTTREE_H_

#include "mcld/ADT/BinTree.h"
#include "mcld/ADT/TypeTraits.h"
#include "mcld/MC/Input.h"
#include "mcld/Support/Path.h"

#include <string>

namespace mcld {

/** \class template<typename Traits, typename Iterator>
 * PolicyIterator<mcld::Input>
 *  \brief PolicyIterator<mcld::Input> is a partially specific PolicyIterator
 */
template <typename Traits, typename IteratorType>
class PolicyIterator<mcld::Input, Traits, IteratorType>
    : public PolicyIteratorBase<Input, Traits, IteratorType> {
 public:
  typedef PolicyIterator<Input, Traits, IteratorType> Self;
  typedef PolicyIteratorBase<Input, Traits, IteratorType> Base;
  typedef PolicyIterator<Input, typename Traits::nonconst_traits, IteratorType>
      iterator;
  typedef PolicyIterator<Input, typename Traits::const_traits, IteratorType>
      const_iterator;

 public:
  PolicyIterator() : Base() {}

  PolicyIterator(const iterator& X) : Base(X.m_pNode) {}

  explicit PolicyIterator(NodeBase* X) : Base(X) {}

  virtual ~PolicyIterator() {}

  bool isGroup() const { return !Base::hasData() && !Base::isRoot(); }

  Self& operator++() {
    IteratorType::advance();
    // skip the Group node
    while (isGroup())
      IteratorType::advance();
    return *this;
  }

  Self operator++(int) {
    Self tmp(*this);
    IteratorType::advance();
    // skip the Group node
    while (isGroup())
      IteratorType::advance();
    return tmp;
  }
};

template <>
class BinaryTree<Input> : public BinaryTreeBase<Input> {
 public:
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
  typedef Input value_type;
  typedef value_type* pointer;
  typedef value_type& reference;
  typedef const value_type* const_pointer;
  typedef const value_type& const_reference;

  typedef BinaryTree<Input> Self;
  typedef TreeIterator<value_type, NonConstTraits<value_type> > iterator;
  typedef TreeIterator<value_type, ConstTraits<value_type> > const_iterator;

  typedef PolicyIterator<value_type, NonConstTraits<value_type>, DFSIterator>
      dfs_iterator;
  typedef PolicyIterator<value_type, ConstTraits<value_type>, DFSIterator>
      const_dfs_iterator;
  typedef PolicyIterator<value_type, NonConstTraits<value_type>, BFSIterator>
      bfs_iterator;
  typedef PolicyIterator<value_type, ConstTraits<value_type>, BFSIterator>
      const_bfs_iterator;

 protected:
  typedef Node<value_type> node_type;

 public:
  // -----  constructors and destructor  ----- //
  BinaryTree() : BinaryTreeBase<Input>() {}

  ~BinaryTree() {}

  // -----  iterators  ----- //
  bfs_iterator bfs_begin() {
    bfs_iterator it = bfs_iterator(BinaryTreeBase<Input>::m_Root.node.left);
    if (it.isGroup())
      ++it;
    return it;
  }

  bfs_iterator bfs_end() {
    return bfs_iterator(BinaryTreeBase<Input>::m_Root.node.right);
  }

  const_bfs_iterator bfs_begin() const {
    const_bfs_iterator it =
        const_bfs_iterator(BinaryTreeBase<Input>::m_Root.node.left);
    if (it.isGroup())
      ++it;
    return it;
  }

  const_bfs_iterator bfs_end() const {
    return const_bfs_iterator(BinaryTreeBase<Input>::m_Root.node.right);
  }

  dfs_iterator dfs_begin() {
    dfs_iterator it = dfs_iterator(BinaryTreeBase<Input>::m_Root.node.left);
    if (it.isGroup())
      ++it;
    return it;
  }

  dfs_iterator dfs_end() {
    return dfs_iterator(BinaryTreeBase<Input>::m_Root.node.right);
  }

  const_dfs_iterator dfs_begin() const {
    const_dfs_iterator it =
        const_dfs_iterator(BinaryTreeBase<Input>::m_Root.node.left);
    if (it.isGroup())
      ++it;
    return it;
  }

  const_dfs_iterator dfs_end() const {
    return const_dfs_iterator(BinaryTreeBase<Input>::m_Root.node.right);
  }

  iterator root() { return iterator(&(BinaryTreeBase<Input>::m_Root.node)); }

  const_iterator root() const {
    // FIXME: provide the iterater constructors for constant NodeBase instead of
    // using const_cast
    return const_iterator(
        const_cast<NodeBase*>(&BinaryTreeBase<Input>::m_Root.node));
  }

  iterator begin() {
    iterator it = iterator(BinaryTreeBase<Input>::m_Root.node.left);
    return it;
  }

  iterator end() { return iterator(BinaryTreeBase<Input>::m_Root.node.right); }

  const_iterator begin() const {
    return const_iterator(BinaryTreeBase<Input>::m_Root.node.left);
  }

  const_iterator end() const {
    return const_iterator(BinaryTreeBase<Input>::m_Root.node.right);
  }

  // ----- modifiers  ----- //
  /// join - create a leaf node and merge it in the tree.
  //  This version of join determines the direction on compilation time.
  //  @param DIRECT the direction of the connecting edge of the parent node.
  //  @param position the parent node
  //  @param value the value being pushed.
  template <size_t DIRECT>
  BinaryTree& join(TreeIteratorBase& pPosition, const Input& value) {
    node_type* node = BinaryTreeBase<Input>::createNode();
    node->data = const_cast<Input*>(&value);

    if (pPosition.isRoot())
      pPosition.hook<TreeIteratorBase::Leftward>(node);
    else
      pPosition.hook<DIRECT>(node);

    return *this;
  }

  /// merge - merge the tree
  //  @param DIRECT the direction of the connecting edge of the parent node.
  //  @param position the parent node
  //  @param the tree being joined.
  //  @return the joined tree
  template <size_t DIRECT>
  BinaryTree& merge(TreeIteratorBase& pPosition, BinaryTree& pTree) {
    if (this == &pTree)
      return *this;

    if (!pTree.empty()) {
      pPosition.hook<DIRECT>(pTree.m_Root.node.left);
      BinaryTreeBase<Input>::m_Root.summon(pTree.BinaryTreeBase<Input>::m_Root);
      BinaryTreeBase<Input>::m_Root.delegate(pTree.m_Root);
      pTree.m_Root.node.left = pTree.m_Root.node.right = &pTree.m_Root.node;
    }
    return *this;
  }
};

/** \class InputTree
 *  \brief InputTree is the input tree to contains all inputs from the
 *  command line.
 *
 *  InputTree, of course, is uncopyable.
 *
 *  @see Input
 */
class InputTree : public BinaryTree<Input> {
 private:
  typedef BinaryTree<Input> BinTreeTy;

 public:
  enum Direction {
    Inclusive = TreeIteratorBase::Leftward,
    Positional = TreeIteratorBase::Rightward
  };

  typedef BinaryTree<Input>::iterator iterator;
  typedef BinaryTree<Input>::const_iterator const_iterator;

 public:
  /** \class Mover
   *  \brief Mover provides the interface for moving iterator forward.
   *
   *  Mover is a function object (functor). @ref Mover::move moves
   *  iterator forward in certain direction. @ref Mover::connect
   *  connects two nodes of the given iterators togather.
   */
  struct Mover {
    virtual void connect(TreeIteratorBase& pFrom, NodeBase* pTo) const = 0;
    virtual void move(TreeIteratorBase& pNode) const = 0;
    virtual ~Mover() {}
  };

  /** \class Succeeder
   *  \brief class Succeeder moves the iterator afterward.
   */
  struct Succeeder : public Mover {
    void connect(TreeIteratorBase& pFrom, NodeBase* pTo) const {
      pFrom.hook<Positional>(pTo);
    }

    void move(TreeIteratorBase& pNode) const { pNode.move<Positional>(); }
  };

  /** \class Includer
   *  \brief class Includer moves the iterator downward.
   */
  struct Includer : public Mover {
    void connect(TreeIteratorBase& pFrom, NodeBase* pTo) const {
      pFrom.hook<Inclusive>(pTo);
    }

    void move(TreeIteratorBase& pNode) const { pNode.move<Inclusive>(); }
  };

 public:
  static Succeeder Afterward;
  static Includer Downward;

 public:
  using BinTreeTy::merge;

  // -----  modify  ----- //
  template <size_t DIRECT>
  InputTree& enterGroup(TreeIteratorBase pRoot);

  template <size_t DIRECT>
  InputTree& insert(TreeIteratorBase pRoot, Input& pInput);

  InputTree& merge(TreeIteratorBase pRoot,
                   const Mover& pMover,
                   InputTree& pTree);

  InputTree& insert(TreeIteratorBase pRoot, const Mover& pMover, Input& pInput);

  InputTree& enterGroup(TreeIteratorBase pRoot, const Mover& pMover);
};

bool isGroup(const InputTree::iterator& pos);
bool isGroup(const InputTree::const_iterator& pos);
bool isGroup(const InputTree::dfs_iterator& pos);
bool isGroup(const InputTree::const_dfs_iterator& pos);
bool isGroup(const InputTree::bfs_iterator& pos);
bool isGroup(const InputTree::const_bfs_iterator& pos);

}  // namespace mcld

//===----------------------------------------------------------------------===//
// template member functions
//===----------------------------------------------------------------------===//
template <size_t DIRECT>
mcld::InputTree& mcld::InputTree::enterGroup(mcld::TreeIteratorBase pRoot) {
  BinTreeTy::node_type* node = createNode();

  if (pRoot.isRoot())
    pRoot.hook<TreeIteratorBase::Leftward>(node);
  else
    pRoot.hook<DIRECT>(node);

  return *this;
}

template <size_t DIRECT>
mcld::InputTree& mcld::InputTree::insert(mcld::TreeIteratorBase pRoot,
                                         mcld::Input& pInput) {
  BinTreeTy::node_type* node = createNode();
  node->data = &pInput;

  if (pRoot.isRoot())
    pRoot.hook<TreeIteratorBase::Leftward>(node);
  else
    pRoot.hook<DIRECT>(node);

  return *this;
}

#endif  // MCLD_INPUTTREE_H_