#include "e.h"

#define E_TOK_STYLE ":style="

static Eina_Bool          _font_hash_free_cb(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED);
static Eina_Hash         *_e_font_available_hash_add(Eina_Hash *font_hash, const char *full_name);
static E_Font_Properties *_e_font_fontconfig_name_parse(Eina_Hash **font_hash, E_Font_Properties *efp, const char *font);
static char _fn_buf[1024];

EINTERN int
e_font_init(void)
{
   /* all init stuff is in e_config */
   return 1;
}

EINTERN int
e_font_shutdown(void)
{
   /* e_config will do this */
   return 1;
}

E_API void
e_font_apply(void)
{
   char buf[1024];
   const Eina_List *l;
   E_Client *ec;
   E_Font_Default *efd;
   E_Font_Fallback *eff;
   int blen, len;

   /* setup edje fallback list */
   blen = sizeof(buf) - 1;
   buf[0] = 0;
   buf[blen] = 0;
   l = e_config->font_fallbacks;
   if (l)
     {
        eff = eina_list_data_get(l);
        len = strlen(eff->name);
        if (len < blen)
          {
             strcpy(buf, eff->name);
             blen -= len;
          }
        EINA_LIST_FOREACH(eina_list_next(l), l, eff)
          {
             len = 1;
             if (len < blen)
               {
                  strcat(buf, ",");
                  blen -= len;
               }
             len = strlen(eff->name);
             if (len < blen)
               {
                  strcat(buf, eff->name);
                  blen -= len;
               }
          }
        edje_fontset_append_set(buf);
     }
   else
     edje_fontset_append_set(NULL);

   /* setup edje text classes */
   EINA_LIST_FOREACH(e_config->font_defaults, l, efd)
     {
        edje_text_class_set(efd->text_class, efd->font, efd->size);
     }

   /* Update clients */
   if (!e_comp) return;
   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     e_client_frame_recalc(ec);
}

E_API Eina_List *
e_font_available_list(void)
{
   Eina_List *evas_fonts;
   Eina_List *e_fonts;
   Eina_List *l;
   const char *evas_font;

   evas_fonts = evas_font_available_list(e_comp->evas);

   e_fonts = NULL;
   EINA_LIST_FOREACH(evas_fonts, l, evas_font)
     {
        E_Font_Available *efa;

        efa = E_NEW(E_Font_Available, 1);
        efa->name = eina_stringshare_add(evas_font);
        e_fonts = eina_list_append(e_fonts, efa);
     }

   evas_font_available_list_free(e_comp->evas, evas_fonts);

   return e_fonts;
}

E_API void
e_font_available_list_free(Eina_List *available)
{
   E_Font_Available *efa;

   EINA_LIST_FREE(available, efa)
     {
        if (efa->name) eina_stringshare_del(efa->name);
        E_FREE(efa);
     }
}

E_API void
e_font_properties_free(E_Font_Properties *efp)
{
   const char *str;

   EINA_LIST_FREE(efp->styles, str)
     {
        if (str) eina_stringshare_del(str);
     }
   if (efp->name) eina_stringshare_del(efp->name);
   free(efp);
}

static Eina_Bool
_font_hash_free_cb(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata EINA_UNUSED)
{
   E_Font_Properties *efp;

   efp = data;
   e_font_properties_free(efp);
   return 1;
}

E_API void
e_font_available_hash_free(Eina_Hash *hash)
{
   eina_hash_foreach(hash, _font_hash_free_cb, NULL);
   eina_hash_free(hash);
}

E_API E_Font_Properties *
e_font_fontconfig_name_parse(const char *font)
{
   if (!font) return NULL;
   return _e_font_fontconfig_name_parse(NULL, NULL, font);
}

static E_Font_Properties *
_e_font_fontconfig_name_parse(Eina_Hash **font_hash, E_Font_Properties *efp, const char *font)
{
   char *s1;

   s1 = strchr(font, ':');
   if (s1)
     {
        char *s2, *name, *style, *temp;
        int len;

        len = s1 - font;
        name = calloc(sizeof(char), len + 1);
        strncpy(name, font, len);

        /* Get subname (should be english)  */
        s2 = strchr(name, ',');
        if (s2)
          {
             len = s2 - name;
             temp = name;
             name = realloc(name, sizeof(char) * len + 1);
             if (!name)
               {
                  free(temp);
                  return NULL;
               }
             memset(name, 0, sizeof(char) * len + 1);
             strncpy(name, font, len);
          }

        if (strncmp(s1, E_TOK_STYLE, strlen(E_TOK_STYLE)) == 0)
          {
             style = s1 + strlen(E_TOK_STYLE);

             if (font_hash) efp = eina_hash_find(*font_hash, name);
             if (!efp)
               {
                  efp = calloc(1, sizeof(E_Font_Properties));
                  efp->name = eina_stringshare_add(name);
                  if (font_hash)
                    {
                       if (!*font_hash) *font_hash = eina_hash_string_superfast_new(NULL);
                       eina_hash_add(*font_hash, name, efp);
                    }
               }
             s2 = strchr(style, ',');
             if (s2)
               {
                  char *style_old;

                  len = s2 - style;
                  style_old = style;
                  style = calloc(sizeof(char), len + 1);
                  strncpy(style, style_old, len);
                  efp->styles = eina_list_append(efp->styles, eina_stringshare_add(style));
                  free(style);
               }
             else
               efp->styles = eina_list_append(efp->styles, eina_stringshare_add(style));
          }
        free(name);
     }
   else
     {
        if (font_hash) efp = eina_hash_find(*font_hash, font);
        if (!efp)
          {
             efp = calloc(1, sizeof(E_Font_Properties));
             efp->name = eina_stringshare_add(font);
             if (font_hash)
               {
                  if (!*font_hash) *font_hash = eina_hash_string_superfast_new(NULL);
                  eina_hash_add(*font_hash, font, efp);
               }
          }
     }
   return efp;
}

static Eina_Hash *
_e_font_available_hash_add(Eina_Hash *font_hash, const char *full_name)
{
   _e_font_fontconfig_name_parse(&font_hash, NULL, full_name);
   return font_hash;
}

E_API Eina_Hash *
e_font_available_list_parse(Eina_List *list)
{
   Eina_Hash *font_hash;
   Eina_List *next;
   void *key;

   font_hash = NULL;

   /* Populate Default Font Families */
   font_hash = _e_font_available_hash_add(font_hash, "Sans:style=Regular");
   font_hash = _e_font_available_hash_add(font_hash, "Sans:style=Bold");
   font_hash = _e_font_available_hash_add(font_hash, "Sans:style=Oblique");
   font_hash = _e_font_available_hash_add(font_hash, "Sans:style=Bold Oblique");

   font_hash = _e_font_available_hash_add(font_hash, "Serif:style=Regular");
   font_hash = _e_font_available_hash_add(font_hash, "Serif:style=Bold");
   font_hash = _e_font_available_hash_add(font_hash, "Serif:style=Oblique");
   font_hash = _e_font_available_hash_add(font_hash, "Serif:style=Bold Oblique");

   font_hash = _e_font_available_hash_add(font_hash, "Monospace:style=Regular");
   font_hash = _e_font_available_hash_add(font_hash, "Monospace:style=Bold");
   font_hash = _e_font_available_hash_add(font_hash, "Monospace:style=Oblique");
   font_hash = _e_font_available_hash_add(font_hash, "Monospace:style=Bold Oblique");

   EINA_LIST_FOREACH(list, next, key)
     font_hash = _e_font_available_hash_add(font_hash, key);

   return font_hash;
}

E_API const char *
e_font_fontconfig_name_get(const char *name, const char *style)
{
   char buf[256];

   if (!name) return NULL;
   if (!style || style[0] == 0) return eina_stringshare_add(name);
   snprintf(buf, 256, "%s"E_TOK_STYLE "%s", name, style);
   return eina_stringshare_add(buf);
}

E_API void
e_font_fallback_clear(void)
{
   E_Font_Fallback *eff;

   EINA_LIST_FREE(e_config->font_fallbacks, eff)
     {
        if (eff->name) eina_stringshare_del(eff->name);
        E_FREE(eff);
     }
}

E_API void
e_font_fallback_append(const char *font)
{
   E_Font_Fallback *eff;

   e_font_fallback_remove(font);

   eff = E_NEW(E_Font_Fallback, 1);
   eff->name = eina_stringshare_add(font);
   e_config->font_fallbacks = eina_list_append(e_config->font_fallbacks, eff);
}

E_API void
e_font_fallback_prepend(const char *font)
{
   E_Font_Fallback *eff;

   e_font_fallback_remove(font);

   eff = E_NEW(E_Font_Fallback, 1);
   eff->name = eina_stringshare_add(font);
   e_config->font_fallbacks = eina_list_prepend(e_config->font_fallbacks, eff);
}

E_API void
e_font_fallback_remove(const char *font)
{
   Eina_List *next;
   E_Font_Fallback *eff;

   EINA_LIST_FOREACH(e_config->font_fallbacks, next, eff)
     {
        if (!e_util_strcmp(eff->name, font))
          {
             e_config->font_fallbacks =
               eina_list_remove_list(e_config->font_fallbacks, next);
             if (eff->name) eina_stringshare_del(eff->name);
             E_FREE(eff);
             break;
          }
     }
}

E_API Eina_List *
e_font_fallback_list(void)
{
   return e_config->font_fallbacks;
}

E_API void
e_font_default_set(const char *text_class, const char *font, Evas_Font_Size size)
{
   E_Font_Default *efd;
   Eina_List *next;

   /* search for the text class */
   EINA_LIST_FOREACH(e_config->font_defaults, next, efd)
     {
        if (!strcmp(efd->text_class, text_class))
          {
             if (efd->font) eina_stringshare_del(efd->font);
             efd->font = eina_stringshare_add(font);
             efd->size = size;
             /* move to the front of the list */
             e_config->font_defaults =
               eina_list_remove_list(e_config->font_defaults, next);
             e_config->font_defaults =
               eina_list_prepend(e_config->font_defaults, efd);
             return;
          }
     }

   /* the text class doesn't exist */
   efd = E_NEW(E_Font_Default, 1);
   efd->text_class = eina_stringshare_add(text_class);
   efd->font = eina_stringshare_add(font);
   efd->size = size;

   e_config->font_defaults = eina_list_prepend(e_config->font_defaults, efd);
}

/*
 * returns a pointer to the data, return null if nothing if found.
 */
E_API E_Font_Default *
e_font_default_get(const char *text_class)
{
   E_Font_Default *efd = NULL, *defd = NULL;
   Eina_List *next;

   /* search for the text class */
   EINA_LIST_FOREACH(e_config->font_defaults, next, efd)
     {
        if (!strcmp(efd->text_class, text_class))
          {
             /* move to the front of the list */
             e_config->font_defaults =
               eina_list_remove_list(e_config->font_defaults, next);
             e_config->font_defaults =
               eina_list_prepend(e_config->font_defaults, efd);
             return efd;
          }
        if (!strcmp(efd->text_class, "default")) defd = efd;
     }
   if (!defd) defd = efd;
   return defd;
}

E_API void
e_font_default_remove(const char *text_class)
{
   E_Font_Default *efd;
   Eina_List *next;

   /* search for the text class */
   EINA_LIST_FOREACH(e_config->font_defaults, next, efd)
     {
        if (!e_util_strcmp(efd->text_class, text_class))
          {
             e_config->font_defaults =
               eina_list_remove_list(e_config->font_defaults, next);
             if (efd->text_class) eina_stringshare_del(efd->text_class);
             if (efd->font) eina_stringshare_del(efd->font);
             E_FREE(efd);
             edje_text_class_del(text_class);
             return;
          }
     }
}

E_API Eina_List *
e_font_default_list(void)
{
   return e_config->font_defaults;
}

/* return the default font name with fallbacks, font size is returned
 * in size_ret. This function is needed when all hell breaks loose and
 * we need a font name and size.
 */
E_API const char *
e_font_default_string_get(const char *text_class, Evas_Font_Size *size_ret)
{
   E_Font_Default *efd;
   Eina_List *next;
   E_Font_Fallback *eff;
   int blen, len;

   _fn_buf[0] = 0;
   efd = e_font_default_get(text_class);
   if (!efd)
     {
        if (size_ret) *size_ret = 0;
        return "";
     }
   blen = sizeof(_fn_buf) - 1;

   len = strlen(efd->font);
   if (len < blen)
     {
        strcpy(_fn_buf, efd->font);
        blen -= len;
     }

   EINA_LIST_FOREACH(e_config->font_fallbacks, next, eff)
     {
        len = 1;
        if (len < blen)
          {
             strcat(_fn_buf, ",");
             blen -= len;
          }
        len = strlen(eff->name);
        if (len < blen)
          {
             strcat(_fn_buf, eff->name);
             blen -= len;
          }
     }

   if (size_ret) *size_ret = efd->size;
   return _fn_buf;
}

