#include "Atlas.h"
#include "BitmapRef.hpp"
#include "GlyphBox.h"
#include "conversion.h"
#include "edge-coloring.h"
#include "import-font.h"
#include "ofFileUtils.h"
#include "ofTrueTypeFont.h"
#include <cstdlib>
#include <set>

ofxMsdfgen::Atlas::Atlas(){
}
ofxMsdfgen::Atlas::~Atlas(){
}

void ofxMsdfgen::Atlas::setup(string _fontPath){
    fontPath = _fontPath;
    if(generate()){
        ofLogNotice("ofxMsdfgen::Atlas::setup()") << "generated Atlas";
    }else{
        ofLogError("ofxMsdfgen::Atlas::setup()") << "whoops, could not generate Atlas";
    }
}

void ofxMsdfgen::Atlas::setup(string _fontPath,
                              AtlasSettings _settings){
    settings = _settings;
    setup(_fontPath);
}

bool ofxMsdfgen::Atlas::generate(){
    bool success = false;
    //{
    msdfgen::FreetypeHandle * ft = msdfgen::initializeFreetype();
    if(ft){
        const char * fontPath_c_str = fontPath.c_str();
        msdfgen::FontHandle * font = loadFont(ft, fontPath_c_str);
        if(font){
            // Storage for glyph geometry and their coordinates in the atlas
            std::vector <msdf_atlas::GlyphGeometry> glyphs;
            // FontGeometry is a helper class that loads a set of glyphs from a single font.
            // It can also be used to get additional font metrics, kerning information, etc.
            msdf_atlas::FontGeometry fontGeometry(&glyphs);
            // Load a set of character glyphs:
            // The second argument can be ignored unless you mix different font sizes in one atlas.
            // In the last argument, you can specify a charset other than ASCII.
            // To load specific glyph indices, use loadGlyphs instead.
            if(settings.characters == "charset::ascii"){
                fontGeometry.loadCharset(font, 1.0, msdf_atlas::Charset::ASCII);
            }else if(settings.characters == ""){
                msdfgen::GlyphIndex glyphIndex;
            }else{
                msdf_atlas::Charset charset;
                for(const unsigned char c : settings.characters){
                    msdf_atlas::unicode_t ut(c);
                    cout << "add char(" << c << " " << ofToString(ut) << ")" << endl;
                    charset.add(ut);
                    cout << "charset.size: " << charset.size() << endl;
                }
                fontGeometry.loadCharset(font, 1.0, charset);
                if(glyphs.size() != settings.characters.length()){
                    fontGeometry.getGlyphs().empty();
                    fontGeometry.loadCharset(font,
                                             1.0,     // fontScale
                                             charset, // charset
                                             false,   // preprocess geometry
                                             true);   // kerning
                }
            }
            // Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies.
            for(msdf_atlas::GlyphGeometry & glyph : glyphs){
                //FIXME: we do not need to inverse y axis, something else is wrong
                // msdfgen::Shape & shape = const_cast <msdfgen::Shape &>(glyph.getShape());
                // shape.inverseYAxis = true;
                glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, // strategy
                                   settings.maxCornerAngle,       // angleThreshold
                                   0);                            // seed
            }
            // TightAtlasPacker class computes the layout of the atlas.
            msdf_atlas::TightAtlasPacker packer;
            // Set atlas parameters:
            // setDimensions or setDimensionsConstraint to find the best value
            packer.setDimensionsConstraint(msdf_atlas::TightAtlasPacker::DimensionsConstraint::SQUARE);
            // setScale for a fixed size or setMinimumScale to use the largest that fits
            //packer.setMinimumScale(settings.minimumScale);
            packer.setScale(settings.scale);
            // setPixelRange or setUnitRange
            packer.setPixelRange(settings.pixelRange);
            packer.setMiterLimit(settings.miterLimit);
            // Compute atlas layout - pack glyphs
            packer.pack(glyphs.data(), glyphs.size());
            // Get final atlas dimensions
            int width = 0, height = 0;
            packer.getDimensions(width, height);
            // The ImmediateAtlasGenerator class facilitates the generation of the atlas bitmap.
            msdf_atlas::ImmediateAtlasGenerator <
                float,     // pixel type of buffer for individual glyphs depends on generator function
                3,     // number of atlas color channels
                & msdf_atlas::msdfGenerator,     // function to generate bitmaps for individual glyphs
                msdf_atlas::BitmapAtlasStorage <float, 3>     // class that stores the atlas bitmap
                // For example, a custom atlas storage class that stores it in VRAM can be used.
                > generator(width, height);
            // GeneratorAttributes can be modified to change the generator's default settings.
            msdf_atlas::GeneratorAttributes attributes;
            generator.setAttributes(attributes);
            generator.setThreadCount(settings.threadCount);
            // Generate atlas bitmap
            generator.generate(glyphs.data(), glyphs.size());
            // The atlas bitmap can now be retrieved via atlasStorage as a BitmapConstRef.
            // The glyphs array (or fontGeometry) contains positioning data for typesetting text.
            msdfgen::Bitmap <float, 3> bitmap(generator.atlasStorage());
            ofxMsdfgen::toOfImage(bitmap, atlasImage);

            std::vector <msdf_atlas::GlyphBox> layout = generator.getLayout();
            int i = 0;
            for(const msdf_atlas::GlyphBox & gb : layout){
                msdf_atlas::GlyphGeometry gg = glyphs.data()[i];
                i++;
                glyphGeometries.push_back(gg);
            }
            // Cleanup
            msdfgen::destroyFont(font);

            success = true;     // FIXME: always turns true, why do we have this
        }
        msdfgen::deinitializeFreetype(ft);
    }
    return success;
}

const ofImage & ofxMsdfgen::Atlas::getAtlasImage(){
    return atlasImage;
}

const vector <ofxMsdfgen::GlyphGeometry> & ofxMsdfgen::Atlas::getGlyphGeometries(){
    return glyphGeometries;
}
