/*
 * unity-webapps-rate.c
 * Copyright (C) Canonical LTD 2011
 *
 * Author: Robert Carr <racarr@canonical.com>
 * 
 unity-webapps 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 3 of the License, or
 * (at your option) any later version.
 * 
 * unity-webapps 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 program.  If not, see <http://www.gnu.org/licenses/>.";
 */

#include "unity-webapps-rate.h"
#include "unity-webapps-context-private.h"

#include "unity-webapps-debug.h"

/* Maximum number of service requests (notification, indicators, launcher,
   music player) per GLOBAL_TICK (in milliseconds) */
#define MAXIMUM_GLOBAL_RATE_PER_TICK 50
/* The tick interval for global service limiting in milliseconds */
#define GLOBAL_TICK 1000

/* Maximum number of notification requests per NOTIFICATION_TICK */
#define MAXIMUM_NOTIFICATION_RATE_PER_TICK 20
/* The tick interval for notification limiting in milliseconds */
#define NOTIFICATION_TICK 1000

/* Maximum number of indicator requests per INDICATOR_TICK */
#define MAXIMUM_INDICATOR_RATE_PER_TICK 30
/* The tick interval for indicator limiting in milliseconds */
#define INDICATOR_TICK 1000

/* Maximum number of music player requests per MUSIC_PLAYER_TICK */
#define MAXIMUM_MUSIC_PLAYER_RATE_PER_TICK 30
/* The tick interval for music player limiting in milliseconds */
#define MUSIC_PLAYER_TICK 1000


/*
 * Used as a timeout callback, decrement the global rate count
 */
static gboolean
_decrement_global_rate (gpointer user_data)
{
  UnityWebappsContext *context = (UnityWebappsContext *) user_data;
  
  context->priv->global_rate--;
  
  g_object_unref (G_OBJECT (context));

  return FALSE;
}

/*
 * Used as a timeout callback, decrement the notification rate count
 */
static gboolean
_decrement_notification_rate (gpointer user_data)
{
  UnityWebappsContext *context = (UnityWebappsContext *) user_data;
  
  context->priv->notification_context->notification_rate--;

  return FALSE;
}

/*
 * Used as a timeout callback, decrement the indicator rate count
 */
static gboolean
_decrement_indicator_rate (gpointer user_data)
{
  UnityWebappsContext *context = (UnityWebappsContext *) user_data;
  
  context->priv->indicator_context->indicator_rate--;

  return FALSE;
}

static gboolean
_decrement_launcher_rate (gpointer user_data)
{
  UnityWebappsContext *context = (UnityWebappsContext *) user_data;
  
  context->priv->launcher_context->launcher_rate--;

  return FALSE;
}

/*
 * Used as a timeout callback, decrement the music player rate count
 */
static gboolean
_decrement_music_player_rate (gpointer user_data)
{
  UnityWebappsContext *context = (UnityWebappsContext *) user_data;
  
  context->priv->music_player_context->music_player_rate--;

  return FALSE;
}

/*
 * Check if a context is below the global rate limit (and log a request)
 *
 * The global rate limiting approach works as follows:
 *
 * 1. Check if a global rate variable is over MAXIMUM_GLOBAL_RATE_PER_TICK.
 * 2. If so, we are rate limited.
 * 3. Otherwise increment the global rate variable, set a timeout to decrement it in GLOBAL_TICK milliseconds, and return true.
 */
static gboolean
unity_webapps_rate_check_global_rate_limit (UnityWebappsContext *context)
{
  if (context->priv->global_rate > MAXIMUM_GLOBAL_RATE_PER_TICK)
    {
      UNITY_WEBAPPS_NOTE (RATE, "Global rate limit check failed");
      return FALSE;
    }
  
  context->priv->global_rate++;
  
  g_object_ref (G_OBJECT (context));
  g_timeout_add (GLOBAL_TICK, _decrement_global_rate, context);

  return TRUE;
}

gboolean 
unity_webapps_rate_check_notification_rate_limit (UnityWebappsContext *context)
{
  gboolean ret;
  
  if (context->priv->notification_context->notification_rate > MAXIMUM_NOTIFICATION_RATE_PER_TICK)
    {
      UNITY_WEBAPPS_NOTE (RATE, "Notification rate limit check failed");
      return FALSE;
    }
  context->priv->notification_context->notification_rate++;
  
  g_timeout_add (NOTIFICATION_TICK, _decrement_notification_rate, context);
  
  ret = unity_webapps_rate_check_global_rate_limit (context);
  
  return ret;
}

/**
 * Check the indicator and global rate limits (and log an access)
 */
gboolean
unity_webapps_rate_check_indicator_rate_limit (UnityWebappsContext *context)
{
  gboolean ret;

  if (context->priv->indicator_context->indicator_rate > MAXIMUM_INDICATOR_RATE_PER_TICK)
    {
      UNITY_WEBAPPS_NOTE (RATE, "Indicator rate limit check failed");
      return FALSE;
    }
  context->priv->indicator_context->indicator_rate++;
  
  g_timeout_add (INDICATOR_TICK, _decrement_indicator_rate, context);

  
  ret = unity_webapps_rate_check_global_rate_limit (context);
  
  return ret;
}


/**
 * Check the music player and global rate limits (and log an access)
 */
gboolean
unity_webapps_rate_check_music_player_rate_limit (UnityWebappsContext *context)
{
  gboolean ret;

  if (context->priv->music_player_context->music_player_rate > MAXIMUM_MUSIC_PLAYER_RATE_PER_TICK)
    {
      UNITY_WEBAPPS_NOTE (RATE, "Music player rate limit check failed");
      return FALSE;
    }
  context->priv->music_player_context->music_player_rate++;
  
  g_timeout_add (MUSIC_PLAYER_TICK, _decrement_music_player_rate, context);
  
  ret = unity_webapps_rate_check_global_rate_limit (context);
  
  return ret;
}


gboolean
unity_webapps_rate_check_launcher_rate_limit (UnityWebappsContext *context)
{
  gboolean ret;

  if (context->priv->launcher_context->launcher_rate > MAXIMUM_INDICATOR_RATE_PER_TICK)
    {
      UNITY_WEBAPPS_NOTE (RATE, "Launcher rate limit check failed");
      return FALSE;
    }
  context->priv->launcher_context->launcher_rate++;
  
  g_timeout_add (INDICATOR_TICK, _decrement_launcher_rate, context);

  
  ret = unity_webapps_rate_check_global_rate_limit (context);
  
  return ret;
}


