/*
 * string.hpp
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit 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 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit 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 Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Created on: Apr 1, 2013
 * Author: Jan Travnicek
 */

#ifndef __STRING_HPP_
#define __STRING_HPP_

#include <stdexcept>
#include <algorithm>

#include "vector.hpp"

namespace ext {

/**
 * Class extending the set class from the standard library. Original reason is to allow printing of the container with overloaded operator <<.
 *
 * The class mimics the behavior of the set from the standatd library.
 *
 */
class string : public std::string {
public:
	/**
	 * Inherit constructors of the standard set
	 */
	using std::string::string; // NOLINT(modernize-use-equals-default)

	/**
	 * Inherit operator = of the standard set
	 */
	using std::string::operator =;
#ifndef __clang__

	/**
	 * Default constructor needed by g++ since it is not inherited
	 */
	string ( ) = default;

	/**
	 * Copy constructor needed by g++ since it is not inherited
	 */
	string ( const string & other ) = default;

	/**
	 * Move constructor needed by g++ since it is not inherited
	 */
	string ( string && other ) = default;

	/**
	 * Copy operator = needed by g++ since it is not inherited
	 */
	string & operator = ( string && other ) = default;

	/**
	 * Move operator = needed by g++ since it is not inherited
	 */
	string & operator = ( const string & other ) = default;
#endif
	/**
	 * \brief
	 * Constructor from standard string.
	 *
	 * \param the standard string
	 */
	explicit string ( const std::string & other ) noexcept : std::string ( other ) {
	}

	/**
	 * \brief
	 * Constructor from standard string.
	 *
	 * \param the standard string
	 */
	explicit string ( std::string && other ) noexcept : std::string ( std::move ( other ) ) {
	}

	/**
	 * \brief
	 * Assignment from standard string.
	 *
	 * \param the standard string
	 */
	string & operator = ( std::string && other ) noexcept {
		static_cast < std::string & > ( * this ) = std::move ( other );
		return * this;
	}

	/**
	 * \brief
	 * Assignment from standard string.
	 *
	 * \param the standard string
	 */
	string & operator = ( const std::string & other ) noexcept {
		static_cast < std::string & > ( * this ) = other;
		return * this;
	}

	/**
	 * Tests whether this string starts with given one. TODO remove after switching to c++20
	 *
	 * \param x the tested prefix
	 *
	 * \return bool true if x is prefix of this string, false otherwise
	 */
	bool starts_with ( const std::string & x ) const noexcept {
		return this->compare ( 0, x.size ( ), x ) == 0;
	}
};

/**
 * \brief
 * To string method designated for objects that can be casted to string.
 *
 * \tparam T type of the casted instance
 *
 * \param value the casted instance
 *
 * \return string representation of the instance
 */
template < typename T, typename std::enable_if < std::is_constructible < std::string, T >::value >::type* = nullptr >
std::string to_string ( const T & value ) {
	return (std::string) value;
}

/**
 * \brief
 * To string method designed for string to make the interface complete. The method copies the string only.
 *
 * \param value the source value
 *
 * \return the copy of the parameter
 */
std::string to_string ( const std::string & value );

/**
 * \brief
 * To string method designed for integers. The method converts integer to string.
 *
 * \param value the integer value to covnert
 *
 * \return string representation of integer
 */
std::string to_string ( int value );

/**
 * \brief
 * To string method designed for booleans. The method converts boolean to string.
 *
 * \param value the boolean value to covnert
 *
 * \return string representation of boolean
 */
std::string to_string ( bool value );

/**
 * \brief
 * To string method designed for longs. The method converts long to string.
 *
 * \param value the long value to covnert
 *
 * \return string representation of long
 */
std::string to_string ( long value );

/**
 * \brief
 * To string method designed for long longs. The method converts long long to string.
 *
 * \param value the long long value to covnert
 *
 * \return string representation of long long
 */
std::string to_string ( long long value );

/**
 * \brief
 * To string method designed for unsigneds. The method converts unsigned to string.
 *
 * \param value the unsigned value to covnert
 *
 * \return string representation of unsigned
 */
std::string to_string ( unsigned value );

/**
 * \brief
 * To string method designed for unsigned longs. The method converts unsigned long to string.
 *
 * \param value the unsigned long value to covnert
 *
 * \return string representation of unsigned long
 */
std::string to_string ( unsigned long value );

/**
 * \brief
 * To string method designed for unsigned long longs. The method converts unsigned long long to string.
 *
 * \param value the unsigned long long value to covnert
 *
 * \return string representation of unsigned long long
 */
std::string to_string ( unsigned long long value );

/**
 * \brief
 * To string method designed for doubles. The method converts double to string.
 *
 * \param value the double value to covnert
 *
 * \return string representation of double
 */
std::string to_string ( double value );

/**
 * \brief
 * To string method designed for characters. The method converts character to string.
 *
 * \param value the character value to covnert
 *
 * \return string representation of character
 */
std::string to_string ( char value );

/**
 * \brief
 * To string method designed for cstrings. The method converts cstring to string.
 *
 * \param value the cstring value to covnert
 *
 * \return string representation of cstring
 */
std::string to_string ( char * param );




/**
 * \brief
 * Predeclaration of from string method. The from string methods are supposed to convert string representation of a given type to that type.
 *
 * \tparam the requested result type
 *
 * \param the string representation
 *
 * \return value converted from string
 */
template < typename T >
T from_string ( const std::string & );

/**
 * \brief
 * Specialisation of from string method for string result to make the interface complete.
 *
 * \param the string representation
 *
 * \return string copy
 */
template < >
std::string from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for integer result.
 *
 * \param the string representation
 *
 * \return integer value converted from string
 */
template < >
int from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for booleans result.
 *
 * \param the string representation
 *
 * \return boolean value converted from string
 */
template < >
bool from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for long result.
 *
 * \param the string representation
 *
 * \return long value converted from string
 */
template < >
long from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for long long result.
 *
 * \param the string representation
 *
 * \return long long value converted from string
 */
template < >
long long from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for unsigned result.
 *
 * \param the string representation
 *
 * \return unsigned value converted from string
 */
template < >
unsigned from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for unsigned long result.
 *
 * \param the string representation
 *
 * \return unsigned long value converted from string
 */
template < >
unsigned long from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for unsigned long long result.
 *
 * \param the string representation
 *
 * \return unsigned long long value converted from string
 */
template < >
unsigned long long from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of from string method for double result.
 *
 * \param the string representation
 *
 * \return double value converted from string
 */
template < >
double from_string ( const std::string & value );

/**
 * \brief
 * Specialisation of the compare structure implementing the three-way comparison.
 */
template < >
struct compare < std::string > {
	int operator ()( const std::string & first, const std::string & second ) const {
		return first.compare ( second );
	}

};

/**
 * \brief
 * Splits the string to substrings based on delimiter.
 *
 * \param source the string to split
 * \param delimiter the string to split by
 *
 * \return vector of substrings
 */
ext::vector < std::string > explode ( const std::string & source, const std::string & delimiter );

/**
 * \brief
 * Merges strings using the delimiter.
 *
 * \param source the vector of strings to merge
 * \param delimiter the connection string
 *
 * \return the merged string
 */
std::string implode ( const std::vector < std::string > & source, const std::string & delimiter );

/**
 * \brief
 * isspace method.
 *
 * \param ch the tested character
 *
 * \return true if the character is whitespace, false othervise
 */
inline bool isspace ( int ch ) {
	return std::isspace ( ch ) != 0;
}

/**
 * \brief
 * Inverse of isspace method.
 *
 * \param ch the tested character
 *
 * \return true if the character is not whitespace, false othervise
 */
inline bool not_isspace ( int ch ) {
	return std::isspace ( ch ) == 0;
}

/**
 * \brief
 * Trims spaces inside the string from the left.
 *
 * \param s the trimed string
 *
 * \return string without spaces at front of it
 */
inline std::string_view ltrim ( std::string_view s ) {
	auto endPos = std::find_if ( s.begin ( ), s.end ( ), not_isspace );
	s.remove_prefix ( std::distance ( s.begin ( ), endPos ) );
	return s;
}

/**
 * \brief
 * Trims spaces inside the string from the right.
 *
 * \param s the trimed string
 *
 * \return string without spaces at back of it
 */
inline std::string_view rtrim ( std::string_view s ) {
	auto beginPos = std::find_if ( s.rbegin ( ), s.rend ( ), not_isspace ).base ( );
	s.remove_suffix ( std::distance ( beginPos, s.end ( ) ) );
	return s;
}

/**
 * \brief
 * Trims spaces inside the string from both sides.
 *
 * \param s the trimed string
 *
 * \return string without spaces at both sides of it
 */
inline std::string_view trim ( std::string_view s ) {
	return rtrim(ltrim(s));
}

// Creates a string view from a pair of iterators
//  http://stackoverflow.com/q/33750600/882436
inline std::string_view make_string_view ( std::string::const_iterator begin, std::string::const_iterator end ) {
	return std::string_view { ( begin != end ) ? & * begin : nullptr, ( typename std::string_view::size_type ) std::max ( std::distance ( begin, end ), ( typename std::string_view::difference_type ) 0 ) };
} // make_string_view

} /* namespace ext */

#endif /* __STRING_HPP_ */
