
#include "artery-font-export.h"

#ifndef MSDF_ATLAS_NO_ARTERY_FONT

#include <artery-font/std-artery-font.h>
#include <artery-font/stdio-serialization.h>
#include "GlyphGeometry.h"
#include "image-encode.h"

namespace msdf_atlas {

static artery_font::ImageType convertImageType(ImageType imageType) {
    switch (imageType) {
        case ImageType::HARD_MASK:
        case ImageType::SOFT_MASK:
            return artery_font::IMAGE_LINEAR_MASK;
        case ImageType::SDF:
            return artery_font::IMAGE_SDF;
        case ImageType::PSDF:
            return artery_font::IMAGE_PSDF;
        case ImageType::MSDF:
            return artery_font::IMAGE_MSDF;
        case ImageType::MTSDF:
            return artery_font::IMAGE_MTSDF;
    }
    return artery_font::IMAGE_NONE;
}

static artery_font::CodepointType convertCodepointType(GlyphIdentifierType glyphIdentifierType) {
    switch (glyphIdentifierType) {
        case GlyphIdentifierType::GLYPH_INDEX:
            return artery_font::CP_INDEXED;
        case GlyphIdentifierType::UNICODE_CODEPOINT:
            return artery_font::CP_UNICODE;
    }
    return artery_font::CP_UNSPECIFIED;
}

template <typename T, int N>
static bool encodeTiff(std::vector<byte> &output, const msdfgen::BitmapConstRef<T, N> &atlas) {
    // TODO
    return false;
}

template <typename T>
static artery_font::PixelFormat getPixelFormat();

template <>
artery_font::PixelFormat getPixelFormat<byte>() {
    return artery_font::PIXEL_UNSIGNED8;
}
template <>
artery_font::PixelFormat getPixelFormat<float>() {
    return artery_font::PIXEL_FLOAT32;
}

template <typename REAL, typename T, int N>
bool exportArteryFont(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<T, N> &atlas, const char *filename, const ArteryFontExportProperties &properties) {
    artery_font::StdArteryFont<REAL> arfont = { };
    arfont.metadataFormat = artery_font::METADATA_NONE;

    arfont.variants = artery_font::StdList<typename artery_font::StdArteryFont<REAL>::Variant>(fontCount);
    for (int i = 0; i < fontCount; ++i) {
        const FontGeometry &font = fonts[i];
        GlyphIdentifierType identifierType = font.getPreferredIdentifierType();
        const msdfgen::FontMetrics &fontMetrics = font.getMetrics();
        typename artery_font::StdArteryFont<REAL>::Variant &fontVariant = arfont.variants[i] = typename artery_font::StdArteryFont<REAL>::Variant();
        fontVariant.codepointType = convertCodepointType(identifierType);
        fontVariant.imageType = convertImageType(properties.imageType);
        fontVariant.metrics.fontSize = REAL(properties.fontSize*fontMetrics.emSize);
        if (properties.imageType != ImageType::HARD_MASK)
            fontVariant.metrics.distanceRange = REAL(properties.pxRange);
        fontVariant.metrics.emSize = REAL(fontMetrics.emSize);
        fontVariant.metrics.ascender = REAL(fontMetrics.ascenderY);
        fontVariant.metrics.descender = REAL(fontMetrics.descenderY);
        fontVariant.metrics.lineHeight = REAL(fontMetrics.lineHeight);
        fontVariant.metrics.underlineY = REAL(fontMetrics.underlineY);
        fontVariant.metrics.underlineThickness = REAL(fontMetrics.underlineThickness);
        const char *name = font.getName();
        if (name)
            (std::string &) fontVariant.name = name;
        fontVariant.glyphs = artery_font::StdList<artery_font::Glyph<REAL> >(font.getGlyphs().size());
        int j = 0;
        for (const GlyphGeometry &glyphGeom : font.getGlyphs()) {
            artery_font::Glyph<REAL> &glyph = fontVariant.glyphs[j++];
            glyph.codepoint = glyphGeom.getIdentifier(identifierType);
            glyph.image = 0;
            double l, b, r, t;
            glyphGeom.getQuadPlaneBounds(l, b, r, t);
            glyph.planeBounds.l = REAL(l);
            glyph.planeBounds.b = REAL(b);
            glyph.planeBounds.r = REAL(r);
            glyph.planeBounds.t = REAL(t);
            glyphGeom.getQuadAtlasBounds(l, b, r, t);
            glyph.imageBounds.l = REAL(l);
            glyph.imageBounds.b = REAL(b);
            glyph.imageBounds.r = REAL(r);
            glyph.imageBounds.t = REAL(t);
            glyph.advance.h = REAL(glyphGeom.getAdvance());
            glyph.advance.v = REAL(0);
        }
        switch (identifierType) {
            case GlyphIdentifierType::GLYPH_INDEX:
                for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
                    artery_font::KernPair<REAL> kernPair = { };
                    kernPair.codepoint1 = elem.first.first;
                    kernPair.codepoint2 = elem.first.second;
                    kernPair.advance.h = REAL(elem.second);
                    ((std::vector<artery_font::KernPair<REAL> > &) fontVariant.kernPairs).push_back((artery_font::KernPair<REAL> &&) kernPair);
                }
                break;
            case GlyphIdentifierType::UNICODE_CODEPOINT:
                for (const std::pair<std::pair<int, int>, double> &elem : font.getKerning()) {
                    const GlyphGeometry *glyph1 = font.getGlyph(msdfgen::GlyphIndex(elem.first.first));
                    const GlyphGeometry *glyph2 = font.getGlyph(msdfgen::GlyphIndex(elem.first.second));
                    if (glyph1 && glyph2 && glyph1->getCodepoint() && glyph2->getCodepoint()) {
                        artery_font::KernPair<REAL> kernPair = { };
                        kernPair.codepoint1 = glyph1->getCodepoint();
                        kernPair.codepoint2 = glyph2->getCodepoint();
                        kernPair.advance.h = REAL(elem.second);
                        ((std::vector<artery_font::KernPair<REAL> > &) fontVariant.kernPairs).push_back((artery_font::KernPair<REAL> &&) kernPair);
                    }
                }
                break;
        }
    }

    arfont.images = artery_font::StdList<typename artery_font::StdArteryFont<REAL>::Image>(1);
    {
        typename artery_font::StdArteryFont<REAL>::Image &image = arfont.images[0] = typename artery_font::StdArteryFont<REAL>::Image();
        image.width = atlas.width;
        image.height = atlas.height;
        image.channels = N;
        image.imageType = convertImageType(properties.imageType);
        switch (properties.imageFormat) {
            case ImageFormat::PNG:
                image.encoding = artery_font::IMAGE_PNG;
                image.pixelFormat = artery_font::PIXEL_UNSIGNED8;
                if (!encodePng((std::vector<byte> &) image.data, atlas))
                    return false;
                break;
            case ImageFormat::TIFF:
                image.encoding = artery_font::IMAGE_TIFF;
                image.pixelFormat = artery_font::PIXEL_FLOAT32;
                if (!encodeTiff((std::vector<byte> &) image.data, atlas))
                    return false;
                break;
            case ImageFormat::BINARY:
                image.pixelFormat = artery_font::PIXEL_UNSIGNED8;
                goto BINARY_EITHER;
            case ImageFormat::BINARY_FLOAT:
                image.pixelFormat = artery_font::PIXEL_FLOAT32;
                goto BINARY_EITHER;
            BINARY_EITHER:
                if (image.pixelFormat != getPixelFormat<T>())
                    return false;
                image.encoding = artery_font::IMAGE_RAW_BINARY;
                image.rawBinaryFormat.rowLength = N*sizeof(T)*atlas.width;
                image.data = artery_font::StdByteArray(N*sizeof(T)*atlas.width*atlas.height);
                switch (properties.yDirection) {
                    case YDirection::BOTTOM_UP:
                        image.rawBinaryFormat.orientation = artery_font::ORIENTATION_BOTTOM_UP;
                        memcpy((byte *) image.data, atlas.pixels, N*sizeof(T)*atlas.width*atlas.height);
                        break;
                    case YDirection::TOP_DOWN: {
                        image.rawBinaryFormat.orientation = artery_font::ORIENTATION_TOP_DOWN;
                        byte *imageData = (byte *) image.data;
                        for (int y = atlas.height-1; y >= 0; --y) {
                            memcpy(imageData, atlas.pixels+N*atlas.width*y, N*sizeof(T)*atlas.width);
                            imageData += N*sizeof(T)*atlas.width;
                        }
                        break;
                    }
                }
                break;
            default:
                return false;
        }
    }

    return artery_font::writeFile(arfont, filename);
}

template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<byte, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 1> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 3> &atlas, const char *filename, const ArteryFontExportProperties &properties);
template bool exportArteryFont<float>(const FontGeometry *fonts, int fontCount, const msdfgen::BitmapConstRef<float, 4> &atlas, const char *filename, const ArteryFontExportProperties &properties);

}

#endif
