/*
 * gui-nick.c - nick functions (used by all GUI)
 *
 * Copyright (C) 2003-2019 Sébastien Helleu <flashcode@flashtux.org>
 *
 * This file is part of WeeChat, the extensible chat client.
 *
 * WeeChat is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * WeeChat 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WeeChat.  If not, see <https://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "../core/weechat.h"
#include "../core/wee-config.h"
#include "../core/wee-hashtable.h"
#include "../core/wee-string.h"
#include "../core/wee-utf8.h"
#include "gui-nick.h"
#include "gui-color.h"


/*
 * Hashes a nickname to find color.
 *
 * Returns a number which is the index of color in the nicks colors of option
 * "weechat.color.chat_nick_colors".
 */

int
gui_nick_hash_color (const char *nickname)
{
    uint64_t color;
    uint32_t color_32;
    const char *ptr_nick;

    if (!nickname || !nickname[0])
        return 0;

    if (!config_nick_colors)
        config_set_nick_colors ();

    if (config_num_nick_colors == 0)
        return 0;

    ptr_nick = nickname;
    color = 0;

    switch (CONFIG_INTEGER(config_look_nick_color_hash))
    {
        case CONFIG_LOOK_NICK_COLOR_HASH_DJB2:
            /* variant of djb2 hash */
            color = 5381;
            while (ptr_nick && ptr_nick[0])
            {
                color ^= (color << 5) + (color >> 2) + utf8_char_int (ptr_nick);
                ptr_nick = utf8_next_char (ptr_nick);
            }
            break;
        case CONFIG_LOOK_NICK_COLOR_HASH_SUM:
            /* sum of letters */
            color = 0;
            while (ptr_nick && ptr_nick[0])
            {
                color += utf8_char_int (ptr_nick);
                ptr_nick = utf8_next_char (ptr_nick);
            }
            break;
        case CONFIG_LOOK_NICK_COLOR_HASH_DJB2_32:
            /* variant of djb2 hash (using 32-bit integer) */
            color_32 = 5381;
            while (ptr_nick && ptr_nick[0])
            {
                color_32 ^= (color_32 << 5) + (color_32 >> 2)
                    + utf8_char_int (ptr_nick);
                ptr_nick = utf8_next_char (ptr_nick);
            }
            color = color_32;
            break;
        case CONFIG_LOOK_NICK_COLOR_HASH_SUM_32:
            /* sum of letters (using 32-bit integer) */
            color_32 = 0;
            while (ptr_nick && ptr_nick[0])
            {
                color_32 += utf8_char_int (ptr_nick);
                ptr_nick = utf8_next_char (ptr_nick);
            }
            color = color_32;
            break;
    }

    return (color % config_num_nick_colors);
}

/*
 * Gets forced color for a nick.
 *
 * Returns the name of color (for example: "green"), NULL if no color is forced
 * for nick.
 */

const char *
gui_nick_get_forced_color (const char *nickname)
{
    const char *forced_color;
    char *nick_lower;

    if (!nickname || !nickname[0])
        return NULL;

    forced_color = hashtable_get (config_hashtable_nick_color_force, nickname);
    if (forced_color)
        return forced_color;

    nick_lower = strdup (nickname);
    if (nick_lower)
    {
        string_tolower (nick_lower);
        forced_color = hashtable_get (config_hashtable_nick_color_force,
                                      nick_lower);
        free (nick_lower);
    }

    return forced_color;
}

/*
 * Duplicates a nick and stops at first char in list (using option
 * weechat.look.nick_color_stop_chars).
 *
 * Note: result must be freed after use.
 */

char *
gui_nick_strdup_for_color (const char *nickname)
{
    int char_size, other_char_seen;
    char *result, *pos, utf_char[16];

    if (!nickname)
        return NULL;

    result = malloc (strlen (nickname) + 1);
    pos = result;
    other_char_seen = 0;
    while (nickname[0])
    {
        char_size = utf8_char_size (nickname);
        memcpy (utf_char, nickname, char_size);
        utf_char[char_size] = '\0';

        if (strstr (CONFIG_STRING(config_look_nick_color_stop_chars),
                    utf_char))
        {
            if (other_char_seen)
            {
                pos[0] = '\0';
                return result;
            }
        }
        else
        {
            other_char_seen = 1;
        }
        memcpy (pos, utf_char, char_size);
        pos += char_size;

        nickname += char_size;
    }
    pos[0] = '\0';
    return result;
}

/*
 * Finds a color code for a nick (according to nick letters).
 *
 * Returns a WeeChat color code (that can be used for display).
 */

const char *
gui_nick_find_color (const char *nickname)
{
    int color;
    char *nickname2;
    const char *forced_color, *str_color;

    if (!nickname || !nickname[0])
        return gui_color_get_custom ("default");

    if (!config_nick_colors)
        config_set_nick_colors ();

    if (config_num_nick_colors == 0)
        return gui_color_get_custom ("default");

    nickname2 = gui_nick_strdup_for_color (nickname);

    /* look if color is forced */
    forced_color = gui_nick_get_forced_color (
        (nickname2) ? nickname2 : nickname);
    if (forced_color)
    {
        forced_color = gui_color_get_custom (forced_color);
        if (forced_color && forced_color[0])
        {
            if (nickname2)
                free (nickname2);
            return forced_color;
        }
    }

    /* hash nickname to get color */
    color = gui_nick_hash_color ((nickname2) ? nickname2 : nickname);

    if (nickname2)
        free (nickname2);

    /* return color */
    str_color = gui_color_get_custom (config_nick_colors[color]);
    return (str_color[0]) ? str_color : gui_color_get_custom ("default");
}

/*
 * Finds a color name for a nick (according to nick letters).
 *
 * Returns the name of a color (for example: "green").
 */

const char *
gui_nick_find_color_name (const char *nickname)
{
    int color;
    char *nickname2;
    const char *forced_color;
    static char *default_color = "default";

    if (!nickname || !nickname[0])
        return default_color;

    if (!config_nick_colors)
        config_set_nick_colors ();

    if (config_num_nick_colors == 0)
        return default_color;

    nickname2 = gui_nick_strdup_for_color (nickname);

    /* look if color is forced */
    forced_color = gui_nick_get_forced_color (
        (nickname2) ? nickname2 : nickname);
    if (forced_color)
    {
        if (nickname2)
            free (nickname2);
        return forced_color;
    }

    /* hash nickname to get color */
    color = gui_nick_hash_color ((nickname2) ? nickname2 : nickname);

    if (nickname2)
        free (nickname2);

    /* return color name */
    return config_nick_colors[color];
}
