import {PNG} from 'pngjs';
import {Projection} from '../../utils/types';
import * as d3geo from 'd3-geo';

export default interface ProjectionService {}
export class ProjectionServiceImpl implements ProjectionService {
  private readonly projections: Record<Projection, Function> = {
    [Projection.EQUIRECTANGULAR]: d3geo.geoEquirectangular,
    [Projection.MERCATOR]: d3geo.geoMercator,
  } as const

  private readonly referenceWidth = 960 as const;

  project(equiImage: PNG, projection: Projection) {
    if (projection === Projection.EQUIRECTANGULAR) {
      return equiImage;
    }

    const projectionFunction = this.projections[projection]();
    if (!projectionFunction.invert) {
      return undefined;
    }

    const sx = equiImage.width;
    const sy = equiImage.height;
    const sourceData = equiImage.data;
    const tx = sx;
    // const py = projection === Projection.MERCATOR ? dy * 2 : dy;
    const ty = sy;
    const target = new PNG({
      width: tx,
      height: ty,
    });
    const targetData = target.data;

    let i = 0;
    for (let y = 0; y < sy; y += 1) {
      for (let x = 0; x < sx; x += 1) {
        const projected = projectionFunction.invert([x / sx * this.referenceWidth, y / sx * this.referenceWidth]) as [number, number];
        if (projected) {
          const [lambda, phi] = projected;
          if (!(lambda > 180 || lambda < -180 || phi > 90 || phi < -90)) {
            const q = ((90 - phi) / 180 * sy | 0) * sx + ((180 + lambda) / 360 * sx | 0) << 2;
            targetData[i] = sourceData[q];
            targetData[i + 1] = sourceData[q + 1];
            targetData[i + 2] = sourceData[q + 2];
            targetData[i + 3] = 255;
          }
        }
        i += 4;
      }
    }

    return target;
  }
}

