/*
 * Copyright (c) 1988-1997 Sam Leffler
 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

/*
 * TIFF Library
 *
 * Compression Scheme Configuration Support.
 */
#include "tiffiop.h"

static int TIFFNoEncode(TIFF *tif, const char *method)
{
    const TIFFCodec *c = TIFFFindCODEC(tif->tif_dir.td_compression);

    if (c)
    {
        TIFFErrorExtR(tif, tif->tif_name, "%s %s encoding is not implemented",
                      c->name, method);
    }
    else
    {
        TIFFErrorExtR(tif, tif->tif_name,
                      "Compression scheme %" PRIu16
                      " %s encoding is not implemented",
                      tif->tif_dir.td_compression, method);
    }
    return (-1);
}

int _TIFFNoRowEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
{
    (void)pp;
    (void)cc;
    (void)s;
    return (TIFFNoEncode(tif, "scanline"));
}

int _TIFFNoStripEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
{
    (void)pp;
    (void)cc;
    (void)s;
    return (TIFFNoEncode(tif, "strip"));
}

int _TIFFNoTileEncode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
{
    (void)pp;
    (void)cc;
    (void)s;
    return (TIFFNoEncode(tif, "tile"));
}

static int TIFFNoDecode(TIFF *tif, const char *method)
{
    const TIFFCodec *c = TIFFFindCODEC(tif->tif_dir.td_compression);

    if (c)
        TIFFErrorExtR(tif, tif->tif_name, "%s %s decoding is not implemented",
                      c->name, method);
    else
        TIFFErrorExtR(tif, tif->tif_name,
                      "Compression scheme %" PRIu16
                      " %s decoding is not implemented",
                      tif->tif_dir.td_compression, method);
    return (0);
}

static int _TIFFNoFixupTags(TIFF *tif)
{
    (void)tif;
    return (1);
}

int _TIFFNoRowDecode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
{
    (void)pp;
    (void)cc;
    (void)s;
    return (TIFFNoDecode(tif, "scanline"));
}

int _TIFFNoStripDecode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
{
    (void)pp;
    (void)cc;
    (void)s;
    return (TIFFNoDecode(tif, "strip"));
}

int _TIFFNoTileDecode(TIFF *tif, uint8_t *pp, tmsize_t cc, uint16_t s)
{
    (void)pp;
    (void)cc;
    (void)s;
    return (TIFFNoDecode(tif, "tile"));
}

int _TIFFNoSeek(TIFF *tif, uint32_t off)
{
    (void)off;
    TIFFErrorExtR(tif, tif->tif_name,
                  "Compression algorithm does not support random access");
    return (0);
}

int _TIFFNoPreCode(TIFF *tif, uint16_t s)
{
    (void)tif;
    (void)s;
    return (1);
}

static int _TIFFtrue(TIFF *tif)
{
    (void)tif;
    return (1);
}
static void _TIFFvoid(TIFF *tif) { (void)tif; }

static uint64_t _TIFFDefaultGetMaxCompressionRatio(TIFF *tif)
{
    (void)tif;
    return 0; /* unknown */
}

static uint64_t _TIFFGetMaxCompressionRatioOne(TIFF *tif)
{
    (void)tif;
    return 1; /* no compression */
}

void _TIFFSetDefaultCompressionState(TIFF *tif)
{
    tif->tif_fixuptags = _TIFFNoFixupTags;
    tif->tif_decodestatus = TRUE;
    tif->tif_setupdecode = _TIFFtrue;
    tif->tif_predecode = _TIFFNoPreCode;
    tif->tif_decoderow = _TIFFNoRowDecode;
    tif->tif_decodestrip = _TIFFNoStripDecode;
    tif->tif_decodetile = _TIFFNoTileDecode;
    tif->tif_encodestatus = TRUE;
    tif->tif_setupencode = _TIFFtrue;
    tif->tif_preencode = _TIFFNoPreCode;
    tif->tif_postencode = _TIFFtrue;
    tif->tif_encoderow = _TIFFNoRowEncode;
    tif->tif_encodestrip = _TIFFNoStripEncode;
    tif->tif_encodetile = _TIFFNoTileEncode;
    tif->tif_close = _TIFFvoid;
    tif->tif_seek = _TIFFNoSeek;
    tif->tif_cleanup = _TIFFvoid;
    tif->tif_defstripsize = _TIFFDefaultStripSize;
    tif->tif_deftilesize = _TIFFDefaultTileSize;
    tif->tif_getmaxcompressionratio = _TIFFDefaultGetMaxCompressionRatio;
    tif->tif_flags &= ~(TIFF_NOBITREV | TIFF_NOREADRAW);
}

int TIFFSetCompressionScheme(TIFF *tif, int scheme)
{
    const TIFFCodec *c = TIFFFindCODEC((uint16_t)scheme);

    _TIFFSetDefaultCompressionState(tif);
    if (scheme == COMPRESSION_NONE)
        tif->tif_getmaxcompressionratio = _TIFFGetMaxCompressionRatioOne;
    /*
     * Don't treat an unknown compression scheme as an error.
     * This permits applications to open files with data that
     * the library does not have builtin support for, but which
     * may still be meaningful.
     */
    return (c ? (*c->init)(tif, scheme) : 1);
}

uint64_t TIFFGetMaxCompressionRatio(TIFF *tif)
{
    if (tif->tif_getmaxcompressionratio)
        return tif->tif_getmaxcompressionratio(tif);
    return 0;
}

/*
 * Other compression schemes may be registered.  Registered
 * schemes can also override the builtin versions provided
 * by this library.
 */
typedef struct _codec
{
    struct _codec *next;
    TIFFCodec *info;
} codec_t;
static codec_t *registeredCODECS = NULL;

const TIFFCodec *TIFFFindCODEC(uint16_t scheme)
{
    const TIFFCodec *c;
    codec_t *cd;

    for (cd = registeredCODECS; cd; cd = cd->next)
        if (cd->info->scheme == scheme)
            return ((const TIFFCodec *)cd->info);
    for (c = _TIFFBuiltinCODECS; c->name; c++)
        if (c->scheme == scheme)
            return (c);
    return NULL;
}

TIFFCodec *TIFFRegisterCODEC(uint16_t scheme, const char *name,
                             TIFFInitMethod init)
{
    codec_t *cd = (codec_t *)_TIFFmallocExt(
        NULL,
        (tmsize_t)(sizeof(codec_t) + sizeof(TIFFCodec) + strlen(name) + 1));

    if (cd != NULL)
    {
        char *codec_name;
        cd->info = (TIFFCodec *)((uint8_t *)cd + sizeof(codec_t));
        codec_name = (char *)((uint8_t *)cd->info + sizeof(TIFFCodec));
        strcpy(codec_name, name);
        cd->info->name = codec_name;
        cd->info->scheme = scheme;
        cd->info->init = init;
        cd->next = registeredCODECS;
        registeredCODECS = cd;
    }
    else
    {
        TIFFErrorExt(0, "TIFFRegisterCODEC",
                     "No space to register compression scheme %s", name);
        return NULL;
    }
    return (cd->info);
}

void TIFFUnRegisterCODEC(TIFFCodec *c)
{
    codec_t *cd;
    codec_t **pcd;

    for (pcd = &registeredCODECS; (cd = *pcd) != NULL; pcd = &cd->next)
        if (cd->info == c)
        {
            *pcd = cd->next;
            _TIFFfreeExt(NULL, cd);
            return;
        }
    TIFFErrorExt(0, "TIFFUnRegisterCODEC",
                 "Cannot remove compression scheme %s; not registered",
                 c->name);
}

/************************************************************************/
/*                      TIFFGetConfiguredCODECs()                       */
/************************************************************************/

/**
 * Get list of configured codecs, both built-in and registered by user.
 * Caller is responsible to free this structure.
 *
 * @return returns array of TIFFCodec records (the last record should be NULL)
 * or NULL if function failed.
 */

TIFFCodec *TIFFGetConfiguredCODECs(void)
{
    int i = 1;
    codec_t *cd;
    const TIFFCodec *c;
    TIFFCodec *codecs = NULL;
    TIFFCodec *new_codecs;

    for (cd = registeredCODECS; cd; cd = cd->next)
    {
        new_codecs = (TIFFCodec *)_TIFFreallocExt(
            NULL, codecs, (tmsize_t)((size_t)i * sizeof(TIFFCodec)));
        if (!new_codecs)
        {
            _TIFFfreeExt(NULL, codecs);
            return NULL;
        }
        codecs = new_codecs;
        _TIFFmemcpy(codecs + i - 1, cd->info, sizeof(TIFFCodec));
        i++;
    }
    for (c = _TIFFBuiltinCODECS; c->name; c++)
    {
        if (TIFFIsCODECConfigured(c->scheme))
        {
            new_codecs = (TIFFCodec *)_TIFFreallocExt(
                NULL, codecs, (tmsize_t)((size_t)i * sizeof(TIFFCodec)));
            if (!new_codecs)
            {
                _TIFFfreeExt(NULL, codecs);
                return NULL;
            }
            codecs = new_codecs;
            _TIFFmemcpy(codecs + i - 1, c, sizeof(TIFFCodec));
            i++;
        }
    }

    new_codecs = (TIFFCodec *)_TIFFreallocExt(
        NULL, codecs, (tmsize_t)((size_t)i * sizeof(TIFFCodec)));
    if (!new_codecs)
    {
        _TIFFfreeExt(NULL, codecs);
        return NULL;
    }
    codecs = new_codecs;
    _TIFFmemset(codecs + i - 1, 0, sizeof(TIFFCodec));

    return codecs;
}
