/** @internal
 *  @memberof oyObject_s
 *  @brief   custom object memory managers
 *
 *  @param[in]    object         the object to modify
 *  @param[in]    allocateFunc   zero for default or user memory allocator
 *  @param[in]    deallocateFunc zero for default or user memory deallocator
 *
 *  @since Oyranos: version 0.1.8
 *  @date  november 2007 (API 0.1.8)
 */
oyObject_s   oyObject_SetAllocators_  ( oyObject_s        object,
                                        oyAlloc_f         allocateFunc,
                                        oyDeAlloc_f       deallocateFunc )
{
  if(!object) return 0;

  /* we require a allocation function to be present */
  if(allocateFunc)
    object->allocateFunc_ = allocateFunc;
  else
    object->allocateFunc_ = oyAllocateFunc_;

  object->deallocateFunc_ = deallocateFunc;

  return object;
}

/**
 *  @internal
 *  Function oyObject_Ref
 *  @memberof oyObject_s
 *  @brief   increase the ref counter and return the above zero ref value
 *
 *  @version Oyranos: 0.1.8
 *  @date    2011/02/02
 *  @since   2008/02/07 (Oyranos: 0.1.8)
 */
int          oyObject_Ref            ( oyObject_s          obj )
{
  oyObject_s s = obj;
  int error = !s;

  if( s->type_ != oyOBJECT_OBJECT_S)
  {
    WARNc2_S("Attempt to manipulate a non oyObject_s object; type: %d ID: %d",
             s->type_, s->id_)
    return 1;
  }

  if(error <= 0)
    oyObject_Lock( s, __FILE__, __LINE__ );

  if(error <= 0)
  {
    ++s->ref_;

    if(oy_debug)
      /* track object */
      oyObject_GetId( obj );
  }
#   if DEBUG_OBJECT
    WARNc3_S("%s   ID: %d refs: %d",
             oyStructTypeToText( s->parent_types_[s->parent_types_[0]] ), s->id_, s->ref_)
#   endif

  if(obj->parent_types_[obj->parent_types_[0]] == oyOBJECT_NAMED_COLORS_S)
  {
    int e_a = error;
    error = pow(e_a,2.1);
    error = e_a;
  }

  if(error <= 0)
    oyObject_UnLock( s, __FILE__, __LINE__ );

  return s->ref_;
}

/** @internal
 *  Function oyObject_Hashed_
 *  @memberof oyObject_s
 *  @brief   check if a object has a hash sum computed
 *
 *  @version Oyranos: 0.3.0
 *  @date    2011/04/09
 *  @since   2008/11/02 (Oyranos: 0.1.8)
 */
int32_t      oyObject_Hashed_        ( oyObject_s          s )
{
  int32_t hashed = 0;
  if(s && s->type_ == oyOBJECT_OBJECT_S && s->hash_ptr_)
      if(*((uint32_t*)(&s->hash_ptr_[0])) ||
         *((uint32_t*)(&s->hash_ptr_[4])) ||
         *((uint32_t*)(&s->hash_ptr_[8])) ||
         *((uint32_t*)(&s->hash_ptr_[12])) )
        hashed = 1;
  return hashed;
}

/** @internal
 *  Function oyObject_HashSet
 *  @memberof oyObject_s
 *  @brief   check if a object has a hash allocated and set it
 *
 *  @param[in,out] s                   the object
 *  @param[in]     hash                the hash of max size 2 * OY_HASH_SIZE
 *  @return                            0 - fine, otherwise error
 *
 *  @version Oyranos: 0.1.10
 *  @date    2009/04/16
 *  @since   2009/04/16 (Oyranos: 0.1.10)
 */
int          oyObject_HashSet          ( oyObject_s        s,
                                         const unsigned char * hash )
{
  int error = 0;

  if(s && s->type_ == oyOBJECT_OBJECT_S && !s->hash_ptr_)
  {
    s->hash_ptr_ = s->allocateFunc_(OY_HASH_SIZE*2);
    error = !s->hash_ptr_;
  }

  if(!error)
  {
    if(hash)
      memcpy( s->hash_ptr_, hash, OY_HASH_SIZE*2 );
    else
      memset( s->hash_ptr_, 0, OY_HASH_SIZE*2 );
  }

  return error;
}

/** @internal
 *  Function oyObject_HashEqual
 *  @memberof oyObject_s
 *  @brief   check if two objects hash is equal
 *
 *  @version Oyranos: 0.1.10
 *  @since   2009/04/16 (Oyranos: 0.1.10)
 *  @date    2009/05/26
 */
int          oyObject_HashEqual        ( oyObject_s        s1,
                                         oyObject_s        s2 )
{
  int equal = 0;

  if(s1->hash_ptr_ &&
     s2->hash_ptr_)
  {
    if(memcmp(s1->hash_ptr_, s2->hash_ptr_, OY_HASH_SIZE*2) == 0)
      return 1;
  }

  return equal;
}

static oyPointer oy_object_id_mutex_ = NULL;

/** @internal
 *  @memberof oyObject_s
 *  @brief    get a object identification number
 *
 *  @version  Oyranos: 0.9.5
 *  @date     2014/02/04
 *  @since    2014/02/04 (Oyranos: 0.9.5)
 */
int oyGetNewObjectID()
{
  static int oy_object_id_ = 0;
  int val = -1;
  if(!oy_object_id_mutex_)
    oy_object_id_mutex_ = oyStruct_LockCreateFunc_(NULL);

  oyLockFunc_(oy_object_id_mutex_,__FILE__,__LINE__);
  val = oy_object_id_++;
  oyUnLockFunc_(oy_object_id_mutex_,__FILE__,__LINE__);
  return val;
}

#define MAX_OBJECTS_TRACKED 1000000
/* private tracking API's start */
static oyObject_s * oy_obj_track_list = 0;
void               oyObject_Track    ( oyObject_s          obj )
{
  if(!oy_obj_track_list)
  {
    oy_obj_track_list = oyAllocateFunc_( sizeof(oyObject_s) * (MAX_OBJECTS_TRACKED + 1) );
    if(oy_obj_track_list)
      memset( oy_obj_track_list, 0, sizeof(oyObject_s) * (MAX_OBJECTS_TRACKED + 1) );
  }
  if(oy_obj_track_list && obj->id_ < MAX_OBJECTS_TRACKED)
    oy_obj_track_list[obj->id_] = obj;
}
void               oyObject_UnTrack    ( oyObject_s          obj )
{
  if(oy_obj_track_list && obj->id_ < MAX_OBJECTS_TRACKED)
    oy_obj_track_list[obj->id_] = NULL;
}
/* private tracking API's end */

int *              oyObjectGetCurrentObjectIdList( void )
{
  int * id_list = oyAllocateFunc_( sizeof(int) * MAX_OBJECTS_TRACKED );
  int i;

  if(id_list)
    for(i = 0; i < MAX_OBJECTS_TRACKED; ++i)
    {
      if(oy_obj_track_list && oy_obj_track_list[i] && oy_obj_track_list[i]->parent_)
        id_list[i] = oy_obj_track_list[i]->parent_->type_;
      else
        id_list[i] = -1;
    }
  return id_list;
}

const oyObject_s * oyObjectGetList   ( int               * max_count )
{ if(max_count) *max_count = MAX_OBJECTS_TRACKED; return oy_obj_track_list; }

int *              oyObjectFindNewIds( int               * old,
                                       int               * new )
{
  int * id_list = oyAllocateFunc_( sizeof(int) * MAX_OBJECTS_TRACKED );
  int i;

  if(id_list)
    for(i = 0; i < MAX_OBJECTS_TRACKED; ++i)
    {
      if(old[i] == -1 && new[i] != -1)
        id_list[i] = new[i];
      else
        id_list[i] = -1;
    }
  return id_list;
}
void               oyObjectReleaseCurrentObjectIdList(
                                       int              ** id_list )
{ oyDeAllocateFunc_(*id_list); *id_list = NULL; }
int                oyObjectIdListShowDiffAndRelease (
                                       int              ** ids_old,
                                       const char        * location )
{
  int * ids_new = oyObjectGetCurrentObjectIdList(),
      * ids_remaining_new = oyObjectFindNewIds( *ids_old, ids_new ),
      max_count,i, count = 0;
  const oyObject_s * obs = oyObjectGetList( &max_count );

  for(i = 0; i < max_count; ++i)
    if(ids_remaining_new[i] != -1)
      ++count;
  if(count)
  {
    fprintf( stderr, "new allocated objects inside %s: %d\n", location, count );
    for(i = 0; i < max_count; ++i)
      if(ids_remaining_new[i] != -1)
        fputs( oyObject_Show( obs[i] ), stderr );
    fprintf( stderr, "... end new allocated objects inside %s\n", location );
    fflush( stderr );
  }

  oyObjectReleaseCurrentObjectIdList( ids_old );
  oyObjectReleaseCurrentObjectIdList( &ids_new );
  oyObjectReleaseCurrentObjectIdList( &ids_remaining_new );

  return count;
}

