import proj4 from "proj4";

// 屏幕点和真实点通用对象
type Pos = [number, number, number];
type Conversion = {
  scale: number;
  rotation: number;
  translate: number[];
  startAltitude: number[];
  altitude: number;
};

// 获取角度
const getAngle = (p1: Pos, p2: Pos) => Math.atan2(p2[0] - p1[0], p2[1] - p1[1]);
// 获取长度
const getLength = (p1: Pos, p2: Pos) =>
  Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2));

// 获取本地与魔卡托的映射关系
const getConversion2D = ([p1, p2]: Pos[], [b1, b2]: Pos[]) => {
  const rotation = getAngle(p1, p2) - getAngle(b1, b2);
  const scale = getLength(b1, b2) / getLength(p1, p2);
  const translate = [
    b1[0] - scale * (p1[0] * Math.cos(rotation) - p1[1] * Math.sin(rotation)),
    b1[1] - scale * (p1[0] * Math.sin(rotation) + p1[1] * Math.cos(rotation)),
  ];
  const altitude = (b2[2] - b1[2]) / (p2[2] - p1[2]);
  const startAltitude = [p1[2], b1[2]];

  return {
    rotation,
    scale,
    translate,
    startAltitude,
    altitude,
  };
};

const transform = (point: Pos, cover: Conversion): Pos => {
  const ctrla = cover.scale * Math.cos(cover.rotation);
  const ctrlb = cover.scale * Math.sin(cover.rotation);
  return [
    ctrla * point[0] - ctrlb * point[1] + cover.translate[0],
    ctrlb * point[0] + ctrla * point[1] + cover.translate[1],
    cover.altitude * (point[2] - cover.startAltitude[0]) +
      cover.startAltitude[1],
  ];
};

const wCoordType = "EPSG:4326";
const tCoordType = "EPSG:3857";

proj4.defs(wCoordType, "+proj=longlat +ellps=GRS80 +no_defs");
proj4.defs(
  tCoordType,
  "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs"
);

// 传入本地坐标与控制点的关系
export const conversionFactory = (locals: [Pos, Pos], gpss: [Pos, Pos]) => {
  const maps = gpss.map((pos) => proj4(wCoordType, tCoordType, pos));
  const coverLocalToWeb = getConversion2D(locals, maps);
  const coverWebToLocal = getConversion2D(maps, locals);

  return {
    toLocal(pos: Pos): Pos {
      const tPos = transform(
        proj4(wCoordType, tCoordType, [...pos]),
        coverWebToLocal
      );
      return tPos;
    },
    toWGS84(pos: Pos): Pos {
      const tPos = transform(pos, coverLocalToWeb);
      const wPos = proj4(tCoordType, wCoordType, [...tPos]);

      return [wPos[0], wPos[1], tPos[2]];
    },
  };
};
