#include <stdio.h>
#include <stdlib.h>

#include <sqlite3.h>

#include "stmt.h"
#include "irccolors.h"
#include "error.h"
#include "irc.h"

#define DBFILE "test.sqlite"

#define DBERR(line) do {                                                \
    const int e = line;                                                 \
    if(e != SQLITE_OK && e != SQLITE_ROW && e != SQLITE_DONE)           \
    {                                                                   \
      fprintf(stderr,                                                   \
              "sqlite (%d): %s\n",                                      \
              sqlite3_errcode(connection), sqlite3_errmsg(connection)); \
      exit(DB_ERROR);                                                   \
    }                                                                   \
  } while (0)

static sqlite3 * connection = NULL;

DECL int
api_init(void)
{
	DBERR(sqlite3_open(DBFILE, &connection));
	// dont you fucking dare to remove this spacing
	DBERR(stmt_prepare(remind_stmt));
	DBERR(stmt_prepare(set_repo_stmt));
	DBERR(stmt_prepare(get_nth_id_stmt));
    DBERR(stmt_prepare(new_assignment_stmt));
    DBERR(stmt_prepare(purge_assignments_stmt));
	return 0;
}

DECL void
api_rope(void)
{
    DBERR(sqlite3_finalize(remind_stmt));
    DBERR(sqlite3_finalize(set_repo_stmt));
    DBERR(sqlite3_finalize(get_nth_id_stmt));
    DBERR(sqlite3_finalize(new_assignment_stmt));
    DBERR(sqlite3_finalize(purge_assignments_stmt));
    sqlite3_close(connection);
}

DECL void
rope(void)
{
  if (session)
  { irc_destroy_session(session); }
  api_rope();
}

DECL char *
remind(char * who)
{
	char * r;
	char * title;
	char * desc;
	char * repo;
	DBERR(sqlite3_reset(remind_stmt));
	DBERR(sqlite3_bind_text(remind_stmt, 1, who, -1, SQLITE_STATIC));
	const int i = sqlite3_step(remind_stmt);
	DBERR(i);
	if (i == SQLITE_ROW)
	{
	  title = (char *) sqlite3_column_text(remind_stmt, 0);
	  title = strdup(title);
	  desc = (char *) sqlite3_column_text(remind_stmt, 1);
	  if (desc) { desc = strdup(desc); } else { desc = ""; }
	  repo = (char *) sqlite3_column_text(remind_stmt, 3);
	  if (repo) { repo = strdup(repo); } else { repo = "<no link available>"; }
	  asprintf(&r,
             IRC_RED "%s: " IRC_YELLOW "%s" IRC_GREEN
             " (@" IRC_BLUE "%s" IRC_GREEN ")" IRC_STOP,
             title, desc, repo);
	}
	else
	{
	  r = strdup(IRC_RED "No current assignment." IRC_STOP);
	}
	return r;
}

DECL void
set_repo(char const * const who,
         char const * const link)
{
	DBERR(sqlite3_reset(set_repo_stmt));
	DBERR(sqlite3_bind_text(set_repo_stmt, 1, link, -1, SQLITE_STATIC));
	DBERR(sqlite3_bind_text(set_repo_stmt, 2, who, -1, SQLITE_STATIC));
	DBERR(sqlite3_step(set_repo_stmt));
}

DECL int
rtos(void * data,
  int argc,
  char** argv,
  char** colname
){
  (void) colname;

  char *const *const r = (char**)data;
  for(int i = 0; i < argc; i++){
	strcat(*r, "|");
	if(argv[i]){
		strcat(*r, argv[i]);
	}
	else
	{
		strcat(*r, "NULL");
	}
  }
  strcat(*r, "|\n");

  return 0;
}

DECL char *
dump()
{
  char* errmsg;
  /* TODO: allow for reallocing in rtos, start with a smaller value */
  char* r = (char*)calloc(sizeof(char), 10000);
  DBERR(sqlite3_exec(connection, dump_stmt, rtos, &r, &errmsg));
  return r;
}

DECL char *
raw(char const * const sql)
{
  char* errmsg;
  /* TODO: allow for reallocing in rtos, start with a smaller value */
  char *r = (char*)calloc(sizeof(char), 10000);
  sqlite3_exec(connection, sql, rtos, &r, &errmsg);
  if (errmsg){
    free(r);
	r = errmsg;
  } else { strcat(r, "\00"); }
  return r;
}


DECL int
get_project_count_callback(void* data, int argc, char** argv, char** colname)
{
	(void)argc;
	(void)colname;
    int* count = (int*)data;
    *count = atoi(argv[0]);
    return 0;
}

DECL int
get_project_count()
{
    int r = 0;

    char const * sql = "SELECT COUNT(*) FROM project;";
    DBERR(sqlite3_exec(connection, sql, get_project_count_callback, &r, NULL));

    return r;
}

DECL int
get_nth_id(const int i)
{
    int r;
	DBERR(sqlite3_reset(get_nth_id_stmt));
    DBERR(sqlite3_bind_int(get_nth_id_stmt, 1, i));
    DBERR(sqlite3_step(get_nth_id_stmt));
    r = sqlite3_column_int(get_nth_id_stmt, 0);
    return r;
}

DECL void
new_assignment(char const * const who, const int project)
{
	DBERR(sqlite3_reset(new_assignment_stmt));
    DBERR(sqlite3_bind_text(new_assignment_stmt, 1, who, -1, SQLITE_STATIC));
    DBERR(sqlite3_bind_int(new_assignment_stmt, 2, project));
    DBERR(sqlite3_step(new_assignment_stmt));
}

DECL void
random_assign(char const * const who)
{
	int i = rand() % get_project_count();
	i = get_nth_id(i);
	new_assignment(who, i);
}

DECL void
purge_assignments(char const * const who)
{
	DBERR(sqlite3_reset(purge_assignments_stmt));
  DBERR(sqlite3_bind_text(purge_assignments_stmt, 1, who, -1, SQLITE_STATIC));
  DBERR(sqlite3_step(purge_assignments_stmt));
}