/***************************************************************************
 *
 * Author: "Sjors H.W. Scheres"
 * MRC Laboratory of Molecular Biology
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * This complete copyright notice must be included in any revised version of the
 * source code. Additional authorship citations may be added, but existing
 * author citations must be preserved.
 ***************************************************************************/
/***************************************************************************
 *
 * Authors:     J.R. Bilbao-Castro (jrbcast@ace.ual.es)
 *
 * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307  USA
 *
 *  All comments concerning this program package may be sent to the
 *  e-mail address 'xmipp@cnb.csic.es'
 ***************************************************************************/

#ifndef XMIPP_STRINGS_H
#define XMIPP_STRINGS_H

#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <string.h>
#include <stdio.h>
#include "src/macros.h"

/// @defgroup StringUtilities String utilities
/// @ingroup DataLibrary
//@{

//@name String processing
//@{

/** Removes all occurrences of 'character' from the string no matter
where they are */
std::string removeChar( const std::string& str, char character );

/** Removes escaped symbols ESC+n, t, v, b, r, f, and a
 * Note that tabs are replaced by spaces.
 * */
std::string unescape( const std::string& str );

/** Best precision for a float number.
 *
 * This function returns the best precision to be used in a "printf" format if
 * this number is to fit in a given width. It returns -1 if the exponential
 * format is advised.
 *
 * @code
 * template<typename T>
 * std::ostream& operator<<(std::ostream& out, const T& val)
 * {
 *     int i,j;
 *
 *     if (val.xdim == 0)
 *         out << "NULL matrix" << std::endl;
 *     else
 *         out << std::endl;
 *
 *     T aux = ABSnD(val);
 *     int prec = bestPrecision(aux.max(), 10);
 *
 *     for (i=STARTINGY(val); i<=FINISHINGY(val); i++)
 *     {
 *         for (j=STARTINGX(val); j<=FINISHINGX(val); j++)
 *         {
 *             out << floatToString((float) val(i,j), 10, prec) << ' ';
 *         }
 *         out << std::endl;
 *     }
 *
 *     return out;
 * }
 *
 * @endcode
 */
int bestPrecision(float F, int _width);

/** String (char*) to DOUBLE conversion.
 *
 * @code
 * DOUBLE key = textToDOUBLE(firstToken(line), 1602, "Error reading key");
 * @endcode
 */
DOUBLE textToDOUBLE(const char* str,
                    int _errno = 2101,
                    std::string errmsg = "Error in textToDouble");

/** String (char*) to float conversion.
 *
 * @code
 * float key = textToFloat(firstToken(line), 1602, "Error reading key");
 * @endcode
 */
float textToFloat(const char* str,
                  int _errno = 2101,
                  std::string errmsg = "Error in textToFloat");

/** String (STL) to float conversion.
 *
 * @code
 * float key = textToFloat(str, 1602, "Error reading key");
 * @endcode
 */
inline float textToFloat(const std::string& str,
                         int _errno = 2101,
                         std::string errmsg = "Error in textToFloat")
{
    return textToFloat(str.c_str(), _errno, errmsg);
}

/** String (char*) to integer conversion.
 *
 * @code
 * int param_no = textToInteger(nextToken(), 1602, "Error reading number parameters")
 * @endcode
 */
int textToInteger(const char* str,
                  int _errno = 2102,
                  std::string errmsg = "Error in textToInteger");

/** String (STL) to integer conversion.
 *
 * @code
 * int param_no = textToInteger(str, 1602, "Error reading number parameters")
 * @endcode
 */
inline int textToInteger(const std::string& str,
                         int _errno = 2102,
                         std::string errmsg = "Error in textToInteger")
{
    return textToInteger(str.c_str(), _errno, errmsg);
}

/** String (char*) to long long integer conversion.
 *
 * @code
 * long long param_no = textToLongLong(nextToken(), 1602, "Error reading number
 *     parameters")
 * @endcode
 */
long long textToLongLong(const char* str,
                         int _errno = 2102,
                         std::string errmsg = "Error in AtoL");

/** Float to string conversion.
 *
 * If precision==0 the precision is automatically computed in such a way that
 * the number fits the width (the exponential format might be chosen). If
 * precision==-1 then the exponential format is forced. If width==0 then the
 * minimum width is used.
 *
 * @code
 * REPORT_ERROR(1602, "Value not recognised " + floatToString(val));
 * @endcode
 */
std::string floatToString(float F, int _width = 0, int _prec = 0);

/** Integer to string conversion.
 *
 * If width==0 then writes the number with the number of digits needed. The
 * fill_with field indicates which is the filling character for the left
 * positions.
 *
 * @code
 * REPORT_ERROR(1602, "Error reading key " + integerToString(key));
 * @endcode
 */
std::string integerToString(int I, int _width = 0, char fill_with = '0');

/** Character to integer conversion.
 *
 * Takes a character and produces a number according to its ASCII code minus 48.
 * For instance, ASCII=48 produces number 0, ASCII=49 produces 1, ..., ASCII=57
 * produces 9, ASCII=58 produces 10!!, ... This is used when you have codified
 * numbers greater than 9 in a single character.
 *
 * @code
 * int param_no = textToInt(token, 1602, "Error reading number parameters");
 * @endcode
 */
int textToInt(const char* str,
              int _errno = 2103,
              std::string errmsg = "Error in textToInt");

/** String to string with given length conversion.
 *
 * The output string will have the information of the input one with the given
 * width. If the width is smaller than the string length then the string is
 * truncated and if it is greater the string is right padded with spaces. If
 * width==0 then the same string is returned.
 */
std::string stringToString(const std::string& str, int _width = 0);

/** Check angle.
 *
 * If the argument is not "rot", "tilt" nor "psi" an exception is thrown
 */
void checkAngle(const std::string& str);

/** To lower.
 *
 * All characters between A-Z are brought to a-z. Result is rewritten on input
 * string
 */
void toLower(char* _str);

/** To lower, for STL strings.
 */
void toLower(std::string& _str);

/** Removes white spaces from the beginning and the end of the string
as well as escaped characters
and simplifies the rest of groups of white spaces of the string to
a single white space */
std::string simplify( const std::string& str );

/** Remove trailing spaces */
void trim(std::string& str);

/** Remove consecutive spaces.
 *
 * All consecutive spaces are replaced by a single one and starting and
 * finishing spaces are removed
 */
std::string removeSpaces(const std::string& _str);

/** Remove quotes.
 *
 * This function removes the first character if it is a DOUBLE or single quote,
 * as well as the last character. The char pointer might be moved.
 *
 * @code
 * char str[10] = "\"Hello\"";
 * (&str);
 * @endcode
 */
void removeQuotes(char** _str);
//@}

/** @name Tokenization
 *
 * These functions allow to split a string into small pieces separated by blank
 * spaces, giving you a pointer to the different word each time. The different
 * elements from the string are selected using strtok, so after the application
 * of this function to the input string, this is modified and NULL characters
 * are introduced as delimiters of the elements. This is useful in most
 * situations since after reading a list you might go on reading more things,
 * but you must be aware of it. Here goes an example of doing so:
 *
 * @code
 * std::cout << "Whole  line: " << line << std::endl;
 * std::cout << "First  word: " << firstToken(line) << std::endl;
 * std::cout << "Second word: " << nextToken() << std::endl;
 * std::cout << "Third  word: " << nextToken() << std::endl;
 * ...
 * @endcode
 *
 * When there are no more words, both functions return a NULL pointer. Here we
 * make a distinction between tokens (words that might be empty) and words
 * (words that cannot be empty, if they are then an exception or an exit error
 * is thrown).
 *
 * For STL there is another way. You supply a string object and a vector of
 * strings is returned with all the elements
 */
//@{
/** Split a STL string given some delimiter.
 *
 * Returns a the number of tokens found. The tokens are in the variable results.
 */
int splitString(const std::string& input,
                const std::string& delimiter,
                std::vector< std::string >& results,
                bool includeEmpties = false);

/** Returns first token (char*).
 *
 * @code
 * char line[80];
 *
 * std::cout << "First  word: " << firstToken(line) << std::endl;
 * @endcode
 */
inline char* firstToken(const char* str)
{
    return strtok((char*) str, " \t\n");
}

/** Returns first token (STL).
 *
 * @code
 * std::string line;
 *
 * std::cout << "First  word: " << firstToken(line) << std::endl;
 * @endcode
 */
inline char* firstToken(const std::string& str)
{
    return strtok((char*) str.c_str(), " \t\n");
}

/** Returns next token.
 *
 * This functions returns the next word of the line we have given last as
 * parameter to firstToken.
 *
 * @code
 * char line[80];
 * ...
 * firstToken(line);
 * std::cout << "Second  word: " << nextToken(line) << std::endl;
 *
 * stf::string line;
 * ...
 * firstToken(line);
 * std::cout << "Second  word: " << nextToken(line) << std::endl;
 * @endcode
 */
inline char* nextToken()
{
    return strtok((char*) NULL, " \t\n");
}

/** Returns next token.
 *
 * It reads from position i. Returns (in i) the following position to search on.
 * When there are no more tokens. It returns "".
 */
std::string nextToken(const std::string& str, int& i);

/** Get non empty string (char*).
 *
 * This function returns the first word found in the given line disregarding the
 * leading blanks. If no word is found then an exception or an exit error is
 * produced. After calling this function the first blank character after the
 * word is substituted by a NULL character (as it uses the function firstToken.
 * Further word readings should use the function read_nextWord
 */
char* firstWord(char* str,
                int _errno = 2106,
                const std::string & errmsg = "first word: String not found");

/** Get non empty string (STL).
 *
 * Same as the previous function but for STL strings
 */
inline char* firstWord(std::string& str,
                       int _errno = 2106,
                       std::string errmsg = "first word: String not found")
{
    // FIXME C-style cast
    return firstWord((char*) str.c_str(), _errno, errmsg);
}

/** Get next non empty string.
 *
 * This is the same as the nextToken, but an exception is thrown or an exit
 * error produced if the word is empty
 */
inline char* nextWord(int _errno = 2106,
                      std::string errmsg = "next word: String not found")
{
    return firstWord((char*) NULL, _errno, errmsg);
}

/** Tokenize a string and return a list of tokens
 *
 */
void tokenize(const std::string& str,
              std::vector< std::string >& tokens,
              const std::string& delimiters = " \t");
//@}
//@}
#endif
