/**************************************************************************
 *
 * Copyright 2010 LunarG, Inc.  All Rights Reserved.
 *
 * 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, sub license, 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 (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * 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 NON-INFRINGEMENT.
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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 "util/u_memory.h"
#include "cso_cache/cso_hash.h"

#include "text.h"
#include "image.h"
#include "path.h"

#ifdef OPENVG_VERSION_1_1

struct vg_font {
   struct vg_object base;
   struct cso_hash *glyphs;
};

struct vg_glyph {
   struct vg_object *object; /* it could be NULL */
   VGboolean is_hinted;
   VGfloat glyph_origin[2];
   VGfloat escapement[2];
};

static VGboolean del_glyph(struct vg_font *font,
                           VGuint glyphIndex)
{
   struct vg_glyph *glyph;

   glyph = (struct vg_glyph *)
      cso_hash_take(font->glyphs, (unsigned) glyphIndex);
   if (glyph)
      FREE(glyph);

   return (glyph != NULL);
}

static void add_glyph(struct vg_font *font,
                      VGuint glyphIndex,
                      struct vg_object *obj,
                      VGboolean isHinted,
                      const VGfloat glyphOrigin[2],
                      const VGfloat escapement[2])
{
   struct vg_glyph *glyph;

   /* remove the existing one */
   del_glyph(font, glyphIndex);

   glyph = CALLOC_STRUCT(vg_glyph);
   glyph->object = obj;
   glyph->is_hinted = isHinted;
   memcpy(glyph->glyph_origin, glyphOrigin, sizeof(glyphOrigin));
   memcpy(glyph->escapement, escapement, sizeof(escapement));

   cso_hash_insert(font->glyphs, (unsigned) glyphIndex, glyph);
}

static struct vg_glyph *get_glyph(struct vg_font *font,
                                  VGuint glyphIndex)
{
   struct cso_hash_iter iter;

   iter = cso_hash_find(font->glyphs, (unsigned) glyphIndex);
   return (struct vg_glyph *) cso_hash_iter_data(iter);
}

static void vg_render_glyph(struct vg_context *ctx,
                            struct vg_glyph *glyph,
                            VGbitfield paintModes,
                            VGboolean allowAutoHinting)
{
   if (glyph->object && paintModes) {
      struct vg_state *state = &ctx->state.vg;
      struct matrix m;

      m = state->glyph_user_to_surface_matrix;
      matrix_translate(&m,
            state->glyph_origin[0].f - glyph->glyph_origin[0],
            state->glyph_origin[1].f - glyph->glyph_origin[1]);

      if (glyph->object->type == VG_OBJECT_PATH) {
         path_render((struct path *) glyph->object, paintModes, &m);
      }
      else {
         assert(glyph->object->type == VG_OBJECT_IMAGE);
         image_draw((struct vg_image *) glyph->object, &m);
      }
   }
}

static void vg_advance_glyph(struct vg_context *ctx,
                             struct vg_glyph *glyph,
                             VGfloat adjustment_x,
                             VGfloat adjustment_y,
                             VGboolean last)
{
   struct vg_value *glyph_origin = ctx->state.vg.glyph_origin;

   glyph_origin[0].f += glyph->escapement[0] + adjustment_x;
   glyph_origin[1].f += glyph->escapement[1] + adjustment_y;

   if (last) {
      glyph_origin[0].i = float_to_int_floor(glyph_origin[0].f);
      glyph_origin[1].i = float_to_int_floor(glyph_origin[1].f);
   }
}

struct vg_font *font_create(VGint glyphCapacityHint)
{
   struct vg_context *ctx = vg_current_context();
   struct vg_font *font;

   font = CALLOC_STRUCT(vg_font);
   vg_init_object(&font->base, ctx, VG_OBJECT_FONT);
   font->glyphs = cso_hash_create();

   vg_context_add_object(ctx, VG_OBJECT_FONT, font);

   return font;
}

void font_destroy(struct vg_font *font)
{
   struct vg_context *ctx = vg_current_context();
   struct cso_hash_iter iter;

   vg_context_remove_object(ctx, VG_OBJECT_FONT, font);

   iter = cso_hash_first_node(font->glyphs);
   while (!cso_hash_iter_is_null(iter)) {
      struct vg_glyph *glyph = (struct vg_glyph *) cso_hash_iter_data(iter);
      FREE(glyph);
      iter = cso_hash_iter_next(iter);
   }
   cso_hash_delete(font->glyphs);

   FREE(font);
}

void font_set_glyph_to_path(struct vg_font *font,
                            VGuint glyphIndex,
                            struct path *path,
                            VGboolean isHinted,
                            const VGfloat glyphOrigin[2],
                            const VGfloat escapement[2])
{
   add_glyph(font, glyphIndex, (struct vg_object *) path,
         isHinted, glyphOrigin, escapement);
}

void font_set_glyph_to_image(struct vg_font *font,
                             VGuint glyphIndex,
                             struct vg_image *image,
                             const VGfloat glyphOrigin[2],
                             const VGfloat escapement[2])
{
   add_glyph(font, glyphIndex, (struct vg_object *) image,
         VG_TRUE, glyphOrigin, escapement);
}

void font_clear_glyph(struct vg_font *font,
                      VGuint glyphIndex)
{
   if (!del_glyph(font, glyphIndex)) {
      struct vg_context *ctx = vg_current_context();
      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
   }
}

void font_draw_glyph(struct vg_font *font,
                     VGuint glyphIndex,
                     VGbitfield paintModes,
                     VGboolean allowAutoHinting)
{
   struct vg_context *ctx = vg_current_context();
   struct vg_glyph *glyph;

   glyph = get_glyph(font, glyphIndex);
   if (!glyph) {
      vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
      return;
   }

   vg_render_glyph(ctx, glyph, paintModes, allowAutoHinting);
   vg_advance_glyph(ctx, glyph, 0.0f, 0.0f, VG_TRUE);
}

void font_draw_glyphs(struct vg_font *font,
                      VGint glyphCount,
                      const VGuint *glyphIndices,
                      const VGfloat *adjustments_x,
                      const VGfloat *adjustments_y,
                      VGbitfield paintModes,
                      VGboolean allowAutoHinting)
{
   struct vg_context *ctx = vg_current_context();
   VGint i;

   for (i = 0; i < glyphCount; ++i) {
      if (!get_glyph(font, glyphIndices[i])) {
         vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
         return;
      }
   }

   for (i = 0; i < glyphCount; ++i) {
      struct vg_glyph *glyph;
      VGfloat adj_x, adj_y;

      glyph = get_glyph(font, glyphIndices[i]);

      vg_render_glyph(ctx, glyph, paintModes, allowAutoHinting);

      adj_x = (adjustments_x) ? adjustments_x[i] : 0.0f;
      adj_y = (adjustments_y) ? adjustments_y[i] : 0.0f;
      vg_advance_glyph(ctx, glyph, adj_x, adj_y, (i == glyphCount - 1));
   }
}

VGint font_num_glyphs(struct vg_font *font)
{
   return cso_hash_size(font->glyphs);
}

#endif /* OPENVG_VERSION_1_1 */
