/* 
 * Copyright (c) 2007, 2010, 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
 */
#include "stdafx.h"

#include "common.h"
#include "base/string_utilities.h"

#include <cstdlib>

// XXX: most of the code here is not utf-8 safe, check that.
// TODO: The content here should all move the base library.
namespace bec {

  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 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;
  }


  void replace_string_inplace(std::string &text, const std::string &from, const std::string &to)
  {
    std::string::size_type from_len= from.length();
    std::string::size_type p= text.find(from);
    while (p != std::string::npos)
    {
      text.replace(p, from_len, to);
      p= text.find(from, p);
    }
  }


  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;
  }  

  GStaticMutexLock::GStaticMutexLock(GStaticMutex& mtx) 
  : mutex(mtx) 
  {   
    g_static_mutex_lock(&mutex);  
  }
  
  GStaticMutexLock::~GStaticMutexLock()                               
  {
    g_static_mutex_unlock(&mutex); 
  }
  
  
  GStaticRecMutexLock::GStaticRecMutexLock(GStaticRecMutex& mtx) 
  : mutex(mtx) 
  {   
    g_static_rec_mutex_lock(&mutex);  
  }
  
  GStaticRecMutexLock::~GStaticRecMutexLock()
  {
    g_static_rec_mutex_unlock(&mutex); 
  }  


  TimerActionThread::TimerActionThread(const Action &action, gulong milliseconds) : _action(action), _microseconds(milliseconds * 1000)
  {
    _action_mutex= g_mutex_new();
    _thread= g_thread_create(start, this, FALSE, NULL);
  }

  TimerActionThread::~TimerActionThread()
  {
    g_mutex_free(_action_mutex);
  }

  TimerActionThread * TimerActionThread::create(const Action &action, gulong milliseconds)
  {
    return new TimerActionThread(action, milliseconds);
  }

  gpointer TimerActionThread::start(gpointer data)
  {
    TimerActionThread *thread= static_cast<TimerActionThread*>(data);
    thread->main_loop();
    return NULL;
  }

  void TimerActionThread::stop(bool clear_exit_signal)
  {
    GMutexLock action_mutex(_action_mutex);
    _action= Action();
    if (clear_exit_signal)
      on_exit.disconnect_all_slots();
  }

  void TimerActionThread::main_loop()
  {
    const int poll_interval= 1000000; // check every 1 sec if thread was stopped
    for (;;)
    {
      std::div_t d= std::div(_microseconds, poll_interval);
      for (int n= 0; n < d.quot; ++n)
      {
        g_usleep(poll_interval);
        {
          GMutexLock action_mutex(_action_mutex);
          if (_action.empty())
            goto exit;
        }
      }
      g_usleep(d.rem);
      {
        GMutexLock action_mutex(_action_mutex);
        if (_action.empty())
          goto exit;
        if (_microseconds != 0)
          _action();
        else
          g_usleep(poll_interval);
      }
    }

exit:
    on_exit();
    delete this;
  }


};
