/*
 * Copyright: Vitaly Puzrin
 * Author: Sergey Batishchev <snb2003@rambler.ru>
 *
 * Written for fontello.com project.
 */

'use strict';

var _       = require('lodash');
var SvgPath = require('svgpath');
var ucs2 = require('./lib/ucs2');
var svg     = require('./lib/svg');
var sfnt    = require('./lib/sfnt');


var VERSION_RE = /^(Version )?(\d+[.]\d+)$/i;


function svg2ttf(svgString, options) {
  var font = new sfnt.Font();
  var svgFont = svg.load(svgString);

  options = options || {};

  font.id = options.id || svgFont.id;
  font.familyName = options.familyname || svgFont.familyName || svgFont.id;
  font.copyright = options.copyright || svgFont.metadata;
  font.description = options.description || 'Generated by svg2ttf from Fontello project.';
  font.url = options.url || 'http://fontello.com';
  font.sfntNames.push({ id: 2, value: options.subfamilyname || svgFont.subfamilyName || 'Regular' }); // subfamily name
  font.sfntNames.push({ id: 4, value: options.fullname || svgFont.id }); // full name

  var versionString = options.version || 'Version 1.0';

  if (typeof versionString !== 'string') {
    throw new Error('svg2ttf: version option should be a string');
  }
  if (!VERSION_RE.test(versionString)) {
    throw new Error('svg2ttf: invalid option, version - "' + options.version + '"');
  }

  versionString = 'Version ' + versionString.match(VERSION_RE)[2];
  font.sfntNames.push({ id: 5, value: versionString }); // version ID for TTF name table
  font.sfntNames.push({ id: 6, value: (options.fullname || svgFont.id).replace(/[\s\(\)\[\]<>%\/]/g, '').substr(0, 62) }); // Postscript name for the font, required for OSX Font Book

  if (typeof options.ts !== 'undefined') {
    font.createdDate = font.modifiedDate = new Date(parseInt(options.ts, 10) * 1000);
  }

  // Try to fill font metrics or guess defaults
  //
  font.unitsPerEm   = svgFont.unitsPerEm || 1000;
  font.horizOriginX = svgFont.horizOriginX || 0;
  font.horizOriginY = svgFont.horizOriginY || 0;
  font.vertOriginX  = svgFont.vertOriginX || 0;
  font.vertOriginY  = svgFont.vertOriginY || 0;
  font.width        = svgFont.width || svgFont.unitsPerEm;
  font.height       = svgFont.height || svgFont.unitsPerEm;
  font.descent      = !isNaN(svgFont.descent) ? svgFont.descent : -font.vertOriginY;
  font.ascent       = svgFont.ascent || (font.unitsPerEm - font.vertOriginY);
  // Values for font substitution. We're mostly working with icon fonts, so they aren't expected to be substituted.
  // https://docs.microsoft.com/en-us/typography/opentype/spec/os2#sxheight
  font.capHeight    = svgFont.capHeight || 0; // 0 is a valid value if "H" glyph doesn't exist
  font.xHeight      = svgFont.xHeight || 0;   // 0 is a valid value if "x" glyph doesn't exist

  if (typeof svgFont.weightClass !== 'undefined') {
    var wght = parseInt(svgFont.weightClass, 10);

    if (!isNaN(wght)) font.weightClass = wght;
    else {
      // Unknown names are silently ignored
      if (svgFont.weightClass === 'normal') font.weightClass = 400;
      if (svgFont.weightClass === 'bold') font.weightClass = 700;
    }
  }


  if (typeof svgFont.underlinePosition !== 'undefined') {
    font.underlinePosition = svgFont.underlinePosition;
  }
  if (typeof svgFont.underlineThickness !== 'undefined') {
    font.underlineThickness = svgFont.underlineThickness;
  }

  var glyphs = font.glyphs;
  var codePoints = font.codePoints;
  var ligatures = font.ligatures;

  function addCodePoint(codePoint, glyph) {
    if (codePoints[codePoint]) {
      // Ignore code points already defined
      return false;
    }
    codePoints[codePoint] = glyph;
    return true;
  }

  // add SVG glyphs to SFNT font
  _.forEach(svgFont.glyphs, function (svgGlyph) {
    var glyph = new sfnt.Glyph();

    glyph.name = svgGlyph.name;
    glyph.codes = svgGlyph.ligatureCodes || svgGlyph.unicode; // needed for nice validator error output
    glyph.d = svgGlyph.d;
    glyph.height = !isNaN(svgGlyph.height) ? svgGlyph.height : font.height;
    glyph.width = !isNaN(svgGlyph.width) ? svgGlyph.width : font.width;
    glyphs.push(glyph);

    svgGlyph.sfntGlyph = glyph;

    _.forEach(svgGlyph.unicode, function (codePoint) {
      addCodePoint(codePoint, glyph);
    });
  });

  var missingGlyph;

  // add missing glyph to SFNT font
  // also, check missing glyph existance and single instance
  if (svgFont.missingGlyph) {
    missingGlyph = new sfnt.Glyph();
    missingGlyph.d = svgFont.missingGlyph.d;
    missingGlyph.height = !isNaN(svgFont.missingGlyph.height) ? svgFont.missingGlyph.height : font.height;
    missingGlyph.width = !isNaN(svgFont.missingGlyph.width) ? svgFont.missingGlyph.width : font.width;
  } else {
    missingGlyph = _.find(glyphs, function (glyph) {
      return glyph.name === '.notdef';
    });
  }
  if (!missingGlyph) { // no missing glyph and .notdef glyph, we need to create missing glyph
    missingGlyph = new sfnt.Glyph();
  }

  // Create glyphs for all characters used in ligatures
  _.forEach(svgFont.ligatures, function (svgLigature) {
    var ligature = {
      ligature: svgLigature.ligature,
      unicode: svgLigature.unicode,
      glyph: svgLigature.glyph.sfntGlyph
    };

    _.forEach(ligature.unicode, function (charPoint) {
      // We need to have a distinct glyph for each code point so we can reference it in GSUB
      var glyph = new sfnt.Glyph();
      var added = addCodePoint(charPoint, glyph);

      if (added) {
        glyph.name = ucs2.encode([ charPoint ]);
        glyphs.push(glyph);
      }
    });
    ligatures.push(ligature);
  });

  // Missing Glyph needs to have index 0
  if (glyphs.indexOf(missingGlyph) !== -1) {
    glyphs.splice(glyphs.indexOf(missingGlyph), 1);
  }
  glyphs.unshift(missingGlyph);

  var nextID = 0;

  //add IDs
  _.forEach(glyphs, function (glyph) {
    glyph.id = nextID;
    nextID++;
  });

  _.forEach(glyphs, function (glyph) {

    // Calculate accuracy for cubicToQuad transformation
    // For glyphs with height and width smaller than 500 use relative 0.06% accuracy,
    // for larger glyphs use fixed accuracy 0.3.
    var glyphSize = Math.max(glyph.width, glyph.height);
    var accuracy = (glyphSize > 500) ? 0.3 : glyphSize * 0.0006;

    //SVG transformations
    var svgPath = new SvgPath(glyph.d)
      .abs()
      .unshort()
      .unarc()
      .iterate(function (segment, index, x, y) {
        return svg.cubicToQuad(segment, index, x, y, accuracy);
      });
    var sfntContours = svg.toSfntCoutours(svgPath);

    // Add contours to SFNT font
    glyph.contours = _.map(sfntContours, function (sfntContour) {
      var contour = new sfnt.Contour();

      contour.points = _.map(sfntContour, function (sfntPoint) {
        var point = new sfnt.Point();

        point.x = sfntPoint.x;
        point.y = sfntPoint.y;
        point.onCurve = sfntPoint.onCurve;
        return point;
      });

      return contour;
    });
  });

  var ttf = sfnt.toTTF(font);

  return ttf;
}

module.exports = svg2ttf;
