/*
 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
 * Copyright (C) 2004-2023 Kim Woelders
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "E.h"
#include "emodule.h"
#include "file.h"
#include "user.h"
#include "util.h"
#include "session.h"

/* Update Mode.theme.paths (theme path list) */
static void
_ThemePathsUpdate(void)
{
    char            paths[FILEPATH_LEN_MAX];

    Esnprintf(paths, sizeof(paths), "%s/themes:%s/.themes:%s/themes:%s",
              EDirUserConf(), userhome(), EDirRoot(),
              (Conf.theme.extra_path) ? Conf.theme.extra_path : "");
    EFREE_DUP(Mode.theme.paths, paths);
}

/* Check if this is a theme dir */
static int
_ThemeCheckDir(const char *path, const char *px)
{
    static const char *const theme_files[] = {
        "init.cfg",
#if 0
        "epplets/epplets.cfg",
#endif
        NULL
    };
    const char     *tf;
    int             i;
    char            s[FILEPATH_LEN_MAX];

    if (EDebug(EDBUG_TYPE_CONFIG))
        Eprintf("%s: %s%s\n", __func__, path, px);

    for (i = 0; (tf = theme_files[i]); i++)
    {
        Esnprintf(s, sizeof(s), "%s%s/%s", path, px, tf);
        if (!isfile(s))
            return 0;
    }

    return 1;
}

static int
_ThemeCheckPath1(const char *path)
{
    if (_ThemeCheckDir(path, ""))
        return 1;

    if (_ThemeCheckDir(path, "/e16"))
        return 1;

    return 0;
}

static char    *
_ThemeCheckPath(const char *path)
{
    char            ss[FILEPATH_LEN_MAX];

    if (EDebug(EDBUG_TYPE_CONFIG))
        Eprintf("%s: %s\n", __func__, path);

    if (_ThemeCheckDir(path, ""))
        return Estrdup(path);

    if (_ThemeCheckDir(path, "/e16"))
    {
        Esnprintf(ss, sizeof(ss), "%s/e16", path);
        return Estrdup(ss);
    }

    return NULL;
}

char           *
ThemePathName(const char *path)
{
    const char     *p;
    char           *s;

    if (!path)
        return NULL;
    p = strrchr(path, '/');
    if (!p)
        return Estrdup(path);   /* Name only */
    if (strcmp(p + 1, "e16"))
        return Estrdup(p + 1);  /* Regular path */

    /* <path>/<themename>/e16 */
    s = Estrdup(path);
    s[p - path] = '\0';
    p = strrchr(s, '/');
    if (!p)
        return s;               /* Should not happen */
    p++;
    memmove(s, p, strlen(p) + 1);
    return s;
}

static void
_append_merge_dir(char *dir, char ***list, int *count)
{
    char            ss[4000], s1[FILEPATH_LEN_MAX];
    char          **str, *s;
    int             i, num;

    str = E_ls(dir, &num);
    if (!str)
        return;

    for (i = 0; i < num; i++)
    {
        if (!strcmp(str[i], "DEFAULT"))
            continue;

        Esnprintf(ss, sizeof(ss), "%s/%s", dir, str[i]);

        if (isdir(ss))
        {
            if (_ThemeCheckPath1(ss))
                goto got_one;
            continue;
        }

        if (!isfile(ss))
            continue;

        s = strrchr(ss, '.');
        if (!s)
            continue;

        if (strcmp(s + 1, "etheme") == 0)
        {
            Esnprintf(s1, sizeof(s1), "%s/themes/%s", EDirUserConf(), str[i]);
            s = strrchr(s1, '.');
            if (!s)
                continue;
            *s = '\0';
            if (!isdir(s1))
                goto got_one;
        }

        if (strcmp(s + 1, "theme") == 0)
        {
            Esnprintf(s1, sizeof(s1), "%s/.themes/%s", userhome(), str[i]);
            s = strrchr(s1, '.');
            if (!s)
                continue;
            *s = '\0';
            if (!isdir(s1))
                goto got_one;
        }

        continue;

      got_one:
        (*count)++;
        *list = EREALLOC(char *, *list, *count);

        (*list)[(*count) - 1] = Estrdup(ss);
    }
    StrlistFree(str, num);
}

char          **
ThemesList(int *number)
{
    char          **lst, **list;
    int             i, num, count;

    _ThemePathsUpdate();
    lst = StrlistFromString(Mode.theme.paths, ':', &num);

    count = 0;
    list = NULL;
    for (i = 0; i < num; i++)
        _append_merge_dir(lst[i], &list, &count);

    StrlistFree(lst, num);

    *number = count;
    return list;
}

static char    *
_ThemeExtract(const char *path)
{
    char            th[4000];
    FILE           *f;
    unsigned char   buf[262];
    size_t          ret;
    const char     *p;
    char            name[128], *type;

    if (EDebug(EDBUG_TYPE_CONFIG))
        Eprintf("%s: %s\n", __func__, path);

    /* its a file - check its type */
    f = fopen(path, "r");
    if (!f)
        return NULL;
    ret = fread(buf, 1, sizeof(buf), f);
    memset(buf + ret, 0, sizeof(buf) - ret);
    fclose(f);

    p = strrchr(path, '/');
    p = (p) ? p + 1 : path;
    Esnprintf(name, sizeof(name), "%.127s", p);
    type = strchr(name, '.');
    if (type)
        *type++ = '\0';

    if (type && strcmp(type, "theme") == 0)
    {
        Esnprintf(th, sizeof(th), "%s/.themes", userhome());
        if (!isdir(th))
            E_md(th);
        Esnprintf(th, sizeof(th), "%s/.themes/%s", userhome(), name);
    }
    else
        Esnprintf(th, sizeof(th), "%s/themes/%s", EDirUserConf(), name);

    /* check magic numbers */
    if ((buf[0] == 31) && (buf[1] == 139))
    {
        /* gzipped tarball */
        E_md(th);
        Esystem("gzip -d -c < %s | (cd %s ; tar -xf -)", path, th);
    }
    else if ((buf[257] == 'u') && (buf[258] == 's') &&
             (buf[259] == 't') && (buf[260] == 'a') && (buf[261] == 'r'))
    {
        /* vanilla tarball */
        E_md(th);
        Esystem("cat %s | (cd %s ; tar -xf -)", path, th);
    }
    else
        return NULL;

    return _ThemeCheckPath(th);
}

char           *
ThemePathFind(const char *theme)
{
    static const char *const default_themes[] = {
        "DEFAULT", "winter", "BrushedMetal-Tigert", "ShinyMetal", NULL
    };
    char            tpbuf[4000], *path;
    char          **lst;
    int             i, j, num;

    if (EDebug(EDBUG_TYPE_CONFIG))
        Eprintf("%s: %s\n", __func__, theme);

    _ThemePathsUpdate();

    if (!theme || !theme[0])
        theme = NULL;           /* Lookup default */
    else if (!strcmp(theme, "-"))       /* Use fallbacks */
        return NULL;
    else if (exists(theme))
        goto check;

    lst = StrlistFromString(Mode.theme.paths, ':', &num);

    i = 0;
    do
    {
        if (!theme)
            goto next;
        for (j = 0; j < num; j++)
        {
            Esnprintf(tpbuf, sizeof(tpbuf), "%s/%s", lst[j], theme);
            if (exists(tpbuf))
            {
                theme = tpbuf;
                goto done;
            }
        }
      next:
        theme = default_themes[i++];
    }
    while (theme);

  done:
    StrlistFree(lst, num);

  check:
    if (theme)
    {
        path = NULL;
        if (isdir(theme))
            path = _ThemeCheckPath(theme);
        else if (isfile(theme))
            path = _ThemeExtract(theme);
        if (path)
            return path;
    }

    /* No theme found yet, just find any theme */
    lst = ThemesList(&num);
    if (!lst)
        return NULL;
    path = Estrdup(lst[0]);
    StrlistFree(lst, num);

    return path;
}

void
ThemeFind(const char *theme)
{
    char            name[4000];
    char            namx[FILEPATH_LEN_MAX];
    const char     *p;
    char           *path, *s;

    if (EDebug(EDBUG_TYPE_CONFIG))
        Eprintf("%s: %s\n", __func__, theme);

    name[0] = '\0';
    p = (theme && *theme != ':') ? theme : Conf.theme.name;
    if (p)
        snprintf(name, sizeof(name), "%s", p);

    s = strchr(name, ':');
    if (s)
        *s++ = '\0';

    p = (theme && *theme == ':') ? theme + 1 : s;
    EFREE_DUP(Mode.theme.variant, p);

    path = ThemePathFind(name);

    if (!path && strcmp(name, "-"))
    {
        Alert(_("No themes were found in the default directories:\n"
                " %s\n"
                "Proceeding from here is mostly pointless.\n"),
              Mode.theme.paths);
    }

    if (theme)
    {
        Efree(Conf.theme.name);
        if (isfile(theme))
            Conf.theme.name = ThemePathName(path);
        else
        {
            s = namx;
            if (Mode.theme.variant)
                snprintf(namx, sizeof(namx), "%s:%s", name, Mode.theme.variant);
            else
                s = name;
            Conf.theme.name = Estrdup(s);
        }
    }

    EFREE_SET(Mode.theme.path, (path) ? path : Estrdup("-"));
}

#if ENABLE_DIALOGS
#include "dialog.h"
#include "settings.h"
/*
 * Configuration dialog
 */
static char     tmp_use_theme_font;
static char     tmp_use_alt_font;

static void
_DlgThemeApply(Dialog *d __UNUSED__, int val __UNUSED__, void *data __UNUSED__)
{
    if (Conf.theme.use_theme_font_cfg == tmp_use_theme_font &&
        Conf.theme.use_alt_font_cfg == tmp_use_alt_font)
        return;

    DialogOK(_("Message"), _("Changes will take effect after restart"));

    Conf.theme.use_theme_font_cfg = tmp_use_theme_font;
    Conf.theme.use_alt_font_cfg = tmp_use_alt_font;
    autosave();
}

static void
_DlgThemeFill(Dialog *d __UNUSED__, DItem *table, void *data __UNUSED__)
{
    DItem          *di;
    char            buf[1024];

    tmp_use_theme_font = Conf.theme.use_theme_font_cfg;
    tmp_use_alt_font = Conf.theme.use_alt_font_cfg;

    DialogItemTableSetOptions(table, 2, 0, 0, 0);

    di = DialogAddItem(table, DITEM_CHECKBUTTON);
    DialogItemSetColSpan(di, 2);
    DialogItemSetText(di, _("Use theme font configuration"));
    DialogItemCheckButtonSetPtr(di, &tmp_use_theme_font);

    di = DialogAddItem(table, DITEM_CHECKBUTTON);
    DialogItemSetColSpan(di, 2);
    Esnprintf(buf, sizeof(buf), _("Use alternate font configuration (%s)"),
              Conf.theme.font_cfg ? Conf.theme.font_cfg : _("not set"));
    DialogItemSetText(di, buf);
    DialogItemCheckButtonSetPtr(di, &tmp_use_alt_font);
}

const DialogDef DlgTheme = {
    "CONFIGURE_AUDIO",
    N_("Theme"), N_("Theme Settings"),
    0,
    SOUND_SETTINGS_MISCELLANEOUS,
    "pix/miscellaneous.png",
    N_("Enlightenment Theme\n" "Settings Dialog"),
    _DlgThemeFill,
    DLG_OAC, _DlgThemeApply, NULL
};
#endif                          /* ENABLE_DIALOGS */

/*
 * Theme module
 */

static void
ThemesIpc(const char *params)
{
    const char     *p;
    char            cmd[128], prm[128];
    int             len;

    cmd[0] = prm[0] = '\0';
    p = params;
    if (p)
    {
        len = 0;
        sscanf(p, "%100s %100s %n", cmd, prm, &len);
        p += len;
    }

    if (!p || cmd[0] == '?')
    {
        char           *path;

        IpcPrintf("Name: %s\n", (Conf.theme.name) ? Conf.theme.name : "-");
        IpcPrintf("Full: %s\n", Mode.theme.path);
        path = ThemePathFind(NULL);
        IpcPrintf("Default: %s\n", path);
        Efree(path);
        IpcPrintf("Path: %s\n", Mode.theme.paths);
    }
    else if (!strncmp(cmd, "list", 2))
    {
        char          **lst;
        int             i, num;

        lst = ThemesList(&num);
        if (!lst)
            return;
        for (i = 0; i < num; i++)
            IpcPrintf("%s\n", lst[i]);
        StrlistFree(lst, num);
    }
    else if (!strcmp(cmd, "use"))
    {
        /* FIXME - ThemeCheckIfValid(s) */
        SessionExit(EEXIT_THEME, prm);
    }
}

static const IpcItem ThemeIpcArray[] = {
    {
     ThemesIpc,
     "theme", "th",
     "Theme commands",
     "  theme             Show current theme\n"
     "  theme list        Show all themes\n"
     "  theme use <name>  Switch to theme <name>\n" }
    ,
};

static const CfgItem ThemeCfgItems[] = {
    CFG_ITEM_INT(Conf.theme, cache_ver, 0),
    CFG_ITEM_STR(Conf.theme, name),
    CFG_ITEM_STR(Conf.theme, extra_path),
    CFG_ITEM_BOOL(Conf.theme, use_theme_font_cfg, 0),
    CFG_ITEM_BOOL(Conf.theme, use_alt_font_cfg, 0),
    CFG_ITEM_STR(Conf.theme, font_cfg),
};

/*
 * Module descriptor
 */
extern const EModule ModTheme;

const EModule   ModTheme = {
    "theme", "th",
    NULL,
    MOD_ITEMS(ThemeIpcArray),
    MOD_ITEMS(ThemeCfgItems)
};
