'use strict';

var _ = require('lodash');
var math = require('../math');

// Remove points, that looks like straight line
function simplify(contours, accuracy) {
  return _.map(contours, function (contour) {
    var i, curr, prev, next;
    var p, pPrev, pNext;

    // run from the end, to simplify array elements removal
    for (i = contour.length - 2; i > 1; i--) {
      prev = contour[i - 1];
      next = contour[i + 1];
      curr = contour[i];

      // skip point (both oncurve & offcurve),
      // if [prev,next] is straight line
      if (prev.onCurve && next.onCurve) {
        p = new math.Point(curr.x, curr.y);
        pPrev = new math.Point(prev.x, prev.y);
        pNext = new math.Point(next.x, next.y);
        if (math.isInLine(pPrev, p, pNext, accuracy)) {
          contour.splice(i, 1);
        }
      }
    }
    return contour;
  });
}

// Remove interpolateable oncurve points
// Those should be in the middle of nebor offcurve points
function interpolate(contours, accuracy) {
  return _.map(contours, function (contour) {
    var resContour = [];

    _.forEach(contour, function (point, idx) {
      // Never skip first and last points
      if (idx === 0 || idx === (contour.length - 1)) {
        resContour.push(point);
        return;
      }

      var prev = contour[idx - 1];
      var next = contour[idx + 1];

      var p, pPrev, pNext;

      // skip interpolateable oncurve points (if exactly between previous and next offcurves)
      if (!prev.onCurve && point.onCurve && !next.onCurve) {
        p = new math.Point(point.x, point.y);
        pPrev = new math.Point(prev.x, prev.y);
        pNext = new math.Point(next.x, next.y);
        if (pPrev.add(pNext).div(2).sub(p).dist() < accuracy) {
          return;
        }
      }
      // keep the rest
      resContour.push(point);
    });
    return resContour;
  });
}

function roundPoints(contours) {
  return _.map(contours, function (contour) {
    return _.map(contour, function (point) {
      return { x: Math.round(point.x), y: Math.round(point.y), onCurve: point.onCurve };
    });
  });
}

// Remove closing point if it is the same as first point of contour.
// TTF doesn't need this point when drawing contours.
function removeClosingReturnPoints(contours) {
  return _.map(contours, function (contour) {
    var length = contour.length;

    if (length > 1 &&
      contour[0].x === contour[length - 1].x &&
      contour[0].y === contour[length - 1].y) {
      contour.splice(length - 1);
    }
    return contour;
  });
}

function toRelative(contours) {
  var prevPoint = { x: 0, y: 0 };
  var resContours = [];
  var resContour;

  _.forEach(contours, function (contour) {
    resContour = [];
    resContours.push(resContour);
    _.forEach(contour, function (point) {
      resContour.push({
        x: point.x - prevPoint.x,
        y: point.y - prevPoint.y,
        onCurve: point.onCurve
      });
      prevPoint = point;
    });
  });
  return resContours;
}

function identifier(string, littleEndian) {
  var result = 0;

  for (var i = 0; i < string.length; i++) {
    result = result << 8;
    var index = littleEndian ? string.length - i - 1 : i;

    result += string.charCodeAt(index);
  }

  return result;
}

module.exports.interpolate = interpolate;
module.exports.simplify = simplify;
module.exports.roundPoints = roundPoints;
module.exports.removeClosingReturnPoints = removeClosingReturnPoints;
module.exports.toRelative = toRelative;
module.exports.identifier = identifier;

