/* Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/). All Rights Reserved.
   This software is licensed as OpenSource, under the Apache License, Version 2.0.
   This license is available at: http://opensource.org/licenses/Apache-2.0. */

/*
 * Abstract font descriptor management.
 */

#include "abfdesc.h"
#include "absfont.h"
#include "dictops.h"

#include <string.h>

/* Fill out the size fields in an abfFontDescElement. */
static void fillFontDescElementSizes(abfFontDict *font,
                                     abfFontDescSpecialValues *sv,
                                     abfFontDescElement *elem) {
    abfPrivateDict *Private = &font->Private;

    elem->flags = 0;
    elem->valueCnt = 0;

    /* Count fixed-size values */
    if (sv->CharstringType != cff_DFLT_CharstringType) {
        elem->flags |= ABF_DESC_CharstringType;
        elem->valueCnt++;
    }
    if (font->PaintType != cff_DFLT_PaintType) {
        elem->flags |= ABF_DESC_PaintType;
        elem->valueCnt++;
    }
    if (Private->BlueScale != (float)cff_DFLT_BlueScale) {
        elem->flags |= ABF_DESC_BlueScale;
        elem->valueCnt++;
    }
    if (Private->BlueShift != cff_DFLT_BlueShift) {
        elem->flags |= ABF_DESC_BlueShift;
        elem->valueCnt++;
    }
    if (Private->BlueFuzz != cff_DFLT_BlueFuzz) {
        elem->flags |= ABF_DESC_BlueFuzz;
        elem->valueCnt++;
    }
    if (Private->StdHW != ABF_UNSET_REAL) {
        elem->flags |= ABF_DESC_StdHW;
        elem->valueCnt++;
    }
    if (Private->StdVW != ABF_UNSET_REAL) {
        elem->flags |= ABF_DESC_StdVW;
        elem->valueCnt++;
    }
    if (Private->ForceBold != cff_DFLT_ForceBold) {
        elem->flags |= ABF_DESC_ForceBold;
        elem->valueCnt++;
    }
    if (Private->LanguageGroup != cff_DFLT_LanguageGroup) {
        elem->flags |= ABF_DESC_LanguageGroup;
        elem->valueCnt++;
    }
    if (Private->ExpansionFactor != (float)cff_DFLT_ExpansionFactor) {
        elem->flags |= ABF_DESC_ExpansionFactor;
        elem->valueCnt++;
    }
    if (Private->initialRandomSeed != cff_DFLT_initialRandomSeed) {
        elem->flags |= ABF_DESC_initialRandomSeed;
        elem->valueCnt++;
    }
    if (sv->defaultWidthX != cff_DFLT_defaultWidthX) {
        elem->flags |= ABF_DESC_defaultWidthX;
        elem->valueCnt++;
    }
    if (sv->nominalWidthX != cff_DFLT_nominalWidthX) {
        elem->flags |= ABF_DESC_nominalWidthX;
        elem->valueCnt++;
    }
    if (sv->lenSubrArray != 0) {
        elem->flags |= ABF_DESC_lenSubrArray;
        elem->valueCnt++;
    }
    if (!abfIsDefaultFontMatrix(&font->FontMatrix)) {
        elem->flags |= ABF_DESC_FontMatrix;
        elem->valueCnt += 6;
    }

    /* Count variable-size values */
    elem->BlueValues = (unsigned char)Private->BlueValues.cnt;
    elem->OtherBlues = (unsigned char)Private->OtherBlues.cnt;
    elem->FamilyBlues = (unsigned char)Private->FamilyBlues.cnt;
    elem->FamilyOtherBlues = (unsigned char)Private->FamilyOtherBlues.cnt;
    elem->StemSnapH = (unsigned char)Private->StemSnapH.cnt;
    elem->StemSnapV = (unsigned char)Private->StemSnapV.cnt;

    /* Totalize values */
    elem->valueCnt += (elem->BlueValues +
                       elem->OtherBlues +
                       elem->FamilyBlues +
                       elem->FamilyOtherBlues +
                       elem->StemSnapH +
                       elem->StemSnapV);
}

/* Save float value in destination array. */
static float *saveValue(float *dst, float src, int save) {
    if (save != 0)
        *dst++ = src;
    return dst;
}

/* Save float values in destination array. */
static float *saveValues(float *dst, float *src, int cnt) {
    if (cnt > 0)
        memmove(dst, src, cnt * sizeof(float));
    return dst + cnt;
}

/* Fill out the value fields in an abfFontDescElement. */
static void fillFontDescElementValues(abfFontDict *font,
                                      abfFontDescSpecialValues *sv,
                                      abfFontDescElement *elem) {
    float *values = elem->values;
    abfPrivateDict *Private = &font->Private;

    /* Fill single-valued parameter values */
    values = saveValue(values, (float)sv->CharstringType,
                       elem->flags & ABF_DESC_CharstringType);
    values = saveValue(values, (float)font->PaintType,
                       elem->flags & ABF_DESC_PaintType);
    values = saveValue(values, Private->BlueScale,
                       elem->flags & ABF_DESC_BlueScale);
    values = saveValue(values, Private->BlueShift,
                       elem->flags & ABF_DESC_BlueShift);
    values = saveValue(values, Private->BlueFuzz,
                       elem->flags & ABF_DESC_BlueFuzz);
    values = saveValue(values, Private->StdHW,
                       elem->flags & ABF_DESC_StdHW);
    values = saveValue(values, Private->StdVW,
                       elem->flags & ABF_DESC_StdVW);
    values = saveValue(values, (float)Private->LanguageGroup,
                       elem->flags & ABF_DESC_LanguageGroup);
    values = saveValue(values, (float)Private->ForceBold,
                       elem->flags & ABF_DESC_ForceBold);
    values = saveValue(values, Private->ExpansionFactor,
                       elem->flags & ABF_DESC_ExpansionFactor);
    values = saveValue(values, Private->initialRandomSeed,
                       elem->flags & ABF_DESC_initialRandomSeed);
    values = saveValue(values, sv->defaultWidthX,
                       elem->flags & ABF_DESC_defaultWidthX);
    values = saveValue(values, sv->nominalWidthX,
                       elem->flags & ABF_DESC_nominalWidthX);
    values = saveValue(values, (float)sv->lenSubrArray,
                       elem->flags & ABF_DESC_lenSubrArray);

    /* Fill multi-valued parameter values */
    values = saveValues(values, font->FontMatrix.array,
                        (elem->flags & ABF_DESC_FontMatrix) ? 6 : 0);
    values = saveValues(values, Private->BlueValues.array, elem->BlueValues);
    values = saveValues(values, Private->OtherBlues.array, elem->OtherBlues);
    values = saveValues(values, Private->FamilyBlues.array, elem->FamilyBlues);
    values = saveValues(values, Private->FamilyOtherBlues.array,
                        elem->FamilyOtherBlues);
    values = saveValues(values, Private->StemSnapH.array, elem->StemSnapH);
    values = saveValues(values, Private->StemSnapV.array, elem->StemSnapV);
}

/* Create abstract font descriptor. */
abfFontDescHeader *abfNewFontDesc(ctlMemoryCallbacks *mem_cb,
                                  abfFontDescCallbacks *desc_cb,
                                  long lenGSubrArray,
                                  abfTopDict *top) {
    long i;
    size_t size;
    abfFontDescHeader *hdr;
    abfFontDescElement *elem;
    abfFontDescSpecialValues sv;

    /* Compute total structure size */
    size = sizeof(abfFontDescHeader);
    for (i = 0; i < top->FDArray.cnt; i++) {
        abfFontDescElement tmp;
        desc_cb->getSpecialValues(desc_cb, (int)i, &sv);
        fillFontDescElementSizes(&top->FDArray.array[i], &sv, &tmp);
        size += (sizeof(abfFontDescElement) +
                 (tmp.valueCnt - 1) * sizeof(tmp.values[0]));
    }

    /* Allocate structure */
    hdr = (abfFontDescHeader *)mem_cb->manage(mem_cb, NULL, size);
    if (hdr == NULL)
        return NULL;
    // memset(hdr, 0, size);

    /* Fill header */
    hdr->length = (unsigned short)size;
    hdr->FDElementCnt = (unsigned short)top->FDArray.cnt;
    memcpy(hdr->FontBBox, top->FontBBox, sizeof(hdr->FontBBox));
    hdr->StrokeWidth = top->StrokeWidth;
    hdr->lenGSubrArray = (float)lenGSubrArray;

    /* Fill elements */
    elem = ABF_GET_FIRST_DESC(hdr);
    for (i = 0; i < top->FDArray.cnt; i++) {
        abfFontDict *fd = &top->FDArray.array[i];
        desc_cb->getSpecialValues(desc_cb, (int)i, &sv);
        fillFontDescElementSizes(fd, &sv, elem);
        fillFontDescElementValues(fd, &sv, elem);
        elem = ABF_GET_NEXT_DESC(elem);
    }

    return hdr;
}

/* Return FontMatrix from font descriptor. */
float *abfGetFontDescMatrix(abfFontDescHeader *hdr, int iFD) {
    if (iFD >= 0 && iFD < hdr->FDElementCnt) {
        /* Skip to FDArray element */
        abfFontDescElement *elem = ABF_GET_FIRST_DESC(hdr);
        while (iFD--)
            elem = ABF_GET_NEXT_DESC(elem);

        if (elem->flags & ABF_DESC_FontMatrix) {
            /* Non-default FontMatrix; count values that precede it */
            long count;
            long bits = elem->flags & (ABF_DESC_FontMatrix - 1);
            for (count = 0; bits; count++)
                bits &= bits - 1;
            if (count + 6 <= (long)elem->valueCnt)
                return &elem->values[count];
        }
    }
    return NULL;
}

/* Free abstract font descriptor. */
void abfFreeFontDesc(ctlMemoryCallbacks *mem_cb, abfFontDescHeader *hdr) {
    (void)mem_cb->manage(mem_cb, hdr, 0);
}
