/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include "stdafx.h"
#include "common.h"

// XXX: most of the code here is not utf-8 safe, check that.
namespace bec {

  std::string fmt(const char *fmt, ...)
  {
    va_list args;
    char *tmp;
    std::string ret;

    va_start(args, fmt);
    tmp= g_strdup_vprintf(fmt, args);
    va_end(args);

    ret= tmp;
    g_free(tmp);

    return ret;
  }

  std::string fmttime(time_t t, const char *fmt)
  {
    char date[100];
  #ifdef _WIN32
    errno_t err;
  #else
    int err;
  #endif
    struct tm newtime;


    if (t == 0)
      time(&t);

  #ifdef _WIN32
    err= localtime_s(&newtime, &t);
  #else
    localtime_r(&t, &newtime);
    err= 0;
  #endif

    if (!err)
      strftime(date, sizeof(date), fmt, &newtime);
    else
      date[0]= 0;

    return date;
  }

  
  std::string escape_sql_string(const std::string &text)
  {
    std::string escaped;
    std::string::size_type p, s, len= text.length();

    // escapes ' " \n \r

    p= 0;
    s= 0;
    while (p < len)
    {
      switch (text[p])
      {
      case '\'':
      case '"':
        if (p > s)
          escaped.append(text.substr(s, p-s));
        escaped.append("\\");
        if ((p>0) && (text[p-1] == '\\'))
        {
          escaped.append("\\");
          ++s;
        }
        escaped.append(text.substr(p, 1));
        s= p+1;
        break;
      case '\n':
        if (p > s)
          escaped.append(text.substr(s, p-s));
        escaped.append("\\n");
        s= p+1;
        break;
      case '\r':
        if (p > s)
          escaped.append(text.substr(s, p-s));
        escaped.append("\\r");
        s= p+1;
        break;
      }
      p++;
    }
    if (p > s)
      escaped.append(text.substr(s));

    return escaped;
  }

  
  std::string replace_string(const std::string &s,
                                    const std::string &from,
                                    const std::string &to)
  {
    std::string::size_type p;
    std::string ss, res;

    ss= s;
    p= ss.find(from);
    while (p != std::string::npos)
    {
      if (p > 0)
        res.append(ss.substr(0, p)).append(to);
      else
        res.append(to);
      ss= ss.substr(p+from.size());
      p= ss.find(from);
    }
    res.append(ss);

    return res;
  }

  std::string rtrim(const std::string &value)
  {
    size_t trailing_spaces_count= 0;
    for (std::string::const_reverse_iterator i= value.rbegin(), i_end= value.rend(); (i != i_end) && std::isspace(*i); ++i, ++trailing_spaces_count);
    return value.substr(0, value.size()-trailing_spaces_count);
  }

  std::string replace_variable(const std::string &format, const std::string &variable, const std::string &value)
  {
    
    std::string result= format;
    std::string::size_type pos;
  
    for (;;)
    {
      std::string s;
      std::string::size_type end;
      
      pos= result.find(variable.substr(0, variable.size()-1));
      if (pos == std::string::npos)
        break;
      
      end= result.find('%', pos+1);
      if (end == std::string::npos) // bad format
        break;

      s= result.substr(pos+1, end-pos-1);

      std::string::size_type filter_pos= s.find("|");
      std::string filtered_value= value;
      
      if (filter_pos == std::string::npos)
      {
        if (s.length() != variable.length()-2)
          break;
      }
      else if (filter_pos != variable.length()-2)
        break;
      else
      {
        std::string filter= s.substr(filter_pos+1, s.size()-filter_pos);

        if (filter.compare("capitalize") == 0)
        {
          gunichar ch= g_utf8_get_char(value.data());
          
          ch= g_unichar_toupper(ch);
          
          gchar *rest= g_utf8_find_next_char(value.data(), value.data()+value.size());
          char utf8[10];
          utf8[g_unichar_to_utf8(ch, utf8)]= 0;
          filtered_value= std::string(utf8).append(rest);
        }
        else if (filter.compare("lower") == 0)
        {
          gchar *l= g_utf8_strdown(value.data(), value.size());
          if (l)
            filtered_value= l;
          g_free(l);
        }
        else if (filter.compare("upper") == 0)
        {
          gchar *l= g_utf8_strup(value.data(), value.size());
          if (l)
            filtered_value= l;
          g_free(l);
        }
      }
      result= result.substr(0, pos).append(filtered_value).append(result.substr(end+1));
    }
    
    return result;
  }
  
  
  std::string append_extension_if_needed(const std::string &path,
                                         const std::string &ext)
  {
    if (!has_suffix(path, ext))
      return path+ext;
    return path;
  }


  std::string make_path(const std::string &prefix, const std::string &file)
  {
    if (prefix.empty())
      return file;

    if (prefix[prefix.size()-1] == '/' || prefix[prefix.size()-1] == '\\')
      return prefix+file;
    return prefix+G_DIR_SEPARATOR+file;
  }

  /**
   * Removes all unnecessary path separators as well as "./" combinations.
   * If there is a parent-dir entry (../) then this as well as the directly prefacing
   * dir entry is removed.
   */
  std::string normalize_path(const std::string path)
  {
    // First convert all separators to the one that is used on the platform (no mix)
    // and ease so at the same time further processing here.
    std::string result;
    std::string separator(1, G_DIR_SEPARATOR);

    result= replace_string(path, "\\", separator);
    result= replace_string(result, "/", separator);

    std::string double_separator = separator + separator;
    while (result.find(double_separator) != std::string::npos)
      result= replace_string(result, double_separator, separator);

    // Sanity check. Return *after* we have converted the slashs. This is part of the normalization.
    if (result.size() < 2)
      return result;

    std::vector<std::string> parts= split_string(result, separator);

    // Construct result backwards while examining the path parts.
    result= "";
    int pending_count= 0;
    for (int i= parts.size() - 1; i >= 0; i--)
    {
      if (parts[i].compare(".") == 0)
        // References to the current directory can be removed without further change.
        continue;

      if (parts[i].compare("..") == 0)
      {
        // An entry that points back to the parent dir.
        // Ignore this and keep track for later removal of the parent dir.
        pending_count++;
      }
      else
        if (pending_count > 0)
        {
          // If this is a normal dir entry and we have pending parent-dir redirections
          // then go one step up by removing (ignoring) this entry.
          pending_count--;
        }
        else
          result = separator + parts[i] + result;
    }

    // Don't return the leading separator.
    return result.substr(1);
  }

  std::vector<std::string> split_string(const std::string &s, const std::string &sep, int count)
  {
    std::vector<std::string> parts;
    std::string ss= s;

    std::string::size_type p;

    if (s.empty())
      return parts;

    if (count == 0)
      count= -1;

    p= ss.find(sep);
    while (!ss.empty() && p != std::string::npos && (count<0 || count>0))
    {
      parts.push_back(ss.substr(0, p));
      ss= ss.substr(p+sep.size());

      --count;
      p= ss.find(sep);
    }
    parts.push_back(ss);

    return parts;
  }

  
  GStaticMutexLock::GStaticMutexLock(GStaticMutex& mtx) 
  : mutex(mtx) 
  {   
    g_static_mutex_lock(&mutex);  
  }
  
  GStaticMutexLock::~GStaticMutexLock()                               
  {
    g_static_mutex_unlock(&mutex); 
  }
};
