import {geoProjection as projection} from "d3-geo";
import {augustRaw} from "./august";
import {abs, atan, cos, epsilon, halfPi, log, max, min, sin, sqrt, sqrt1_2, sqrt2} from "./math";

var eisenlohrK = 3 + 2 * sqrt2;

export function eisenlohrRaw(lambda, phi) {
  var s0 = sin(lambda /= 2),
      c0 = cos(lambda),
      k = sqrt(cos(phi)),
      c1 = cos(phi /= 2),
      t = sin(phi) / (c1 + sqrt2 * c0 * k),
      c = sqrt(2 / (1 + t * t)),
      v = sqrt((sqrt2 * c1 + (c0 + s0) * k) / (sqrt2 * c1 + (c0 - s0) * k));
  return [
    eisenlohrK * (c * (v - 1 / v) - 2 * log(v)),
    eisenlohrK * (c * t * (v + 1 / v) - 2 * atan(t))
  ];
}

eisenlohrRaw.invert = function(x, y) {
  if (!(p = augustRaw.invert(x / 1.2, y * 1.065))) return null;
  var lambda = p[0], phi = p[1], i = 20, p;
  x /= eisenlohrK, y /= eisenlohrK;
  do {
    var _0 = lambda / 2,
        _1 = phi / 2,
        s0 = sin(_0),
        c0 = cos(_0),
        s1 = sin(_1),
        c1 = cos(_1),
        cos1 = cos(phi),
        k = sqrt(cos1),
        t = s1 / (c1 + sqrt2 * c0 * k),
        t2 = t * t,
        c = sqrt(2 / (1 + t2)),
        v0 = (sqrt2 * c1 + (c0 + s0) * k),
        v1 = (sqrt2 * c1 + (c0 - s0) * k),
        v2 = v0 / v1,
        v = sqrt(v2),
        vm1v = v - 1 / v,
        vp1v = v + 1 / v,
        fx = c * vm1v - 2 * log(v) - x,
        fy = c * t * vp1v - 2 * atan(t) - y,
        deltatDeltaLambda = s1 && sqrt1_2 * k * s0 * t2 / s1,
        deltatDeltaPhi = (sqrt2 * c0 * c1 + k) / (2 * (c1 + sqrt2 * c0 * k) * (c1 + sqrt2 * c0 * k) * k),
        deltacDeltat = -0.5 * t * c * c * c,
        deltacDeltaLambda = deltacDeltat * deltatDeltaLambda,
        deltacDeltaPhi = deltacDeltat * deltatDeltaPhi,
        A = (A = 2 * c1 + sqrt2 * k * (c0 - s0)) * A * v,
        deltavDeltaLambda = (sqrt2 * c0 * c1 * k + cos1) / A,
        deltavDeltaPhi = -(sqrt2 * s0 * s1) / (k * A),
        deltaxDeltaLambda = vm1v * deltacDeltaLambda - 2 * deltavDeltaLambda / v + c * (deltavDeltaLambda + deltavDeltaLambda / v2),
        deltaxDeltaPhi = vm1v * deltacDeltaPhi - 2 * deltavDeltaPhi / v + c * (deltavDeltaPhi + deltavDeltaPhi / v2),
        deltayDeltaLambda = t * vp1v * deltacDeltaLambda - 2 * deltatDeltaLambda / (1 + t2) + c * vp1v * deltatDeltaLambda + c * t * (deltavDeltaLambda - deltavDeltaLambda / v2),
        deltayDeltaPhi = t * vp1v * deltacDeltaPhi - 2 * deltatDeltaPhi / (1 + t2) + c * vp1v * deltatDeltaPhi + c * t * (deltavDeltaPhi - deltavDeltaPhi / v2),
        denominator = deltaxDeltaPhi * deltayDeltaLambda - deltayDeltaPhi * deltaxDeltaLambda;
    if (!denominator) break;
    var deltaLambda = (fy * deltaxDeltaPhi - fx * deltayDeltaPhi) / denominator,
        deltaPhi = (fx * deltayDeltaLambda - fy * deltaxDeltaLambda) / denominator;
    lambda -= deltaLambda;
    phi = max(-halfPi, min(halfPi, phi - deltaPhi));
  } while ((abs(deltaLambda) > epsilon || abs(deltaPhi) > epsilon) && --i > 0);
  return abs(abs(phi) - halfPi) < epsilon ? [0, phi] : i && [lambda, phi];
};

export default function() {
  return projection(eisenlohrRaw)
      .scale(62.5271);
}
