From 03a60d9efcd03948ac0cd108f3e44a487daeefd0 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Fri, 10 Apr 2020 16:08:11 +0200
Subject: [PATCH] Adding functions to manipulate stickers in DB

---
 inc/lektor/database.h   |  11 ++
 meson.build             |   1 +
 src/commands.c          |   3 +-
 src/database/stickers.c | 232 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 246 insertions(+), 1 deletion(-)
 create mode 100644 src/database/stickers.c

diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index 26cda41e..d5632e3c 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -124,3 +124,14 @@ bool database_plt_add_uri(sqlite3 *db, const char *name, struct lkt_uri_t *uri);
 /* User control, yeah, MPD authentification sucks. */
 bool database_user_authentificate(sqlite3 *db, const char *password);
 bool database_user_add(sqlite3 *db, const char *username, const char *password);
+
+/* Stickers manipulations. */
+typedef bool (*database_sticker_callback_t)(void *args, const char *sticker, const char *type,
+        int uri, int value);
+
+bool database_sticker_create(sqlite3 *db, const char *name);
+bool database_sticker_delete(sqlite3 *db, const char *name);
+bool database_sticker_list(sqlite3 *db, const char *type, void *args, database_sticker_callback_t call);
+bool database_sticker_set(sqlite3 *db, const char *type, const char *name, int uri, int value);
+bool database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, void *args,
+                          database_sticker_callback_t call);
diff --git a/meson.build b/meson.build
index bf5f440b..d2a9b131 100644
--- a/meson.build
+++ b/meson.build
@@ -26,6 +26,7 @@ dep_mpv = dependency('mpv', required : false)
 core_sources =  [ 'src/mkv/bufferfd.c'
                 , 'src/mkv/write.c'
                 , 'src/commands.c'
+                , 'src/database/stickers.c'
                 , 'src/database/open.c'
                 , 'src/database/queue.c'
                 , 'src/database/update.c'
diff --git a/src/commands.c b/src/commands.c
index 9ba427ff..23792ffc 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -251,7 +251,8 @@ command_stop(sqlite3 *db, struct lkt_win *win, enum mpd_idle_flag *watch_mask_pt
 }
 
 bool
-command_add(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_flag *watch_mask_ptr)
+command_add(sqlite3 *db, struct lkt_win *win, char *args[LKT_MESSAGE_ARGS_MAX],
+            enum mpd_idle_flag *watch_mask_ptr)
 {
     if (args == NULL) {
         fprintf(stderr, " ! command_add: invalid NULL arguments\n");
diff --git a/src/database/stickers.c b/src/database/stickers.c
new file mode 100644
index 00000000..0100955c
--- /dev/null
+++ b/src/database/stickers.c
@@ -0,0 +1,232 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <lektor/macro.h>
+#include <lektor/database.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+bool
+database_sticker_create(sqlite3 *db, const char *name)
+{
+    static const char *INSERT =
+        "WITH id_max (id) AS (SELECT MAX(id) FROM 'stickers')"
+        "INSERT INTO 'stickers' (id, name)"
+        " SELECT"
+        "  CASE WHEN id_max.id IS NULL THEN 1 ELSE id_max.id + 1 END,"
+        "  ?"
+        " FROM id_max"
+        "WHERE NOT EXISTS (SELECT 1 FROM 'stickers' WHERE name = ?);";
+    sqlite3_stmt *stmt;
+    int ret = false;
+
+    if (strlen(name) == 0) {
+        fprintf(stderr, " ! database_sticker_create: A sticker name must be at least one character long\n");
+        return ret;
+    }
+
+    SQLITE_PREPARE(db, stmt, INSERT, error);
+    SQLITE_BIND_TEXT(db, stmt, 1, name, error);
+    SQLITE_BIND_TEXT(db, stmt, 2, name, error);
+
+    if (sqlite3_step(stmt) != SQLITE_OK) {
+        fprintf(stderr, " ! database_sticker_create: Failed to create sticker '%s': %s\n",
+                name, sqlite3_errmsg(db));
+        goto error;
+    }
+
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_sticker_delete(sqlite3 *db, const char *name)
+{
+    static const char *INSERT =
+        "DELETE FROM 'stickers' WHERE name = ?;";
+    sqlite3_stmt *stmt;
+    int ret = false;
+
+    if (strlen(name) == 0) {
+        fprintf(stderr, " ! database_sticker_delete: A sticker name must be at least one character long\n");
+        return ret;
+    }
+
+    SQLITE_PREPARE(db, stmt, INSERT, error);
+    SQLITE_BIND_TEXT(db, stmt, 1, name, error);
+
+    if (sqlite3_step(stmt) != SQLITE_OK) {
+        fprintf(stderr, " ! database_sticker_delete: Failed to delete sticker '%s': %s\n",
+                name, sqlite3_errmsg(db));
+        goto error;
+    }
+
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_sticker_list(sqlite3 *db, const char *type, void *args,
+                      database_sticker_callback_t call)
+{
+    const char *SQL = NULL;
+    int ret = false, uri, value;
+    sqlite3_stmt *stmt;
+    const char *sticker;
+
+    if (type == NULL)
+        SQL =
+            "SELECT name, sts.id, value FROM 'stickers' LEFT OUTER JOIN "
+            "( SELECT id, sticker, value FROM 'stickers.song'"
+            "  UNION"
+            "  SELECT id, sticker, value FROM 'stickers.plt'"
+            ") AS sts"
+            "ON sts.sticker = 'stickers'.id;";
+    else if (!strcasecmp(type, "plt"))
+        SQL =
+            "SELECT name, 'stickers.plt'.id, value "
+            "FROM 'stickers' "
+            "LEFT OUTER JOIN 'stickers' "
+            "ON 'stickers'.id = 'stickers.plt'.sticker;";
+    else if (!strcasecmp(type, "song"))
+        SQL =
+            "SELECT name, 'stickers.song'.id, value "
+            "FROM 'stickers' "
+            "LEFT OUTER JOIN 'stickers' "
+            "ON 'stickers'.id = 'stickers.song'.sticker;";
+    else {
+        fprintf(stderr, " . database_sticker_list: type '%s' is invalid\n", type);
+        return false;
+    }
+
+    SQLITE_PREPARE(db, stmt, SQL, error);
+
+    for (;;) {
+        switch (sqlite3_step(stmt)) {
+        case SQLITE_ROW:
+            sticker = (const char *) sqlite3_column_text(stmt, 0);
+            uri     = sqlite3_column_int(stmt, 1);
+            value   = sqlite3_column_int(stmt, 2);
+            if (!call(args, sticker, type, uri, value))
+                goto error;
+            continue;
+
+        case SQLITE_DONE:
+            goto end_loop;
+
+        default:
+            goto error;
+        }
+    }
+
+end_loop:
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_sticker_set(sqlite3 *db, const char *type, const char *name, int uri, int value)
+{
+    const char *SQL = NULL;
+    sqlite3_stmt *stmt;
+    int ret = false;
+
+    /* Bindings:
+     * 1 -> the uri of the plalist or kara
+     * 2 -> the value of the sticker
+     * 3 -> the name of the sticker
+     */
+
+    if (!strcasecmp(type, "song"))
+        SQL =
+            "INSERT OR REPLACE INTO 'stickers.song' (id, sticker, value) "
+            "SELECT ?, 'stickers'.id, ? "
+            "FROM 'stickers'"
+            "WHERE 'stickers'.id = ?;\n";
+    else if (!strcasecmp(type, "song"))
+        SQL =
+            "INSERT OR REPLACE INTO 'stickers.plt' (id, sticker, value) "
+            "SELECT ?, 'stickers'.id, ? "
+            "FROM 'stickers'"
+            "WHERE 'stickers'.id = ?;\n";
+    else {
+        fprintf(stderr, " . database_sticker_set: Type '%s' is invalid\n", type);
+        return false;
+    }
+
+    SQLITE_PREPARE(db, stmt, SQL, error);
+    SQLITE_BIND_INT(db, stmt, 1, uri, error);
+    SQLITE_BIND_INT(db, stmt, 2, value, error);
+    SQLITE_BIND_TEXT(db, stmt, 3, name, error);
+
+    if (sqlite3_step(stmt) != SQLITE_OK) {
+        fprintf(stderr, " ! database_sticker_set: Failed to update or set sticker '%s' for "
+                "'%s' %d: %s\n", name, type, uri, sqlite3_errmsg(db));
+        goto error;
+    }
+
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
+
+bool
+database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, void *args,
+                     database_sticker_callback_t call)
+{
+    const char *SQL = NULL;
+    sqlite3_stmt *stmt;
+    int ret = false, value;
+
+    if (!strcasecmp(type, "song"))
+        SQL =
+            "SELECT value "
+            "FROM 'stickers' "
+            "JOIN 'stickers.song'"
+            " ON 'stickers'.id = 'stickers.song'.sticker"
+            " AND 'stickers'.name = ?;\n";
+    else if (!strcasecmp(type, "song"))
+        SQL =
+            "SELECT value "
+            "FROM 'stickers' "
+            "JOIN 'stickers.song'"
+            " ON 'stickers'.id = 'stickers.song'.sticker"
+            " AND 'stickers'.name = ?;\n";
+    else {
+        fprintf(stderr, " . database_sticker_get: Type '%s' is invalid\n", type);
+        return false;
+    }
+
+    SQLITE_PREPARE(db, stmt, SQL, error);
+    SQLITE_BIND_TEXT(db, stmt, 1, name, error);
+
+    for (;;) {
+        switch (sqlite3_step(stmt)) {
+        case SQLITE_ROW:
+            value = sqlite3_column_int(stmt, 1);
+            if (!call(args, name, type, uri, value))
+                goto error;
+            continue;
+
+        case SQLITE_DONE:
+            goto end_loop;
+
+        default:
+            goto error;
+        }
+    }
+
+end_loop:
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
-- 
GitLab