/* 
 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * 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; version 2 of the
 * License.
 * 
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef _STRING_UTILITIES_H_
#define _STRING_UTILITIES_H_

#ifdef _WIN32
# ifndef PRId64
#   define PRId64 "I64d"
# endif
#else
# include <inttypes.h>
#endif

#include "common.h"
#include <string>
#include <vector>

#include <glib.h>

#define _(s) s // TODO: replace with localization code.

#include <boost/cstdint.hpp>

using boost::uint64_t;
using boost::int64_t;

namespace base
{
  #define SPACES " \t\r\n"

#ifdef _WIN32
  // use this to convert utf8 string to WCHAR* in windows
  BASELIBRARY_PUBLIC_FUNC std::wstring string_to_wstring(const std::string &s);
  BASELIBRARY_PUBLIC_FUNC std::string wstring_to_string(const std::wstring &s);
  BASELIBRARY_PUBLIC_FUNC std::wstring path_from_utf8(const std::string &s);
#else
  BASELIBRARY_PUBLIC_FUNC std::string path_from_utf8(const std::string &s);
#endif
  // use this to convert a utf8 std::string to a std::string that can be used to open files in windows (noop elsewhere)
  BASELIBRARY_PUBLIC_FUNC std::string string_to_path_for_open(const std::string &s);

  // turns a UTF8 string into something that can be used as a file name (ie, strips out special chars)
  BASELIBRARY_PUBLIC_FUNC std::string sanitize_file_name(const std::string &s);

  // Trimming, cleanup etc.
  BASELIBRARY_PUBLIC_FUNC std::string trim_right(const std::string& s, const std::string& t = SPACES);
  BASELIBRARY_PUBLIC_FUNC std::string trim_left(const std::string& s, const std::string& t = SPACES);
  BASELIBRARY_PUBLIC_FUNC std::string trim(const std::string& s, const std::string& t = SPACES);
  BASELIBRARY_PUBLIC_FUNC std::string tolower(const std::string& s);
  BASELIBRARY_PUBLIC_FUNC std::string toupper(const std::string& s);
  BASELIBRARY_PUBLIC_FUNC std::string truncate_text(const std::string& s, int max_length);
  BASELIBRARY_PUBLIC_FUNC std::string sanitize_utf8(const std::string& s);

  // Parsing/Formatting.
  BASELIBRARY_PUBLIC_FUNC std::string get_identifier(const std::string& id, std::string::const_iterator& start);
  BASELIBRARY_PUBLIC_FUNC std::vector<std::string> split_qualified_identifier(const std::string& id);
  BASELIBRARY_PUBLIC_FUNC std::string strfmt(const char *fmt, ...) G_GNUC_PRINTF(1, 2);
  BASELIBRARY_PUBLIC_FUNC std::string sizefmt(int64_t s, bool metric);
  BASELIBRARY_PUBLIC_FUNC std::string pop_path_front(std::string &path);
  BASELIBRARY_PUBLIC_FUNC std::string pop_path_back(std::string &path);
  BASELIBRARY_PUBLIC_FUNC std::string strip_text(const std::string &text, bool left= true, bool right= true);

  BASELIBRARY_PUBLIC_FUNC std::string normalize_path_extension(std::string filename, std::string extension);
  BASELIBRARY_PUBLIC_FUNC std::string normalize_path(const std::string path);
  BASELIBRARY_PUBLIC_FUNC std::string expand_tilde(const std::string &path);
  BASELIBRARY_PUBLIC_FUNC std::string make_valid_filename(const std::string &name);
  
  BASELIBRARY_PUBLIC_FUNC std::string escape_sql_string(const std::string &string, bool wildcards = false); // "strings" or 'strings'
  BASELIBRARY_PUBLIC_FUNC std::string unescape_sql_string(const std::string &string, char escape_char);
  BASELIBRARY_PUBLIC_FUNC std::string escape_backticks(const std::string &string);  // `identifier`
  BASELIBRARY_PUBLIC_FUNC std::string extract_option_from_command_line(const std::string& option,
    const std::string &command_line);
  
  BASELIBRARY_PUBLIC_FUNC bool parse_font_description(const std::string &fontspec, std::string &font,
                                                      float &size, bool &bold, bool &italic);

  // Searching, splitting etc.
  BASELIBRARY_PUBLIC_FUNC bool starts_with(const std::string& s, const std::string& part);
  BASELIBRARY_PUBLIC_FUNC void replace(std::string& value, const std::string& search, const std::string& replacement);
  BASELIBRARY_PUBLIC_FUNC std::vector<std::string> split(const std::string &s, const std::string &sep, int count = -1);
  BASELIBRARY_PUBLIC_FUNC std::vector<std::string> split_by_set(const std::string &s, const std::string &separator_set, int count = -1);
  BASELIBRARY_PUBLIC_FUNC std::vector<std::string> split_token_list(const std::string &s, int sep);
  BASELIBRARY_PUBLIC_FUNC bool partition(const std::string &s, const std::string &sep, std::string &left, std::string &right);
  BASELIBRARY_PUBLIC_FUNC int index_of(const std::vector<std::string> &list, const std::string &s);
  
  // You cannot export a template function, only specializations. But since the code is in the
  // header we don't need to export.
  template<class C> std::string join(const C &list, const std::string &sep)
  {
    std::string s;
    for (typename C::const_iterator i = list.begin(); i != list.end(); ++i)
    {
      if (i != list.begin())
        s.append(sep);
      s.append(*i);
    }
    return s;
  }

  // These functions will perform newline conversion depending on OS. In case of error an exception is thrown.
  BASELIBRARY_PUBLIC_FUNC void set_text_file_contents(const std::string &filename, const std::string &data);
  BASELIBRARY_PUBLIC_FUNC std::string get_text_file_contents(const std::string &filename);

  BASELIBRARY_PUBLIC_FUNC std::string quote_identifier(const std::string& identifier, const char quote_char);
  BASELIBRARY_PUBLIC_FUNC std::string unquote_identifier(const std::string& identifier);
  BASELIBRARY_PUBLIC_FUNC std::string quote_identifier_if_needed(const std::string &ident, const char quote_char);

  BASELIBRARY_PUBLIC_FUNC bool stl_string_compare(const std::string &first, const std::string &second, bool case_sensitive = true);
  BASELIBRARY_PUBLIC_FUNC int string_compare(const std::string &first, const std::string &second, bool case_sensitive = true);
  BASELIBRARY_PUBLIC_FUNC bool same_string(const std::string &first, const std::string &second, bool case_sensitive = true);
  BASELIBRARY_PUBLIC_FUNC bool contains_string(const std::string &text, const std::string &candidate, bool case_sensitive = true);

#ifdef __APPLE__
#undef check
#endif

  class BASELIBRARY_PUBLIC_FUNC EolHelpers
  {
  public:
    enum Eol_format { eol_lf, eol_cr, eol_crlf };
    static Eol_format detect(const std::string &text); // detects eol format based on the first eol occurence, line endings consistency is implied
    static int count_lines(const std::string &text); // counts lines in the text, even if line endings are inconsistent
    static bool check(const std::string &text); // checks whether line endings are consistent (same throughout the text)
    static void conv(const std::string &src_text, Eol_format src_eol_format, std::string &dest_text, Eol_format dest_eol_format); // converts between 2 known eol formats, line endings consistency is implied
    static void fix(const std::string &src_text, std::string &dest_text, Eol_format eol_format); // aligns eol format to the specified

    static bool is_eol(const char *sym_ptr) // reliably determines whether the given string position contains last symbol of eol sequence, line ending inconsistency is allowed
    {
      switch (*sym_ptr)
      {
      case '\n': return true;
      case '\r': return ('\n' != *(sym_ptr+1));
      default: return false;
      }
    }

    static Eol_format default_eol_format() // platform default eol format
    {
#if defined(_WIN32)
      return eol_crlf;
#elif defined(__APPLE__)
      return eol_cr;
#else
      return eol_lf;
#endif
    }

    static const std::string & eol(Eol_format eol= default_eol_format()) // returns eol sequence by eol format
    {
      static std::string eol_crlf_seq= "\r\n";
      static std::string eol_cr_seq= "\r";
      static std::string eol_lf_seq= "\n";
      switch (eol)
      {
      case eol_crlf: return eol_crlf_seq;
      case eol_cr: return eol_cr_seq;
      default: return eol_lf_seq;
      }
    }
  };

/**
 * @brief Splits a string into several lines
 * This function splits the string into several lines, depending on the line_length. You can prefix each line using the 
 * left_fill string, which can be ignored if the line_length is too small. For line_length lower then the minimum length (5)
 * returns an empty string.
 * When the text is too big and max_lines is reached, it will truncate the text and add elipses, so you should account for
 * an extra line in this situation.
 * @param text The string to be splitted
 * @param line_length The length of the line in characters
 * @param left_fill A string prefixing each line. This will not be used if the line_length is too small. Defaults to "".
 * @param indent_first True to indent the first line. Defaults to true.
 * @param max_lines The maximum amount of lines. Defaults to 30.
 * @return A string splitted in several lines. If the string is not encoded in utf8 or the line_length is too small, it will return an empty string.
 **/
BASELIBRARY_PUBLIC_FUNC std::string reflow_text(const std::string &text, unsigned int line_length, const std::string &left_fill="", bool indent_first=true, unsigned int max_lines=30);

  
} // namespace base


#endif // _STRING_UTILITIES_H_
