//===- 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:: namespace. 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>

namespace mcld {
namespace sys  {
namespace fs   {

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

/** \class Path
 *  \brief Path provides an abstraction for the path to a file or directory in
 *   the operating system's filesystem.
 *
 *  FIXME: current Path library only support UTF-8 chararcter set.
 *
 */
class Path
{
public:
#if defined(MCLD_ON_WIN32)
  typedef wchar_t                            ValueType;
#else
  typedef char                               ValueType;
#endif
  typedef std::basic_string<ValueType>       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(); }

  std::string string() const;

  // -----  decomposition  ----- //
  Path parent_path() 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                        //
//--------------------------------------------------------------------------//

/// is_separator - is the given character a separator of a path.
// @param value a character
// @result true if \a value is a path separator character on the host OS
//bool status_known(FileStatus f) { return f.type() != StatusError; }

bool is_separator(char value);

bool exists(const Path &pPath);

bool is_directory(const Path &pPath);


std::ostream &operator<<(std::ostream& pOS, const Path& pPath);

std::istream &operator>>(std::istream& pOS, Path& pPath);

llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Path &pPath);


//--------------------------------------------------------------------------------------//
//                     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