#include <sqlite3.h>
#include <string.h>
#include "utils.h"
#include "pkgtools.h"

namespace slack {

/**
 * slack::get_file:
 * @curl: curl easy handle.
 * @source_url: source url.
 * @dest: destination.
 *
 * Download the file.
 *
 * Returns: CURLE_OK (zero) on success, non-zero otherwise.
 **/
CURLcode
get_file (CURL **curl, gchar *source_url, gchar *dest)
{
	gchar *dest_dir_name;
	FILE *fout = NULL;
	CURLcode ret;
	glong response_code;

	if ((*curl == NULL) && (!(*curl = curl_easy_init())))
	{
		return CURLE_BAD_FUNCTION_ARGUMENT;
	}

	curl_easy_setopt(*curl, CURLOPT_FOLLOWLOCATION, 1L);
	curl_easy_setopt(*curl, CURLOPT_URL, source_url);

	if (dest == NULL)
	{
		curl_easy_setopt(*curl, CURLOPT_NOBODY, 1L);
		curl_easy_setopt(*curl, CURLOPT_HEADER, 1L);
		ret = curl_easy_perform(*curl);
		curl_easy_getinfo(*curl, CURLINFO_RESPONSE_CODE, &response_code);

		if (response_code != 200)
		{
			ret = CURLE_REMOTE_FILE_NOT_FOUND;
		}
	}
	else
	{
		if (g_file_test(dest, G_FILE_TEST_IS_DIR))
		{
			dest_dir_name = dest;
			dest = g_strconcat(dest_dir_name, g_strrstr(source_url, "/"), NULL);
			g_free(dest_dir_name);
		}
		if ((fout = fopen(dest, "ab")) == NULL)
		{
			return CURLE_WRITE_ERROR;
		}
		curl_easy_setopt(*curl, CURLOPT_WRITEDATA, fout);
		ret = curl_easy_perform(*curl);
	}
	curl_easy_reset(*curl);
	if (fout != NULL)
	{
		fclose(fout);
	}
	return ret;
}

/**
 * slack::split_package_name:
 * Got the name of a package, without version-arch-release data.
 **/
gchar **
split_package_name (const gchar *pkg_filename)
{
	gchar *pkg_full_name;
	gchar **pkg_tokens;

	g_return_val_if_fail(pkg_filename != NULL, NULL);

	gint len = strlen(pkg_filename);
	if (len < 4)
	{
		return NULL;
	}

	if (pkg_filename[len - 4] == '.')
	{
		pkg_tokens = static_cast<gchar **> (g_malloc_n (6, sizeof (gchar *)));

		/* Full name without extension */
		len -= 4;
		pkg_full_name = g_strndup (pkg_filename, len);
		pkg_tokens[3] = g_strdup (pkg_full_name);

		/* The last 3 characters should be the file extension */
		pkg_tokens[4] = g_strdup (pkg_filename + len + 1);
		pkg_tokens[5] = NULL;
	}
	else
	{
		pkg_tokens = static_cast<gchar **> (g_malloc_n (4, sizeof (gchar *)));
		pkg_full_name = g_strdup (pkg_filename);
		pkg_tokens[3] = NULL;
	}

	/* Reverse all of the bytes in the package filename to get the name, version and the architecture */
	g_strreverse (pkg_full_name);
	gchar **reversed_tokens = g_strsplit (pkg_full_name, "-", 4);
	pkg_tokens[0] = g_strreverse (reversed_tokens[3]); /* Name */
	pkg_tokens[1] = g_strreverse (reversed_tokens[2]); /* Version */
	pkg_tokens[2] = g_strreverse (reversed_tokens[1]); /* Architecture */

	g_free (reversed_tokens[0]); /* Build number */
	g_free (reversed_tokens);
	g_free (pkg_full_name);

	return pkg_tokens;
}

/**
 * slack::is_installed:
 * Checks if a package is already installed in the system.
 *
 * Params:
 * 	pkg_fullname = Package name should be looked for.
 *
 * Returns: PK_INFO_ENUM_INSTALLING if pkg_fullname is already installed,
 *          PK_INFO_ENUM_UPDATING if an elder version of pkg_fullname is
 *          installed, PK_INFO_ENUM_UNKNOWN if pkg_fullname is malformed.
 **/
PkInfoEnum
is_installed (const gchar *pkg_fullname)
{
	GFileEnumerator *pkg_metadata_enumerator;
	GFileInfo *pkg_metadata_file_info;
	GFile *pkg_metadata_dir;
	PkInfoEnum ret = PK_INFO_ENUM_INSTALLING;
    const gchar *it;
    guint8 dashes = 0;
	ptrdiff_t pkg_name;

	g_return_val_if_fail(pkg_fullname != NULL, PK_INFO_ENUM_UNKNOWN);

    // We want to find the package name without version for the package we're
    // looking for.
    g_debug("Looking if %s is installed", pkg_fullname);

    for (it = pkg_fullname + strlen(pkg_fullname); it != pkg_fullname; --it)
    {
        if (*it == '-')
        {
            if (dashes == 2)
            {
                break;
            }
            ++dashes;
        }
    }
	if (dashes < 2)
	{
		return PK_INFO_ENUM_UNKNOWN;
	}
    pkg_name = it - pkg_fullname;

	// Read the package metadata directory and comprare all installed packages
    // with ones in the cache.
	pkg_metadata_dir = g_file_new_for_path("/var/log/packages");
	if (!(pkg_metadata_enumerator = g_file_enumerate_children(pkg_metadata_dir,
	                                                          "standard::name",
	                                                          G_FILE_QUERY_INFO_NONE,
	                                                          NULL,
	                                                          NULL)))
	{
		g_object_unref(pkg_metadata_dir);
		return PK_INFO_ENUM_UNKNOWN;
	}

	while ((pkg_metadata_file_info = g_file_enumerator_next_file(pkg_metadata_enumerator, NULL, NULL)))
	{
		const gchar *dir = g_file_info_get_name(pkg_metadata_file_info);
        dashes = 0;

        if (strcmp(dir, pkg_fullname) == 0)
        {
            ret = PK_INFO_ENUM_INSTALLED;
        }
		else
		{
			for (it = dir + strlen(dir); it != dir; --it)
			{
				if (*it == '-')
				{
					if (dashes == 2)
					{
						break;
					}
					++dashes;
				}
			}
			if (pkg_name == (it - dir) && strncmp(pkg_fullname, dir, pkg_name) == 0)
			{
				ret = PK_INFO_ENUM_UPDATING;
			}

		}
		g_object_unref(pkg_metadata_file_info);

		if (ret != PK_INFO_ENUM_INSTALLING) /* If installed */
		{
			break;
		}
	}
	g_object_unref(pkg_metadata_enumerator);
	g_object_unref(pkg_metadata_dir);

	return ret;
}

/**
 * slack::cmp_repo:
 **/
gint
cmp_repo (gconstpointer a, gconstpointer b)
{
	auto repo = static_cast<const Pkgtools *> (a);

	return g_strcmp0 (repo->get_name (), (gchar *) b);
}

}
