#define TILE_SIZE (512.0)
#define PI (3.1415926536)
#define WORLD_SCALE (TILE_SIZE / (PI * 2.0))
#define EARTH_CIRCUMFERENCE (40.03e6)

#define COORDINATE_SYSTEM_LNGLAT (1.0) // mapbox
#define COORDINATE_SYSTEM_LNGLAT_OFFSET (2.0) // mapbox offset
#define COORDINATE_SYSTEM_VECTOR_TILE (3.0)
#define COORDINATE_SYSTEM_IDENTITY (4.0)
#define COORDINATE_SYSTEM_METER_OFFSET (5.0)

#pragma include "maplet_uniforms"

const vec2 ZERO_64_XY_LOW = vec2(0.0, 0.0);

// web mercator coords -> world coords
vec2 project_mercator(vec2 lnglat) {
  float x = lnglat.x;
  return vec2(radians(x) + PI, PI - log(tan(PI * 0.25 + radians(lnglat.y) * 0.5)));
}

float project_scale(float meters) {
  return meters * u_PixelsPerMeter.z;
}

// offset coords -> world coords
vec4 project_offset(vec4 offset) {
  float dy = offset.y;
  dy = clamp(dy, -1.0, 1.0);
  vec3 pixels_per_unit = u_PixelsPerDegree + u_PixelsPerDegree2 * dy;
  return vec4(offset.xyz * pixels_per_unit, offset.w);
}

vec3 project_normal(vec3 normal) {
  vec4 normal_modelspace = u_ModelMatrix * vec4(normal, 0.0);
  return normalize(normal_modelspace.xyz * u_PixelsPerMeter);
}

vec3 project_offset_normal(vec3 vector) {
  if (
    u_CoordinateSystem < COORDINATE_SYSTEM_LNGLAT + 0.01 &&
      u_CoordinateSystem > COORDINATE_SYSTEM_LNGLAT - 0.01 ||
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET
  ) {
    // normals generated by the polygon tesselator are in lnglat offsets instead of meters
    return normalize(vector * u_PixelsPerDegree);
  }
  return project_normal(vector);
}

vec4 project_position(vec4 position, vec2 position64xyLow) {
  if (u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET) {
    float X = position.x - u_ViewportCenter.x;
    float Y = position.y - u_ViewportCenter.y;
    return project_offset(
      vec4(X + position64xyLow.x, Y + position64xyLow.y, position.z, position.w)
    );
  }
  if (
    u_CoordinateSystem < COORDINATE_SYSTEM_LNGLAT + 0.01 &&
    u_CoordinateSystem > COORDINATE_SYSTEM_LNGLAT - 0.01
  ) {
    return vec4(
      project_mercator(position.xy) * WORLD_SCALE * u_ZoomScale,
      project_scale(position.z),
      position.w
    );
  }

  return position;
}

vec4 project_position(vec4 position) {
  return project_position(position, ZERO_64_XY_LOW);
}

vec2 project_pixel_size_to_clipspace(vec2 pixels) {
  vec2 offset = pixels / u_ViewportSize * u_DevicePixelRatio * 2.0;
  return offset * u_FocalDistance;
}

// 适配纹理贴图的等像素大小
float project_pixel_texture(float pixel) {
  // mapbox zoom > 12
  if (u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET) {
    return pixel * pow(0.5, u_Zoom) * u_FocalDistance;
  }

  return pixel * 2.0 * u_FocalDistance;
}

// 在不论什么底图下需要统一处理的时候使用
float project_float_pixel(float pixel) {
  if (
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET
  ) {
    // mapbox 坐标系下，为了和 Web 墨卡托坐标系统一，zoom 默认减1
    return pixel * pow(2.0, 19.0 - u_Zoom) * u_FocalDistance;
  }

  return pixel * u_FocalDistance;
}

// Project meter into the unit of pixel which used in the camera world space
float project_float_meter(float meter) {
  if (
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET
  ) {
    // Since the zoom level uniform is updated by mapservice and it's alread been subtracted by 1
    // Not sure if we are supposed to do that again
    return meter;
  } else {
    return project_float_pixel(meter);
  }

  // TODO: change the following code to make adaptations for amap
  // return u_FocalDistance * TILE_SIZE * pow(2.0, u_Zoom) * meter / EARTH_CIRCUMFERENCE;

}

float project_pixel(float pixel) {
  return pixel * u_FocalDistance;
}

vec2 project_pixel(vec2 pixel) {
  return pixel * -1.0 * u_FocalDistance;
}

vec3 project_pixel(vec3 pixel) {
  return pixel * -1.0 * u_FocalDistance;
}

vec4 project_common_position_to_clipspace(vec4 position, mat4 viewProjectionMatrix, vec4 center) {
  if (
    u_CoordinateSystem == COORDINATE_SYSTEM_METER_OFFSET ||
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET
  ) {
    // Needs to be divided with project_uCommonUnitsPerMeter
    position.w *= u_PixelsPerMeter.z;
  }

  return viewProjectionMatrix * position + center;
}

// Projects from common space coordinates to clip space
vec4 project_common_position_to_clipspace(vec4 position) {
  return project_common_position_to_clipspace(
    position,
    u_ViewProjectionMatrix,
    u_ViewportCenterProjection
  );
}

vec4 unproject_clipspace_to_position(vec4 clipspacePos, mat4 u_InverseViewProjectionMatrix) {
  vec4 pos = u_InverseViewProjectionMatrix * (clipspacePos - u_ViewportCenterProjection);

  if (
    u_CoordinateSystem == COORDINATE_SYSTEM_METER_OFFSET ||
    u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET
  ) {
    // Needs to be divided with project_uCommonUnitsPerMeter
    pos.w = pos.w / u_PixelsPerMeter.z;
  }
  return pos;
}

bool isEqual(float a, float b) {
  return a < b + 0.001 && a > b - 0.001;
}

