/*******************************************************************
 *
 *  ttextend.c                                                   2.0
 *
 *    Extensions Interface
 *
 *  Copyright 1996-2000 by
 *  David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 *  This file is part of the FreeType project, and may only be used
 *  modified and distributed under the terms of the FreeType project
 *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
 *  this file you indicate that you have read the license and
 *  understand and accept it fully.
 *
 *  This is an updated version of the extension component, now
 *  located in the main library's source directory.  It allows
 *  the dynamic registration/use of various face object extensions
 *  through a simple API.
 *
 ******************************************************************/

#include "ttextend.h"
#include "ttengine.h"
#include "ttmemory.h"

/* required by the tracing mode */
#undef  TT_COMPONENT
#define TT_COMPONENT      trace_extend


  struct TExtension_Registry_
  {
    Int               num_extensions;
    Long              cur_offset;
    TExtension_Class  classes[TT_MAX_EXTENSIONS];
  };

  typedef struct TExtension_Registry_  TExtension_Registry;
  typedef TExtension_Registry*         PExtension_Registry;



  /* Initialize the extension component */

  FT_INTERNAL_FUNC( TT_Error )
  TTExtend_Init( PEngine_Instance  engine )
  {
    TT_Error             error;
    PExtension_Registry  exts;


    if ( ALLOC( exts, sizeof ( TExtension_Registry ) ) )
      return error;

    exts->num_extensions        = 0;
    exts->cur_offset            = 0;
    engine->extension_component = (void*)exts;

    return TT_Err_Ok;
  }


  /* Finalize the extension component */

  FT_INTERNAL_FUNC( TT_Error )
  TTExtend_Done( PEngine_Instance  engine )
  {
    FREE( engine->extension_component );
    return TT_Err_Ok;
  }


  /* Register a new extension */

  FT_EXPORT_FUNC( TT_Error )
  TT_Register_Extension( PEngine_Instance  engine,
                         Long              id,
                         Long              size,
                         PExt_Constructor  create,
                         PExt_Destructor   destroy )
  {
    PExtension_Registry  exts;
    PExtension_Class     clazz;
    Int                  p;


    exts = (PExtension_Registry)engine->extension_component;
    if ( !exts )
      return TT_Err_Ok;

    p = exts->num_extensions;

    if ( p >= TT_MAX_EXTENSIONS )
      return TT_Err_Too_Many_Extensions;

    clazz          = exts->classes + p;
    clazz->id      = id;
    clazz->size    = size;
    clazz->build   = create;
    clazz->destroy = destroy;

    clazz->offset  = exts->cur_offset;

    exts->num_extensions++;
    exts->cur_offset += ( size + ALIGNMENT-1 ) & -ALIGNMENT;

    return TT_Err_Ok;
  }


  /* Query an extension block by extension_ID */

  FT_EXPORT_FUNC( TT_Error )
  TT_Extension_Get( PFace   face,
                    Long    extension_id,
                    void**  extension_block )
  {
    PExtension_Registry  registry;
    PExtension_Class     clazz;
    Int                  n;


    if ( !face->extension )
      return TT_Err_Extensions_Unsupported;

    registry = (PExtension_Registry)face->engine->extension_component;

    for ( n = 0; n < face->n_extensions; n++ )
    {
      clazz = registry->classes + n;
      if ( clazz->id == extension_id )
      {
        *extension_block = (PByte)face->extension + clazz->offset;
        return TT_Err_Ok;
      }
    }

    return TT_Err_Invalid_Extension_Id;
  }


  /* Destroy all extensions within a face object.  Called by the */
  /* face object destructor.                                     */

  FT_INTERNAL_FUNC( TT_Error )
  Extension_Destroy( PFace  face )
  {
    PEngine_Instance     engine = face->engine;
    PExtension_Registry  registry;
    PExtension_Class     clazz;
    Int                  n;
    PByte                ext;


    registry = (PExtension_Registry)engine->extension_component;

    for ( n = 0; n < face->n_extensions; n++ )
    {
      clazz = registry->classes + n;
      ext   = (PByte)face->extension + clazz->offset;

      /* the destructor is optional */
      if ( clazz->destroy )
        clazz->destroy( (void*)ext, face );
    }

    /* destroy the face's extension block too */
    FREE( face->extension );
    face->n_extensions = 0;

    return TT_Err_Ok;
  }


  /* Create an extension within a face object.  Called by the */
  /* face object constructor.                                 */

  FT_INTERNAL_FUNC( TT_Error )
  Extension_Create( PFace  face )
  {
    PEngine_Instance     engine = face->engine;
    PExtension_Registry  registry;
    PExtension_Class     clazz;
    TT_Error             error;
    Int                  n;
    PByte                ext;


    registry = (PExtension_Registry)engine->extension_component;

    face->n_extensions = registry->num_extensions;
    if ( ALLOC( face->extension, registry->cur_offset ) )
      return error;

    for ( n = 0; n < face->n_extensions; n++ )
    {
      clazz = registry->classes + n;
      ext   = (PByte)face->extension + clazz->offset;
      error = clazz->build( (void*)ext, face );
      if ( error )
        goto Fail;
    }
    return TT_Err_Ok;

  Fail:
    Extension_Destroy( face );
    return error;
  }


/* END */
