/**
 * @file css
 * @author junmer
 */

/* eslint-env node */
import _ from 'lodash';
import * as fs from 'fs';
import * as path from 'path';
import * as url from 'url';
import isTtf from 'is-ttf';
import through from 'through2';
import replaceExt from 'replace-ext';
import { b2a } from 'b3b';

/**
 * tpl
 *
 * @type {string}
 */
var tpl = fs.readFileSync(
    url.fileURLToPath(new URL('../lib/font-face.tpl', import.meta.url))
).toString('utf-8');

/**
 * renderCss
 *
 * @type {function}
 */
var renderCss = _.template(tpl);


/**
 * listUnicode
 *
 * @param  {Array} unicode unicode
 * @return {string}         unicode string
 */
function listUnicode(unicode) {
    return unicode.map(function (u) {
        return '\\' + u.toString(16);
    }).join(',');
}

/**
 * get glyf list from ttf obj
 *
 * @param {ttfObject} ttf ttfObject
 * @return {Object} icon obj
 */
function getGlyfList(ttf) {

    var glyfList = [];

    // exclude empty glyf
    var filtered = ttf.glyf.filter(function (g) {
        return g.name !== '.notdef'
            && g.name !== '.null'
            && g.name !== 'nonmarkingreturn'
            && g.unicode && g.unicode.length;
    });

    // format glyf info
    filtered.forEach(function (g) {
        glyfList.push({
            code: '&#x' + g.unicode[0].toString(16) + ';',
            codeName: listUnicode(g.unicode),
            name: g.name || 'uni' + g.unicode[0].toString(16)
        });
    });

    return {
        glyfList: glyfList
    };

}

/**
 * get font family name
 *
 * @param {Object} fontInfo font info object
 * @param {ttfObject} ttf ttfObject
 * @param {Object} opts opts
 * @return {string} font family name
 */
function getFontFamily(fontInfo, ttf, opts) {
    var fontFamily = opts.fontFamily;
    // Call transform function
    if (typeof fontFamily === 'function') {
        fontFamily = fontFamily(_.cloneDeep(fontInfo), ttf);
    }
    return fontFamily || ttf.name.fontFamily || fontInfo.fontFile;
}

/**
 * Transform font family name
 * @callback FontFamilyTransform
 * @param {Object} font info object
 * @param {ttfObject} ttf ttfObject
 * @return {string} font family name
 */
// function(fontInfo, ttfObject) { return "Font Name"; }

/**
 * css fontmin plugin
 *
 * @param {Object} opts opts
 * @param {boolean=} opts.glyph     generate class for each glyph. default = false
 * @param {boolean=} opts.base64    inject base64
 * @param {string=} opts.iconPrefix icon prefix
 * @param {string=} opts.filename  set filename
 * @param {(string|FontFamilyTransform)=} opts.fontFamily fontFamily
 * @return {Object} stream.Transform instance
 * @api public
 */
export default function (opts) {
    opts = opts || {};

    return through.ctor({
        objectMode: true
    }, function (file, enc, cb) {

        // check null
        if (file.isNull()) {
            cb(null, file);
            return;
        }

        // check stream
        if (file.isStream()) {
            cb(new Error('Streaming is not supported'));
            return;
        }

        // check ttf
        if (!isTtf(file.contents)) {
            cb(null, file);
            return;
        }

        // clone
        this.push(file.clone(false));

        file.path = replaceExt(file.path, '.css');
        var fontFile = opts.filename || path.basename(file.path, '.css');

        // font data
        var fontInfo = {
            fontFile: fontFile,
            fontPath: '',
            base64: '',
            glyph: false,
            iconPrefix: 'icon',
            local: false
        };

        // opts
        _.extend(fontInfo, opts);

        // ttf obj
        var ttfObject = file.ttfObject || {
            name: {}
        };

        // glyph
        if (opts.glyph && ttfObject.glyf) {
            _.extend(
                fontInfo,
                getGlyfList(ttfObject)
            );
        }

        // font family
        fontInfo.fontFamily = getFontFamily(fontInfo, ttfObject, opts);

        // rewrite font family as filename
        if (opts.asFileName) {
            fontInfo.fontFamily = fontFile;
        }

        // base64
        if (opts.base64) {
            fontInfo.base64 = ''
                + 'data:application/x-font-ttf;charset=utf-8;base64,'
                + b2a(file.contents);
        }

        // local
        if (fontInfo.local === true) {
            fontInfo.local = fontInfo.fontFamily;
        }

        // render
        var output = _.attempt(function (data) {
            return Buffer.from(renderCss(data));
        }, fontInfo);

        if (_.isError(output)) {
            cb(output, file);
        }
        else {
            file.contents = output;
            cb(null, file);
        }

    });

};
