#include "MantidKernel/Strings.h"
#include "MantidKernel/UnitLabel.h"

#include <Poco/StringTokenizer.h>
#include <Poco/Path.h>

#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include <cmath>
#include <fstream>

using std::size_t;

namespace Mantid {
namespace Kernel {
namespace Strings {

//------------------------------------------------------------------------------------------------
/** Loads the entire contents of a text file into a string
 *
 * @param filename :: full path to file
 * @return string contents of text file
 */
std::string loadFile(const std::string &filename) {
  std::string retVal;
  std::string str;
  std::ifstream in;
  in.open(filename.c_str());
  getline(in, str);
  while (in) {
    retVal += str + "\n";
    getline(in, str);
  }
  in.close();
  return retVal;
}

//------------------------------------------------------------------------------------------------
/** Return a string with all matching occurence-strings
 *
 * @param input :: input string
 * @param find_what :: will search for all occurences of this string
 * @param replace_with :: ... and replace them with this.
 * @return the modified string.
 */
std::string replace(const std::string &input, const std::string &find_what,
                    const std::string &replace_with) {
  std::string output = input;
  std::string::size_type pos = 0;
  while ((pos = output.find(find_what, pos)) != std::string::npos) {
    output.erase(pos, find_what.length());
    output.insert(pos, replace_with);
    pos += replace_with.length();
  }
  return output;
}

/**
 * Return a string with all occurrences of the characters in the input replaced
 * by the replace string
 * @param input :: The input string to perform the replacement on
 * @param charStr :: Each occurrence of ANY character in this string within the
 * input string will be replaced by substitute
 * @param substitute :: A substitute string
 * @return A new string with the characters replaced
 */
MANTID_KERNEL_DLL std::string replaceAll(const std::string &input,
                                         const std::string &charStr,
                                         const std::string &substitute) {
  std::string replaced;
  replaced.reserve(input.size());
  std::string::const_iterator iend = input.end();
  for (std::string::const_iterator itr = input.begin(); itr != iend; ++itr) {
    char inputChar = (*itr);
    if (charStr.find_first_of(inputChar) == std::string::npos) // Input string
                                                               // char is not
                                                               // one of those
                                                               // to be replaced
    {
      replaced.push_back(inputChar);
    } else {
      replaced.append(substitute);
    }
  }
  return replaced;
}

//------------------------------------------------------------------------------------------------
/**
 * Function to convert a number into hex
 * output (and leave the stream un-changed)
 * @param OFS :: Output stream
 * @param n :: Integer to convert
 * \todo Change this to a stream operator
 */
void printHex(std::ostream &OFS, const int n) {
  std::ios_base::fmtflags PrevFlags = OFS.flags();
  OFS << "Ox";
  OFS.width(8);
  OFS.fill('0');
  hex(OFS);
  OFS << n;
  OFS.flags(PrevFlags);
  return;
}

//------------------------------------------------------------------------------------------------
/**
 * Removes the multiple spaces in the line
 * @param Line :: Line to process
 * @return String with single space components
 */
std::string stripMultSpc(const std::string &Line) {
  std::string Out;
  int spc(1);
  int lastReal(-1);
  for (unsigned int i = 0; i < Line.length(); i++) {
    if (Line[i] != ' ' && Line[i] != '\t' && Line[i] != '\r' &&
        Line[i] != '\n') {
      lastReal = i;
      spc = 0;
      Out += Line[i];
    } else if (!spc) {
      spc = 1;
      Out += ' ';
    }
  }
  lastReal++;
  if (lastReal < static_cast<int>(Out.length()))
    Out.erase(lastReal);
  return Out;
}

//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
/**
 *  Checks that as least cnt letters of
 *  works is part of the string. It is currently
 *  case sensitive. It removes the Word if found
 *  @param Line :: Line to process
 *  @param Word :: Word to use
 * @param cnt :: Length of Word for significants [default =4]
 *  @retval 1 on success (and changed Line)
 *  @retval 0 on failure
 */
int extractWord(std::string &Line, const std::string &Word, const int cnt) {
  if (Word.empty())
    return 0;

  size_t minSize(cnt > static_cast<int>(Word.size()) ? Word.size() : cnt);
  std::string::size_type pos = Line.find(Word.substr(0, minSize));
  if (pos == std::string::npos)
    return 0;
  // Pos == Start of find
  size_t LinePt = minSize + pos;
  for (; minSize < Word.size() && LinePt < Line.size() &&
             Word[minSize] == Line[LinePt];
       LinePt++, minSize++) {
  }

  Line.erase(pos, LinePt - (pos - 1));
  return 1;
}

//------------------------------------------------------------------------------------------------
/** If a word ends with a number representing a positive integer, return
 * the value of that int.
 *
 * @param word :: string possibly ending in a number
 * @return the number, or -1 if it does not end in a number
 */
int endsWithInt(const std::string &word) {
  if (word.empty())
    return -1;
  int out = -1;
  // Find the index of the first number in the string (if any)
  int firstNumber = int(word.size());
  for (int i = int(word.size()) - 1; i >= 0; i--) {
    char c = word[i];
    if ((c > '9') || (c < '0'))
      break;
    firstNumber = i;
  }
  // Convert the string of decimals to an int
  if (firstNumber < int(word.size())) {
    std::string part = word.substr(firstNumber, word.size() - firstNumber);
    if (!convert(part, out))
      return -1;
  }
  return out;
}

//------------------------------------------------------------------------------------------------
/**
 *  Check to see if S is the same as the
 *  first part of a phrase. (case insensitive)
 *  @param S :: string to check
 *  @param fullPhrase :: complete phrase
 *  @return 1 on success
 */
int confirmStr(const std::string &S, const std::string &fullPhrase) {
  const size_t nS(S.length());
  const size_t nC(fullPhrase.length());
  if (nS > nC || nS == 0)
    return 0;
  for (size_t i = 0; i < nS; i++)
    if (S[i] != fullPhrase[i])
      return 0;
  return 1;
}

//------------------------------------------------------------------------------------------------
/**
 *  Gets a line and determine if there is addition component to add
 *  in the case of a very long line.
 *  @param fh :: input stream to get line
 *  @param Out :: string up to last 'tab' or ' '
 *  @param Excess :: string after 'tab or ' '
 *  @param spc :: number of char to try to read
 *  @retval 1 :: more line to be found
 *  @retval -1 :: Error with file
 *  @retval 0  :: line finished.
 */
int getPartLine(std::istream &fh, std::string &Out, std::string &Excess,
                const int spc) {
  // std::string Line;
  if (fh.good()) {
    char *ss = new char[spc + 1];
    const int clen = static_cast<int>(spc - Out.length());
    fh.getline(ss, clen, '\n');
    ss[clen + 1] = 0; // incase line failed to read completely
    Out += static_cast<std::string>(ss);
    delete[] ss;
    // remove trailing comments
    std::string::size_type pos = Out.find_first_of("#!");
    if (pos != std::string::npos) {
      Out.erase(pos);
      return 0;
    }
    if (fh.gcount() == clen - 1) // cont line
    {
      pos = Out.find_last_of("\t ");
      if (pos != std::string::npos) {
        Excess = Out.substr(pos, std::string::npos);
        Out.erase(pos);
      } else
        Excess.erase(0, std::string::npos);
      fh.clear();
      return 1;
    }
    return 0;
  }
  return -1;
}

//------------------------------------------------------------------------------------------------
/**
 *  Removes all spaces from a string
 * except those with in the form '\ '
 *  @param CLine :: Line to strip
 *  @return String without space
 */
std::string removeSpace(const std::string &CLine) {
  std::string Out;
  char prev = 'x';
  for (unsigned int i = 0; i < CLine.length(); i++) {
    if (!isspace(CLine[i]) || prev == '\\') {
      Out += CLine[i];
      prev = CLine[i];
    }
  }
  return Out;
}

//------------------------------------------------------------------------------------------------
/**
 *  Reads a line from the stream of max length spc.
 *  Trailing comments are removed. (with # or ! character)
 *  @param fh :: already open file handle
 *  @param spc :: max number of characters to read
 *  @return String read.
 */
std::string getLine(std::istream &fh, const int spc) {
  char *ss = new char[spc + 1];
  std::string Line;
  if (fh.good()) {
    fh.getline(ss, spc, '\n');
    ss[spc] = 0; // incase line failed to read completely
    Line = ss;
    // remove trailing comments
    std::string::size_type pos = Line.find_first_of("#!");
    if (pos != std::string::npos)
      Line.erase(pos);
  }
  delete[] ss;
  return Line;
}

/**
 * Peek at a line without extracting it from the stream
 */
std::string peekLine(std::istream &fh) {
  std::string str;
  std::streampos pos = fh.tellg();
  getline(fh, str);
  fh.seekg(pos);

  return strip(str);
}

//------------------------------------------------------------------------------------------------
/**
 *  Determines if a string is only spaces
 *  @param A :: string to check
 *  @return 1 on an empty string , 0 on failure
 */
int isEmpty(const std::string &A) {
  std::string::size_type pos = A.find_first_not_of(" \t");
  return (pos != std::string::npos) ? 0 : 1;
}

//------------------------------------------------------------------------------------------------
/**
 *  removes the string after the comment type of
 *  '$ ' or '!' or '#  '
 *  @param A :: String to process
 */
void stripComment(std::string &A) {
  std::string::size_type posA = A.find("$ ");
  std::string::size_type posB = A.find("# ");
  std::string::size_type posC = A.find("!");
  if (posA > posB)
    posA = posB;
  if (posA > posC)
    posA = posC;
  if (posA != std::string::npos)
    A.erase(posA, std::string::npos);
  return;
}

//------------------------------------------------------------------------------------------------
/**
 *  Returns the string from the first non-space to the
 *  last non-space
 * @param A :: string to process
 * @return shortened string
 */
std::string fullBlock(const std::string &A) { return strip(A); }

//------------------------------------------------------------------------------------------------
/** Returns the string from the first non-space to the
 *  last non-space
 *  @param A :: string to process
 *  @return shortened string
 */
std::string strip(const std::string &A) {
  std::string result(A);
  boost::trim(result);
  return result;
}

/**
* Return true if the line is to be skipped (starts with #).
* @param line :: The line to be checked
* @return True if the line should be skipped
*/
bool skipLine(const std::string &line) {
  // Empty or comment
  return (line.empty() || boost::starts_with(line, "#"));
}

//------------------------------------------------------------------------------------------------
/**
 *  Write out the line in the limited form for MCNPX
 *  ie initial line from 0->72 after that 8 to 72
 *  (split on a space or comma)
 *  @param Line :: full MCNPX line
 *  @param OX :: ostream to write to
 */
void writeMCNPX(const std::string &Line, std::ostream &OX) {
  const int MaxLine(72);
  std::string::size_type pos(0);
  std::string X = Line.substr(0, MaxLine);
  std::string::size_type posB = X.find_last_of(" ,");
  int spc(0);
  while (posB != std::string::npos &&
         static_cast<int>(X.length()) >= MaxLine - spc) {
    pos += posB + 1;
    if (!isspace(X[posB]))
      posB++;
    const std::string Out = X.substr(0, posB);
    if (!isEmpty(Out)) {
      if (spc)
        OX << std::string(spc, ' ');
      OX << X.substr(0, posB) << std::endl;
    }
    spc = 8;
    X = Line.substr(pos, MaxLine - spc);
    posB = X.find_last_of(" ,");
  }
  if (!isEmpty(X)) {
    if (spc)
      OX << std::string(spc, ' ');
    OX << X << std::endl;
  }
  return;
}

//------------------------------------------------------------------------------------------------
/**
 *  Splits the sting into parts that are space delminated.
 *  @param Ln :: line component to strip
 *  @return vector of components
 */
std::vector<std::string> StrParts(std::string Ln) {
  std::vector<std::string> Out;
  std::string Part;
  while (section(Ln, Part))
    Out.push_back(Part);
  return Out;
}

/**
 * Splits a string into key value pairs and returns them as a map. Whitespace
 * between separators is ignored
 * @param input :: The string containing the key/values
 * @param keyValSep :: The separator that splits a key and value [default: "="]
 * @param listSep :: The separator that splits elements of the list [default:
 * ","]
 * @returns A map of keys->values
 */
std::map<std::string, std::string>
splitToKeyValues(const std::string &input, const std::string &keyValSep,
                 const std::string &listSep) {
  std::map<std::string, std::string> keyValues;
  const int splitOptions =
      Poco::StringTokenizer::TOK_IGNORE_EMPTY + Poco::StringTokenizer::TOK_TRIM;
  Poco::StringTokenizer listSplitter(input, listSep);
  for (auto iter = listSplitter.begin(); iter != listSplitter.end(); ++iter) {
    Poco::StringTokenizer keyValSplitter(*iter, keyValSep, splitOptions);
    if (keyValSplitter.count() == 2) {
      keyValues[keyValSplitter[0]] = keyValSplitter[1];
    }
  }

  return keyValues;
}

//------------------------------------------------------------------------------------------------
/**
 *  Converts a vax number into a standard unix number
 *  @param A :: float number as read from a VAX file
 *  @return float A in IEEE little eindian format
 */
float getVAXnum(const float A) {
  union {
    // char a[4];
    float f;
    int ival;
  } Bd;

  int sign, expt, fmask;
  float frac;
  double onum;

  Bd.f = A;
  sign = (Bd.ival & 0x8000) ? -1 : 1;
  expt = ((Bd.ival & 0x7f80) >> 7); // reveresed ?
  if (!expt)
    return 0.0;

  fmask = ((Bd.ival & 0x7f) << 16) | ((Bd.ival & 0xffff0000) >> 16);
  expt -= 128;
  fmask |= 0x800000;
  frac = (float)fmask / 0x1000000;
  onum = frac * static_cast<float>(sign) * pow(2.0, expt);
  return (float)onum;
}

//------------------------------------------------------------------------------------------------
/**
 *  Takes a character string and evaluates
 *  the first [typename T] object. The string is then
 *  erase upt to the end of number.
 *  The diffierence between this and section is that
 *  it allows trailing characters after the number.
 *  @param out :: place for output
 *  @param A :: string to process
 *  @return 1 on success 0 on failure
 */
template <typename T> int sectPartNum(std::string &A, T &out) {
  if (A.empty())
    return 0;

  std::istringstream cx;
  T retval;
  cx.str(A);
  cx.clear();
  cx >> retval;
  const std::streamoff xpt = cx.tellg();
  if (xpt < 0)
    return 0;
  A.erase(0, static_cast<unsigned int>(xpt));
  out = retval;
  return 1;
}

//------------------------------------------------------------------------------------------------
/**
 *  Takes a character string and evaluates
 *  the first [typename T] object. The string is then filled with
 *  spaces upto the end of the [typename T] object
 *  @param out :: place for output
 *  @param cA :: char array for input and output.
 *  @return 1 on success 0 on failure
 */
template <typename T> int section(char *cA, T &out) {
  if (!cA)
    return 0;
  std::string sA(cA);
  const int item(section(sA, out));
  if (item) {
    strcpy(cA, sA.c_str());
    return 1;
  }
  return 0;
}

/**
 *  takes a character string and evaluates
 *  the first \<T\> object. The string is then filled with
 *  spaces upto the end of the \<T\> object
 *  @param out :: place for output
 *  @param A :: string for input and output.
 *  @return 1 on success 0 on failure
 */
template <typename T> int section(std::string &A, T &out) {
  if (A.empty())
    return 0;
  std::istringstream cx;
  T retval;
  cx.str(A);
  cx.clear();
  cx >> retval;
  if (cx.fail())
    return 0;
  const std::streamoff xpt = cx.tellg();
  const char xc = static_cast<char>(cx.get());
  if (!cx.fail() && !isspace(xc))
    return 0;
  A.erase(0, static_cast<unsigned int>(xpt));
  out = retval;
  return 1;
}

/**
 *  Takes a character string and evaluates
 *  the first [T] object. The string is then filled with
 *  spaces upto the end of the [T] object.
 *  This version deals with MCNPX numbers. Those
 *  are numbers that are crushed together like
 *  - 5.4938e+04-3.32923e-6
 *  @param out :: place for output
 *  @param A :: string for input and output.
 *  @return 1 on success 0 on failure
 */
template <typename T> int sectionMCNPX(std::string &A, T &out) {
  if (A.empty())
    return 0;
  std::istringstream cx;
  T retval;
  cx.str(A);
  cx.clear();
  cx >> retval;
  if (!cx.fail()) {
    const std::streamoff xpt = cx.tellg();
    if (xpt < 0) {
      return 0;
    }
    const char xc = static_cast<char>(cx.get());
    if (!cx.fail() && !isspace(xc) && (xc != '-' || xpt < 5)) {
      return 0;
    }
    A.erase(0, static_cast<unsigned int>(xpt));
    out = retval;
    return 1;
  }
  return 0;
}

//------------------------------------------------------------------------------------------------
/**
 *  Takes a character string and evaluates
 *  the first [typename T] object. The string is then
 *  erase upto the end of number.
 *  The diffierence between this and convert is that
 *  it allows trailing characters after the number.
 *  @param out :: place for output
 *  @param A :: string to process
 *  @retval number of char read on success
 *  @retval 0 on failure
 */
template <typename T> int convPartNum(const std::string &A, T &out) {
  if (A.empty())
    return 0;
  std::istringstream cx;
  T retval;
  cx.str(A);
  cx.clear();
  cx >> retval;
  // If we have reached the end of the stream, then we need to clear the error
  // as
  // it will cause the tellg() call to return -1.  Not pretty but works for now.
  cx.clear();
  const std::streamoff xpt = cx.tellg();
  if (xpt < 0)
    return 0;
  out = retval;
  return static_cast<int>(xpt);
}

//------------------------------------------------------------------------------------------------
/**
 *  Convert a string into a value
 *  @param A :: string to pass
 *  @param out :: value if found
 *  @return 0 on failure 1 on success
 */
template <typename T> int convert(const std::string &A, T &out) {
  if (A.empty())
    return 0;
  std::istringstream cx;
  T retval;
  cx.str(A);
  cx.clear();
  cx >> retval;
  if (cx.fail())
    return 0;
  const char clast = static_cast<char>(cx.get());
  if (!cx.fail() && !isspace(clast))
    return 0;
  out = retval;
  return 1;
}

//------------------------------------------------------------------------------------------------
/**
 *  Convert a string into a value
 *  @param A :: string to pass
 *  @param out :: value if found
 *  @return 0 on failure 1 on success
 */
template <typename T> int convert(const char *A, T &out) {
  // No string, no conversion
  if (!A)
    return 0;
  std::string Cx = A;
  return convert(Cx, out);
}

//------------------------------------------------------------------------------------------------
/** Convert a number or other value to a string
 *
 * @param value :: templated value (e.g. a double) to convert
 * @return a string
 */
template <typename T> std::string toString(const T &value) {
  std::ostringstream mess;
  mess << value;
  return mess.str();
}

/**
 * This assumes that the vector is sorted.
 *
 * @param value :: templated value (only works for integer types) to convert.
 * @return A reduced string representation.
 */
template <typename T> std::string toString(const std::vector<T> &value) {
  std::ostringstream mess;
  auto it = value.begin();
  auto last = value.end();
  T start;
  T stop;
  for (; it != last; ++it) {
    start = *(it);
    stop = start;
    for (; it != last; ++it) {
      if (it + 1 == last)
        break;
      else if ((stop + static_cast<T>(1)) == *(it + 1))
        stop = *(it + 1);
      else
        break;
    }
    mess << start;
    if (start != stop)
      mess << "-" << stop;
    if (it + 1 != last)
      mess << ",";
  }
  return mess.str();
}

template <typename T> std::string toString(const std::set<T> &value) {
  return toString(std::vector<T>(value.begin(), value.end()));
}

template <> MANTID_KERNEL_DLL std::string toString(const UnitLabel &value) {
  return value;
}

//------------------------------------------------------------------------------------------------
/**
 *  Write out the three vectors into a file of type dc 9
 *  @param step :: parameter to control x-step (starts from zero)
 * @param Y :: Y column
 *  @param Fname :: Name of the file
 *  @return 0 on success and -ve on failure
 */
template <template <typename T, typename A> class V, typename T, typename A>
int writeFile(const std::string &Fname, const T &step, const V<T, A> &Y) {
  V<T, A> Ex; // Empty vector
  V<T, A> X;  // Empty vector
  for (unsigned int i = 0; i < Y.size(); i++)
    X.push_back(i * step);

  return writeFile(Fname, X, Y, Ex);
}

//------------------------------------------------------------------------------------------------
/**
 *  Write out the three vectors into a file of type dc 9
 *  @param X :: X column
 *  @param Y :: Y column
 *  @param Fname :: Name of the file
 *  @return 0 on success and -ve on failure
 */
template <template <typename T, typename A> class V, typename T, typename A>
int writeFile(const std::string &Fname, const V<T, A> &X, const V<T, A> &Y) {
  V<T, A> Ex;                        // Empty vector/list
  return writeFile(Fname, X, Y, Ex); // don't need to specific ??
}

//------------------------------------------------------------------------------------------------
/**
 *  Write out the three container into a file with
 *  column free-formated data in the form :
 *   - X  Y Err
 *   If Err does not exist (or is short) 0.0 is substituted.
 *  @param X :: X column
 *  @param Y :: Y column
 *  @param Err :: Err column
 *  @param Fname :: Name of the file
 *  @return 0 on success and -ve on failure
 */
template <template <typename T, typename A> class V, typename T, typename A>
int writeFile(const std::string &Fname, const V<T, A> &X, const V<T, A> &Y,
              const V<T, A> &Err) {
  const size_t Npts(X.size() > Y.size() ? Y.size() : X.size());
  const size_t Epts(Npts > Err.size() ? Err.size() : Npts);

  std::ofstream FX;

  FX.open(Fname.c_str());
  if (!FX.good())
    return -1;

  FX << "# " << Npts << " " << Epts << std::endl;
  FX.precision(10);
  FX.setf(std::ios::scientific, std::ios::floatfield);
  typename V<T, A>::const_iterator xPt = X.begin();
  typename V<T, A>::const_iterator yPt = Y.begin();
  typename V<T, A>::const_iterator ePt = (Epts ? Err.begin() : Y.begin());

  // Double loop to include/exclude a short error stack
  size_t eCount = 0;
  for (; eCount < Epts; eCount++) {
    FX << (*xPt) << " " << (*yPt) << " " << (*ePt) << std::endl;
    ++xPt;
    ++yPt;
    ++ePt;
  }
  for (; eCount < Npts; eCount++) {
    FX << (*xPt) << " " << (*yPt) << " 0.0" << std::endl;
    ++xPt;
    ++yPt;
  }
  FX.close();
  return 0;
}

//------------------------------------------------------------------------------------------------
/**
 *  Call to read in various values in position x1,x2,x3 from the
 *  line. Note to avoid the dependency on crossSort this needs
 *  to be call IN ORDER
 *  @param Line :: string to read
 *  @param Index :: Indexes to read
 *  @param Out :: OutValues [unchanged if not read]
 *  @retval 0 :: success
 *  @retval -ve on failure.
 */
template <typename T>
int setValues(const std::string &Line, const std::vector<int> &Index,
              std::vector<T> &Out) {
  if (Index.empty())
    return 0;

  if (Out.size() != Index.size())
    return -1;
  //    throw ColErr::MisMatch<int>(Index.size(),Out.size(),
  //        "Mantid::Kernel::Strings::setValues");

  std::string modLine = Line;
  std::vector<int> sIndex(Index); // Copy for sorting
  std::vector<int> OPt(Index.size());
  for (unsigned int i = 0; i < Index.size(); i++)
    OPt[i] = i;

  //  mathFunc::crossSort(sIndex,OPt);

  typedef std::vector<int>::const_iterator iVecIter;
  std::vector<int>::const_iterator sc = sIndex.begin();
  std::vector<int>::const_iterator oc = OPt.begin();
  int cnt(0);
  T value;
  std::string dump;
  while (sc != sIndex.end() && *sc < 0) {
    ++sc;
    ++oc;
  }

  while (sc != sIndex.end()) {
    if (*sc == cnt) {
      if (!section(modLine, value))
        return static_cast<int>(
            -1 - distance(static_cast<iVecIter>(sIndex.begin()), sc));
      // this loop handles repeat units
      do {
        Out[*oc] = value;
        ++sc;
        ++oc;
      } while (sc != sIndex.end() && *sc == cnt);
    } else {
      if (!section(modLine, dump))
        return static_cast<int>(
            -1 - distance(static_cast<iVecIter>(sIndex.begin()), sc));
    }
    cnt++; // Add only to cnt [sc/oc in while loop]
  }
  // Success since loop only gets here if sc is exhaused.
  return 0;
}

//-----------------------------------------------------------------------------------------------
/** Get a word from a line and strips spaces
 *
 * @param in :: stream input
 * @param consumeEOL :: set to true to remove the new lines at the end of the
 *line
 * @return a string with the word read in
 */
std::string getWord(std::istream &in, bool consumeEOL) {
  std::string ret;
  char nextch = 0;

  // Skip leading spaces
  do {
    nextch = static_cast<char>(in.get());
  } while (nextch == ' ');

  // Return an empty string on EOL; optionally consume it
  if (nextch == '\n' || nextch == '\r') {
    if (!consumeEOL) {
      in.unget();
    } else if ((nextch == '\n' && in.peek() == '\r') ||
               (nextch == '\r' && in.peek() == '\n')) {
      // Handle CRLF and LFCR on Unix by consuming both
      in.ignore();
    }

    return ret;
  } else {      // Non-EOL and non-space character
    in.unget(); // Put it back on stream
  }

  // Get next word if stream is still valid
  if (in.good())
    in >> ret;

  // Optionally consume EOL character
  if (consumeEOL) {
    nextch = static_cast<char>(in.get());

    // Handle CRLF and LFCR on Unix by consuming both
    if (nextch == '\n' || nextch == '\r') {
      if ((nextch == '\n' && in.peek() == '\r') ||
          (nextch == '\r' && in.peek() == '\n')) {
        in.ignore();
      }
    } else {
      in.unget();
    }
  }

  return ret;
}

//-----------------------------------------------------------------------------------------------
/** Read up to the eol
 *
 * @param in :: stream input
 * @param ConsumeEOL :: set to true to remove the new lines at the end of the
 *line
 */
void readToEndOfLine(std::istream &in, bool ConsumeEOL) {
  while (in.good() && getWord(in, false).length() > 0)
    getWord(in, false);
  if (!ConsumeEOL)
    return;
  getWord(in, true);
}

/**
 * Function parses a path, placed into input string "path" and returns vector of
 * the folders contributed into the path
 *  @param path :: the string containing input path, found in path string, if
 * they are separated by \ or / symbols.
 *  Treats special symbols, if defined in the input string as path-es
 *  returns 0 for empty input string
 *  @param path_components :: holder for the individual folders in the path
 *  used to generate path in hdf file, so the resulting path has to obey hdf
 * constrains;
 */
size_t split_path(const std::string &path,
                  std::vector<std::string> &path_components) {
  if (path.empty()) {
    path_components.resize(0);
    return 0;
  }
  // convert Windows path into the unix one
  std::string working_path(path);
  for (size_t i = 0; i < path.size(); i++) {
    if (working_path[i] < 0x20 || working_path[i] > 0x7E)
      working_path[i] = '_';
    if (working_path[i] == '\\')
      working_path[i] = '/';
    if (working_path[i] == ' ')
      working_path[i] = '_';
  }

  // path start with relative character, and we need to convert it into full
  // path
  if (path[0] == '.') {
    // get absolute path using working directory as base;
    Poco::Path absol;
    absol = absol.absolute();
    working_path = absol.toString(Poco::Path::PATH_UNIX) + working_path;
  }
  // as poco splt using regular expressions is doing some rubbish, we need to do
  // split manually
  // code below implements perl split(/\\//,string) commamd. (\\ has been
  // converted to / above)
  std::list<int64_t> split_pos;
  split_pos.push_back(-1);
  size_t path_size = working_path.size();
  for (size_t i = 0; i < path_size; i++) {
    if (working_path[i] == '/') {
      split_pos.push_back(i);
    }
  }
  split_pos.push_back(path_size);
  // allocate target vector to keep folder structure and fill it in
  size_t n_folders = split_pos.size() - 1;
  path_components.resize(n_folders);
  std::list<int64_t>::iterator it1 = split_pos.begin();
  std::list<int64_t>::iterator it2 = it1;
  ++it2;

  int64_t ic(0);
  for (; it2 != split_pos.end(); ++it2) {
    std::string folder = working_path.substr(*it1 + 1, *it2 - *it1 - 1);
    if (folder.empty() ||
        (folder.size() == 1 &&
         folder == ".")) { // skip self-references and double slashes;
      it1 = it2;
      continue;
    }
    // reprocess up-references;
    if (folder == "..") {
      if (folder.size() != 2)
        throw(std::invalid_argument("path contains wrong path group"));
      ic--;
      if (ic < 0)
        throw(std::invalid_argument("path contains relative references to a "
                                    "folder outside of the seach tree"));
      it1 = it2;
      continue;
    }
    path_components[ic] = folder;
    ic++;
    it1 = it2;
  }

  n_folders = size_t(ic);
  path_components.resize(n_folders);
  return n_folders;
}

/**
 * Function checks if the candidate is the member of the group
 * @param group :: vector of string to check
 * @param candidate :: the string which has to be checked against the group
 * @returns :: number of the candidate in the input vector of strings if the
 candidate belongs to the group
               or -1 if it does not.
               Returns the number of the first maching entry in the group if
 there are duplicated entries in the group
 */
int isMember(const std::vector<std::string> &group,
             const std::string &candidate) {
  int num(-1);
  for (size_t i = 0; i < group.size(); i++) {
    if (candidate.compare(group[i]) == 0) {
      num = int(i);
      return num;
    }
  }
  return num;
}

/**
 * Parses a number range, e.g. "1,4-9,54-111,3,10", to the vector containing all
 * the elements
 * within the range.
 * @param str String to parse
 * @param elemSep String with characters used to separate elements (',')
 * @param rangeSep String with characters used to separate range start and end
 * ('-')
 * @return A vector with all the elements from the range
 */
std::vector<int> parseRange(const std::string &str, const std::string &elemSep,
                            const std::string &rangeSep) {
  typedef Poco::StringTokenizer Tokenizer;

  boost::shared_ptr<Tokenizer> elements;

  if (elemSep.find(' ') != std::string::npos) {
    // If element separator contains space character it's a special case,
    // because in that case
    // it is allowed to have element separator inside a range, e.g. "4 - 5", but
    // not "4,-5"

    // Space is added so that last empty element of the "1,2,3-" is not ignored
    // and we can
    // spot the error. Behaviour is changed in Poco 1.5 and this will not be
    // needed.
    Tokenizer ranges(str + " ", rangeSep, Tokenizer::TOK_TRIM);
    std::string new_str =
        join(ranges.begin(), ranges.end(), rangeSep.substr(0, 1));

    elements = boost::make_shared<Tokenizer>(
        new_str, elemSep, Tokenizer::TOK_IGNORE_EMPTY | Tokenizer::TOK_TRIM);
  } else {
    elements = boost::make_shared<Tokenizer>(
        str, elemSep, Tokenizer::TOK_IGNORE_EMPTY | Tokenizer::TOK_TRIM);
  }

  std::vector<int> result;

  // Estimation of the resulting number of elements
  result.reserve(elements->count());

  for (Tokenizer::Iterator it = elements->begin(); it != elements->end();
       it++) {
    // See above for the reason space is added
    Tokenizer rangeElements(*it + " ", rangeSep, Tokenizer::TOK_TRIM);

    size_t noOfRangeElements = rangeElements.count();

    // A single element
    if (noOfRangeElements == 1) {
      int element;
      if (convert(rangeElements[0], element) != 1)
        throw std::invalid_argument("Invalid element: " + *it);
      result.push_back(element);
    }
    // A pair
    else if (noOfRangeElements == 2) {
      int start, end;

      if (convert(rangeElements[0], start) != 1 ||
          convert(rangeElements[1], end) != 1)
        throw std::invalid_argument("Invalid range: " + *it);

      if (start >= end)
        throw std::invalid_argument("Range boundaries are reversed: " + *it);

      for (int i = start; i <= end; i++)
        result.push_back(i);
    }
    // Error - e.g. "--""
    else {
      throw std::invalid_argument("Multiple range separators: " + *it);
    }
  }

  return result;
}

/**
* Extract a string until an EOL character is reached. There are 3 scenarios that
* we need to deal with
* 1) Windows-style  - CRLF ('\\r\\n');
* 2) Unix-style     - LF ('\\n');
* 3) Old MAC style  - CR ('\\r').
* This function will give the string preceding any of these sequences
* @param is :: The input stream to read from
* @param str :: The output string to use to accumulate the line
* @returns A reference to the input stream
*/
std::istream &extractToEOL(std::istream &is, std::string &str) {
  // Empty the string
  str = "";
  char c('\0');
  while (is.get(c)) {
    if (c == '\r') {
      c = static_cast<char>(is.peek());
      if (c == '\n') {
        // Extract this as well
        is.get();
      }
      break;
    } else if (c == '\n') {
      break;
    } else {
      // Accumulate the string
      str += c;
    }
  }
  return is;
}

/// \cond TEMPLATE
template MANTID_KERNEL_DLL int section(std::string &, double &);
template MANTID_KERNEL_DLL int section(std::string &, float &);
template MANTID_KERNEL_DLL int section(std::string &, int &);
template MANTID_KERNEL_DLL int section(std::string &, std::string &);

template MANTID_KERNEL_DLL int sectPartNum(std::string &, double &);
template MANTID_KERNEL_DLL int sectPartNum(std::string &, int &);
template MANTID_KERNEL_DLL int sectionMCNPX(std::string &, double &);

template MANTID_KERNEL_DLL int convert(const std::string &, double &);
template MANTID_KERNEL_DLL int convert(const std::string &, float &);
template MANTID_KERNEL_DLL int convert(const std::string &, std::string &);
template MANTID_KERNEL_DLL int convert(const std::string &, int &);
template MANTID_KERNEL_DLL int convert(const std::string &, std::size_t &);
template MANTID_KERNEL_DLL int convert(const std::string &, bool &);
template MANTID_KERNEL_DLL int convert(const char *, std::string &);
template MANTID_KERNEL_DLL int convert(const char *, double &);
template MANTID_KERNEL_DLL int convert(const char *, int &);
template MANTID_KERNEL_DLL int convert(const char *, std::size_t &);
template MANTID_KERNEL_DLL int convert(const char *, bool &);

template MANTID_KERNEL_DLL std::string toString(const double &value);
template MANTID_KERNEL_DLL std::string toString(const float &value);
template MANTID_KERNEL_DLL std::string toString(const int &value);
template MANTID_KERNEL_DLL std::string toString(const uint16_t &value);
template MANTID_KERNEL_DLL std::string
toString(const size_t &value); // Matches uint64_t on Linux 64 & Win 64
#if defined(__APPLE__) || (defined(_WIN32) && !defined(_WIN64)) ||             \
    (defined(__GNUC__) && !defined(__LP64__)) // Mac or 32-bit compiler
template MANTID_KERNEL_DLL std::string toString(const uint64_t &value);
#endif
template MANTID_KERNEL_DLL std::string toString(const std::string &value);

template MANTID_KERNEL_DLL std::string toString(const std::vector<int> &value);

// this block should generate the vector ones as well
template MANTID_KERNEL_DLL std::string toString(const std::set<int> &value);
template MANTID_KERNEL_DLL std::string toString(const std::set<int16_t> &value);
template MANTID_KERNEL_DLL std::string toString(
    const std::set<size_t> &value); // Matches uint64_t on Linux 64 & Win 64
#if defined(__APPLE__) || (defined(_WIN32) && !defined(_WIN64)) ||             \
    (defined(__GNUC__) && !defined(__LP64__)) // Mac or 32-bit compiler
template MANTID_KERNEL_DLL std::string
toString(const std::set<uint64_t> &value);
#endif

template MANTID_KERNEL_DLL int convPartNum(const std::string &, double &);
template MANTID_KERNEL_DLL int convPartNum(const std::string &, int &);

template MANTID_KERNEL_DLL int
setValues(const std::string &, const std::vector<int> &, std::vector<double> &);

template MANTID_KERNEL_DLL int writeFile(const std::string &, const double &,
                                         const std::vector<double> &);
template MANTID_KERNEL_DLL int writeFile(const std::string &,
                                         const std::vector<double> &,
                                         const std::vector<double> &,
                                         const std::vector<double> &);
template MANTID_KERNEL_DLL int writeFile(const std::string &,
                                         const std::vector<double> &,
                                         const std::vector<double> &);
template MANTID_KERNEL_DLL int writeFile(const std::string &,
                                         const std::vector<float> &,
                                         const std::vector<float> &);
template MANTID_KERNEL_DLL int writeFile(const std::string &,
                                         const std::vector<float> &,
                                         const std::vector<float> &,
                                         const std::vector<float> &);
/// \endcond TEMPLATE

} // NAMESPACE Strings

} // NAMESPACE Kernel

} // NAMESPACE Mantid