/*
 * unity-webapps-app-db.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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <sqlite3.h>

#include "context-daemon/util/unity-webapps-dirs.h"
#include "unity-webapps-app-db.h"
#include "unity-webapps-debug.h"

static sqlite3 *app_db = NULL;

#define CREATE_RESOURCES_TABLE_SQL "CREATE TABLE IF NOT EXISTS apps(name TEXT, domain TEXT, homepage TEXT, PRIMARY KEY (name, domain));"
#define CREATE_ACTIONS_TABLE_SQL "CREATE TABLE IF NOT EXISTS actions(name TEXT, domain TEXT, label TEXT, page TEXT, PRIMARY KEY (name, domain, label));"

#define GET_HOMEPAGE_SQL_TEMPLATE "SELECT homepage FROM apps WHERE name=%Q AND domain=%Q;"

static sqlite3_stmt *
db_get_homepage_prepare_statement (const gchar *name, const gchar *domain)
{
  gchar *statement_sql;
  sqlite3_stmt *statement;
  gint rc;

  statement_sql = sqlite3_mprintf (GET_HOMEPAGE_SQL_TEMPLATE, name, domain);

  statement = NULL;

  rc = sqlite3_prepare_v2 (app_db, statement_sql, -1, &statement, NULL);

  if (rc != SQLITE_OK)
    {
      g_critical ("Failed to compile App DB get homepage statement: %s", sqlite3_errmsg (app_db));
    }

  sqlite3_free (statement_sql);

  return statement;
}

gchar *
unity_webapps_app_db_get_homepage (const gchar *name, const gchar *domain)
{
  sqlite3_stmt *homepage_statement;
  gchar *homepage;
  gint rc;

  if (!app_db)
    {
      unity_webapps_app_db_open ();
    }

  homepage = NULL;

  homepage_statement = db_get_homepage_prepare_statement (name, domain);

  if (homepage_statement == NULL)
    {
      return NULL;
    }

  rc = sqlite3_step (homepage_statement);

  if (rc == SQLITE_ROW)
    {
      homepage = g_strdup ((const gchar *)sqlite3_column_text (homepage_statement, 0));
    }

  UNITY_WEBAPPS_NOTE (MISC, "Looked up homepage for (%s, %s) got %s", name, domain, homepage);

  sqlite3_finalize (homepage_statement);
  return homepage;
}

#define INSERT_HOMEPAGE_SQL_TEMPLATE "INSERT OR REPLACE INTO apps (name, domain, homepage) VALUES (%Q, %Q, %Q);"

static sqlite3_stmt *
db_insert_homepage_prepare_statement (const gchar *name, const gchar *domain, const gchar *homepage)
{
  gchar *statement_sql;
  sqlite3_stmt *statement;
  gint rc;

  statement_sql = sqlite3_mprintf (INSERT_HOMEPAGE_SQL_TEMPLATE, name, domain, homepage);

  statement = NULL;

  rc = sqlite3_prepare_v2 (app_db, statement_sql, -1, &statement, NULL);

  if (rc != SQLITE_OK)
    {
      g_critical ("Failed to compile App DB insert homepage statement: %s", sqlite3_errmsg (app_db));
    }

  sqlite3_free (statement_sql);

  return statement;
}

gboolean
unity_webapps_app_db_update_homepage (const gchar *name, const gchar *domain, const gchar *homepage)
{
  sqlite3_stmt *homepage_statement;
  int rc;
  gboolean ret = TRUE;

  if (!app_db)
    {
      unity_webapps_app_db_open ();
    }

  homepage_statement = db_insert_homepage_prepare_statement (name, domain, homepage);

  if (homepage_statement == NULL)
    {
      return FALSE;
    }

  rc = sqlite3_step (homepage_statement);

  if (rc != SQLITE_DONE)
    {
      g_warning ("Error updating homepage in app db: %s", sqlite3_errmsg (app_db));
      ret = FALSE;

      goto out;
    }

 out:
  sqlite3_finalize (homepage_statement);
  return ret;
}

static void
create_apps_table (const gchar *query)
{
  int rc;
  gchar *error_message;

  error_message = NULL;

  rc = sqlite3_exec (app_db, query, NULL, 0, &error_message);

  if (rc != SQLITE_OK)
    {
      g_critical ("Error creating apps table %s \n", error_message);

      sqlite3_free (error_message);

      sqlite3_close (app_db);

      app_db = NULL;
    }

}


gboolean
unity_webapps_app_db_open ()
{
  const gchar *uw_dir;
  gchar *database_file;
  gboolean ret;
  int rc;

  ret = TRUE;

  uw_dir = unity_webapps_dirs_get_user_dir ();
  database_file = g_build_filename (uw_dir, "apps2.db", NULL);

  UNITY_WEBAPPS_NOTE (RESOURCE, "Opening App DB (%s)", database_file);

  rc = sqlite3_open (database_file, &app_db);

  if (rc != 0)
    {
      g_critical ("Failed to open apps database: %s", sqlite3_errmsg(app_db));
      sqlite3_close (app_db);

      ret = FALSE;
      goto out;
    }

  create_apps_table (CREATE_RESOURCES_TABLE_SQL);
  create_apps_table (CREATE_ACTIONS_TABLE_SQL);

 out:
  g_free (database_file);

  return ret;
}

#define INSERT_ACTION_SQL_TEMPLATE "INSERT OR REPLACE INTO actions (name, domain, label, page) VALUES (%Q, %Q, %Q, %Q);"

gboolean
unity_webapps_app_db_add_action (const gchar *name, const gchar *domain, const gchar *label, const gchar *page)
{
  sqlite3_stmt *statement;
  char *statement_sql;
  gint rc;
  gboolean res = TRUE;

  if (!app_db)
    {
      unity_webapps_app_db_open ();
    }

  statement_sql = sqlite3_mprintf (INSERT_ACTION_SQL_TEMPLATE, name, domain, label, page);

  rc = sqlite3_prepare_v2 (app_db, statement_sql, -1, &statement, NULL);
  if (rc != SQLITE_OK)
    {
      g_critical ("Failed to compile Action DB statement: %s", sqlite3_errmsg (app_db));
      sqlite3_free (statement_sql);
      return FALSE;
    }

  sqlite3_free (statement_sql);

  rc = sqlite3_step (statement);

  if (rc != SQLITE_DONE)
    {
      g_warning ("Error inserting action: %s", sqlite3_errmsg (app_db));
      res = FALSE;
    }

  sqlite3_finalize (statement);
  return res;
}

#define DELETE_ALL_ACTIONS_SQL "DELETE FROM actions WHERE name=%Q AND domain=%Q"

gboolean
unity_webapps_app_db_clear_actions (const gchar *name, const gchar *domain)
{
  sqlite3_stmt *statement;
  char *statement_sql;
  gint rc;
  gboolean res = TRUE;

  if (!app_db)
    {
      unity_webapps_app_db_open ();
    }

  statement_sql = sqlite3_mprintf (DELETE_ALL_ACTIONS_SQL, name, domain);

  rc = sqlite3_prepare_v2 (app_db, statement_sql, -1, &statement, NULL);
  if (rc != SQLITE_OK)
    {
      g_critical ("Failed to compile Action DB statement: %s", sqlite3_errmsg (app_db));
      sqlite3_free (statement_sql);
      return FALSE;
    }

  sqlite3_free (statement_sql);

  rc = sqlite3_step (statement);

  if (rc != SQLITE_DONE)
    {
      g_warning ("Error deleting actions: %s", sqlite3_errmsg (app_db));
      res = FALSE;
    }

  sqlite3_finalize (statement);
  return res;
}

#define MAX_ACTIONS_COUNT 10
#define SELECT_ALL_ACTIONS_SQL "SELECT label, page FROM actions WHERE name=%Q AND domain=%Q"

gboolean
unity_webapps_app_db_get_actions (const gchar *name, const gchar *domain, gchar ***labels, gchar ***pages)
{
  sqlite3_stmt *statement;
  char *statement_sql;
  gint rc, i;
  gboolean res = TRUE;

  *labels = NULL;
  *pages = NULL;

  if (!app_db)
    {
      unity_webapps_app_db_open ();
    }

  statement_sql = sqlite3_mprintf (SELECT_ALL_ACTIONS_SQL, name, domain);

  rc = sqlite3_prepare_v2 (app_db, statement_sql, -1, &statement, NULL);
  if (rc != SQLITE_OK)
    {
      g_critical ("Failed to compile Action DB statement: %s", sqlite3_errmsg (app_db));
      sqlite3_free (statement_sql);
      return FALSE;
    }

  sqlite3_free (statement_sql);

  *labels = g_new (gchar*, MAX_ACTIONS_COUNT + 1);
  *pages = g_new (gchar*, MAX_ACTIONS_COUNT + 1);

  for (i = 0, rc = sqlite3_step (statement); i < MAX_ACTIONS_COUNT && rc == SQLITE_ROW; i++, rc = sqlite3_step (statement))
    {
      (*labels)[i] = g_strdup ((const gchar *)sqlite3_column_text (statement, 0));
      (*pages)[i] = g_strdup ((const gchar *)sqlite3_column_text (statement, 1));
    }
  (*labels)[i] = NULL;
  (*pages)[i] = NULL;

  sqlite3_finalize (statement);
  return res;
}
