//===- Path.h -------------------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This file declares the mcld::sys::fs::Path. It follows TR2/boost
// filesystem (v3), but modified to remove exception handling and the
// path class.
//===----------------------------------------------------------------------===//
#ifndef MCLD_PATH_H
#define MCLD_PATH_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif

#include <llvm/Support/raw_ostream.h>
#include <mcld/Config/Config.h>

#include <iosfwd>
#include <functional>
#include <string>
#include <locale>

namespace mcld {
namespace sys  {
namespace fs   {

#if defined(MCLD_ON_WIN32)
const char preferred_separator = '/';
const char separator = '/';
#else
const char preferred_separator = '/';
const char separator = '/';
#endif

const char colon = ':';
const char dot = '.';

/** \class Path
 *  \brief Path provides an abstraction for the path to a file or directory in
 *   the operating system's filesystem.
 */
class Path
{
public:
  typedef char                               ValueType;
  typedef std::string                        StringType;

public:
  Path();
  Path(const ValueType* s);
  Path(const StringType &s);
  Path(const Path& pCopy);
  virtual ~Path();

  // -----  assignments  ----- //
  template <class InputIterator>
  Path& assign(InputIterator begin, InputIterator end);
  Path& assign(const StringType &s);
  Path& assign(const ValueType* s, unsigned int length);

  //  -----  appends  ----- //
  template <class InputIterator>
  Path& append(InputIterator begin, InputIterator end);
  Path& append(const Path& pPath);

  //  -----  observers  ----- //
  bool empty() const;

  bool isFromRoot() const;
  bool isFromPWD() const;

  const StringType& native() const { return m_PathName; }
  StringType&       native()       { return m_PathName; }

  const ValueType* c_str() const
  { return m_PathName.c_str(); }

  // -----  decomposition  ----- //
  Path parent_path() const;
  Path filename() const;
  Path stem() const;
  Path extension() const;

  // -----  generic form observers  ----- //
  StringType generic_string() const;
  bool canonicalize();

public:
  StringType::size_type m_append_separator_if_needed();
  void m_erase_redundant_separator(StringType::size_type sep_pos);

protected:
  StringType m_PathName;
};

bool operator==(const Path& pLHS,const Path& pRHS);
bool operator!=(const Path& pLHS,const Path& pRHS);
Path operator+(const Path& pLHS, const Path& pRHS);

//===----------------------------------------------------------------------===//
// Non-member Functions
//===----------------------------------------------------------------------===//
bool exists(const Path &pPath);

bool is_directory(const Path &pPath);

template <class Char, class Traits>
inline std::basic_ostream<Char, Traits>&
operator<<(std::basic_ostream<Char, Traits>& pOS, const Path& pPath)
{
  return pOS << pPath.native();
}

template <class Char, class Traits>
inline std::basic_istream<Char, Traits>&
operator>>(std::basic_istream<Char, Traits>& pOS, Path& pPath)
{
  return pOS >> pPath.native();
}

inline llvm::raw_ostream&
operator<<(llvm::raw_ostream& pOS, const Path& pPath)
{
  return pOS << pPath.native();
}

//===----------------------------------------------------------------------===//
// class path member template implementation
//===----------------------------------------------------------------------===//
template <class InputIterator>
Path& Path::assign(InputIterator begin, InputIterator end)
{
  m_PathName.clear();
  if (begin != end)
    m_PathName.append<InputIterator>(begin, end);
  return *this;
}

template <class InputIterator>
Path& Path::append(InputIterator begin, InputIterator end)
{
  if (begin == end)
    return *this;
  StringType::size_type sep_pos(m_append_separator_if_needed());
  m_PathName.append<InputIterator>(begin, end);
  if (sep_pos)
    m_erase_redundant_separator(sep_pos);
  return *this;
}

} // namespace of fs
} // namespace of sys
} // namespace of mcld

//===----------------------------------------------------------------------===//
// STL compatible functions
//===----------------------------------------------------------------------===//
namespace std {

template<>
struct less<mcld::sys::fs::Path> : public binary_function<mcld::sys::fs::Path,
                                                         mcld::sys::fs::Path,
                                                         bool>
{
  bool operator() (const mcld::sys::fs::Path& pX,const mcld::sys::fs::Path& pY) const {
    if (pX.generic_string().size() < pY.generic_string().size())
      return true;
    return (pX.generic_string() < pY.generic_string());
  }
};

} // namespace of std

#endif