/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%           RRRR    EEEEE    GGG   IIIII  SSSSS  TTTTT  RRRR   Y   Y          %
%           R   R   E       G        I    SS       T    R   R   Y Y           %
%           RRRR    EEE     G GGG    I     SSS     T    RRRR     Y            %
%           R R     E       G   G    I       SS    T    R R      Y            %
%           R  R    EEEEE    GGG   IIIII  SSSSS    T    R  R     Y            %
%                                                                             %
%                                                                             %
%                       MagickCore Registry Methods                           %
%                                                                             %
%                              Software Design                                %
%                                   Cristy                                    %
%                                 March 2000                                  %
%                                                                             %
%                                                                             %
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
%  dedicated to making software imaging solutions freely available.           %
%                                                                             %
%  You may not use this file except in compliance with the License.  You may  %
%  obtain a copy of the License at                                            %
%                                                                             %
%    https://imagemagick.org/script/license.php                               %
%                                                                             %
%  Unless required by applicable law or agreed to in writing, software        %
%  distributed under the License is distributed on an "AS IS" BASIS,          %
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
%  See the License for the specific language governing permissions and        %
%  limitations under the License.                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
%
*/

/*
  Include declarations.
*/
#include "MagickCore/studio.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/image.h"
#include "MagickCore/list.h"
#include "MagickCore/memory_.h"
#include "MagickCore/memory-private.h"
#include "MagickCore/registry.h"
#include "MagickCore/registry-private.h"
#include "MagickCore/splay-tree.h"
#include "MagickCore/string_.h"
#include "MagickCore/string-private.h"
#include "MagickCore/timer-private.h"
#include "MagickCore/utility.h"

/*
  Typedef declarations.
*/
typedef struct _RegistryInfo
{
  RegistryType
    type;

  void
    *value;

  size_t
    signature;
} RegistryInfo;

/*
  Static declarations.
*/
static SplayTreeInfo
  *registry = (SplayTreeInfo *) NULL;

static SemaphoreInfo
  *registry_semaphore = (SemaphoreInfo *) NULL;

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   D e f i n e I m a g e R e g i s t r y                                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  DefineImageRegistry() associates a key/value pair with the image registry.
%
%  The format of the DefineImageRegistry method is:
%
%      MagickBooleanType DefineImageRegistry(const RegistryType type,
%        const char *option,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o type: the type.
%
%    o option: the option.
%
%    o exception: the exception.
%
*/
MagickExport MagickBooleanType DefineImageRegistry(const RegistryType type,
  const char *option,ExceptionInfo *exception)
{
  char
    key[MagickPathExtent],
    value[MagickPathExtent];

  char
    *p;

  assert(option != (const char *) NULL);
  (void) CopyMagickString(key,option,MagickPathExtent);
  for (p=key; *p != '\0'; p++)
    if (*p == '=')
      break;
  *value='\0';
  if (*p == '=')
    (void) CopyMagickString(value,p+1,MagickPathExtent);
  *p='\0';
  return(SetImageRegistry(type,key,value,exception));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   D e l e t e I m a g e R e g i s t r y                                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  DeleteImageRegistry() deletes a key from the image registry.
%
%  The format of the DeleteImageRegistry method is:
%
%      MagickBooleanType DeleteImageRegistry(const char *key)
%
%  A description of each parameter follows:
%
%    o key: the registry.
%
*/
MagickExport MagickBooleanType DeleteImageRegistry(const char *key)
{
  assert(key != (const char *) NULL);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
  if (registry == (void *) NULL)
    return(MagickFalse);
  return(DeleteNodeFromSplayTree(registry,key));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e t I m a g e R e g i s t r y                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  GetImageRegistry() returns a value associated with an image registry key.
%
%  The format of the GetImageRegistry method is:
%
%      void *GetImageRegistry(const RegistryType type,const char *key,
%        ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o type: the type.
%
%    o key: the key.
%
%    o exception: the exception.
%
*/
MagickExport void *GetImageRegistry(const RegistryType type,const char *key,
  ExceptionInfo *exception)
{
  void
    *value;

  RegistryInfo
    *registry_info;

  assert(key != (const char *) NULL);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
  if (registry == (void *) NULL)
    return((void *) NULL);
  registry_info=(RegistryInfo *) GetValueFromSplayTree(registry,key);
  if (registry_info == (void *) NULL)
    return((void *) NULL);
  value=(void *) NULL;
  switch (type)
  {
    case ImageRegistryType:
    {
      if (type == registry_info->type)
        value=(void *) CloneImageList((Image *) registry_info->value,exception);
      break;
    }
    case ImageInfoRegistryType:
    {
      if (type == registry_info->type)
        value=(void *) CloneImageInfo((ImageInfo *) registry_info->value);
      break;
    }
    case StringRegistryType:
    {
      switch (registry_info->type)
      {
        case ImageRegistryType:
        {
          value=(Image *) ConstantString(((Image *)
            registry_info->value)->filename);
          break;
        }
        case ImageInfoRegistryType:
        {
          value=(Image *) ConstantString(((ImageInfo *)
            registry_info->value)->filename);
          break;
        }
        case StringRegistryType:
        {
          value=(void *) ConstantString((char *) registry_info->value);
          break;
        }
        default:
          break;
      }
      break;
    }
    default:
      break;
  }
  return(value);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e t N e x t I m a g e R e g i s t r y                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  GetNextImageRegistry() gets the next image registry value.
%
%  The format of the GetNextImageRegistry method is:
%
%      char *GetNextImageRegistry(void)
%
*/
MagickExport char *GetNextImageRegistry(void)
{
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  if (registry == (void *) NULL)
    return((char *) NULL);
  return((char *) GetNextKeyInSplayTree(registry));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   R e g i s t r y C o m p o n e n t G e n e s i s                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  RegistryComponentGenesis() instantiates the registry component.
%
%  The format of the RegistryComponentGenesis method is:
%
%      MagickBooleanType RegistryComponentGenesis(void)
%
*/
MagickPrivate MagickBooleanType RegistryComponentGenesis(void)
{
  if (registry_semaphore == (SemaphoreInfo *) NULL)
    registry_semaphore=AcquireSemaphoreInfo();
  return(MagickTrue);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e g i s t r y C o m p o n e n t T e r m i n u s                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  RegistryComponentTerminus() destroys the registry component.
%
%  The format of the DestroyDefines method is:
%
%      void RegistryComponentTerminus(void)
%
*/
MagickPrivate void RegistryComponentTerminus(void)
{
  if (registry_semaphore == (SemaphoreInfo *) NULL)
    ActivateSemaphoreInfo(&registry_semaphore);
  LockSemaphoreInfo(registry_semaphore);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  if (registry != (void *) NULL)
    registry=DestroySplayTree(registry);
  UnlockSemaphoreInfo(registry_semaphore);
  RelinquishSemaphoreInfo(&registry_semaphore);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e m o v e I m a g e R e g i s t r y                                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  RemoveImageRegistry() removes a key from the image registry and returns its
%  value.
%
%  The format of the RemoveImageRegistry method is:
%
%      void *RemoveImageRegistry(const char *key)
%
%  A description of each parameter follows:
%
%    o key: the registry.
%
*/
MagickExport void *RemoveImageRegistry(const char *key)
{
  assert(key != (const char *) NULL);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
  if (registry == (void *) NULL)
    return((void *) NULL);
  return(RemoveNodeFromSplayTree(registry,key));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e s e t I m a g e R e g i s t r y I t e r a t o r                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  ResetImageRegistryIterator() resets the registry iterator.  Use it in
%  conjunction with GetNextImageRegistry() to iterate over all the values
%  in the image registry.
%
%  The format of the ResetImageRegistryIterator method is:
%
%      ResetImageRegistryIterator(void)
%
*/
MagickExport void ResetImageRegistryIterator(void)
{
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
  if (registry == (void *) NULL)
    return;
  ResetSplayTreeIterator(registry);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   S e t I m a g e R e g i s t r y                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  SetImageRegistry() associates a value with an image registry key.
%
%  The format of the SetImageRegistry method is:
%
%      MagickBooleanType SetImageRegistry(const RegistryType type,
%        const char *key,const void *value,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o type: the type.
%
%    o key: the key.
%
%    o value: the value.
%
%    o exception: the exception.
%
*/

static void *DestroyRegistryNode(void *registry_info)
{
  RegistryInfo
    *p;

  p=(RegistryInfo *) registry_info;
  switch (p->type)
  {
    case StringRegistryType:
    default:
    {
      p->value=RelinquishMagickMemory(p->value);
      break;
    }
    case ImageRegistryType:
    {
      p->value=(void *) DestroyImageList((Image *) p->value);
      break;
    }
    case ImageInfoRegistryType:
    {
      p->value=(void *) DestroyImageInfo((ImageInfo *) p->value);
      break;
    }
  }
  return(RelinquishMagickMemory(p));
}

MagickExport MagickBooleanType SetImageRegistry(const RegistryType type,
  const char *key,const void *value,ExceptionInfo *exception)
{
  RegistryInfo
    *registry_info;

  void
    *clone_value;

  assert(key != (const char *) NULL);
  if (IsEventLogging() != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
  if (value == (const void *) NULL)
    return(MagickFalse);
  clone_value=(void *) NULL;
  switch (type)
  {
    case StringRegistryType:
    default:
    {
      const char
        *string;

      string=(const char *) value;
      if (LocaleCompare(key,"date:precision") == 0)
        SetMagickDatePrecision((size_t) StringToInteger(string));
      clone_value=(void *) ConstantString(string);
      break;
    }
    case ImageRegistryType:
    {
      const Image
        *image;

      image=(const Image *) value;
      if ((image == (const Image *) NULL) ||
          (image->signature != MagickCoreSignature))
        {
          (void) ThrowMagickException(exception,GetMagickModule(),RegistryError,
            "UnableToSetRegistry","%s",key);
          return(MagickFalse);
        }
      clone_value=(void *) CloneImageList(image,exception);
      break;
    }
    case ImageInfoRegistryType:
    {
      const ImageInfo
        *image_info;

      image_info=(const ImageInfo *) value;
      if ((image_info == (const ImageInfo *) NULL) ||
          (image_info->signature != MagickCoreSignature))
        {
          (void) ThrowMagickException(exception,GetMagickModule(),RegistryError,
            "UnableToSetRegistry","%s",key);
          return(MagickFalse);
        }
      clone_value=(void *) CloneImageInfo(image_info);
      break;
    }
  }
  if (clone_value == (void *) NULL)
    return(MagickFalse);
  registry_info=(RegistryInfo *) AcquireCriticalMemory(sizeof(*registry_info));
  (void) memset(registry_info,0,sizeof(*registry_info));
  registry_info->type=type;
  registry_info->value=clone_value;
  registry_info->signature=MagickCoreSignature;
  if (registry == (SplayTreeInfo *) NULL)
    {
      if (registry_semaphore == (SemaphoreInfo *) NULL)
        ActivateSemaphoreInfo(&registry_semaphore);
      LockSemaphoreInfo(registry_semaphore);
      if (registry == (SplayTreeInfo *) NULL)
        registry=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
          DestroyRegistryNode);
      UnlockSemaphoreInfo(registry_semaphore);
    }
  return(AddValueToSplayTree(registry,ConstantString(key),registry_info));
}
