// 坐标转换类
import satellite_geo_calc from 'satellite-geo-calc'
import proj4 from 'proj4'
import { crsList, crsTypes } from './constants'

/**
 * 根据坐标系参数获取其WKT字符串
 * @param {*} crs 
 * @returns 
 */
export function getWKTString (crs) {
  let {type, epsg_code} = crs
  for(let i = 0;i < crsList.length;i++) {
    if (!crsTypes[crsList[i].type]) {
      continue
    }
    let typeCode = crsTypes[crsList[i].type].code
    if (typeCode !== type) {
      continue
    }
    let {authCode, wkt, children} = crsList[i]
    if (authCode == epsg_code) {
      return wkt
    }
    if (children?.length) {
      for(let j = 0;j < children.length;j++) {
        if (children[j].children) {
          for(let n = 0;n < children[j].children.length;n++) {
            if (children[j].children[n].authCode == epsg_code) {
              return children[j].children[n].wkt
            }
          }
        } else {
          if (children[j].authCode == epsg_code) {
            return children[j].wkt
          }
        }
      }
    }
  }
  return null
}

/**
 * 将经纬度转为指定级别的像素坐标，瓦片大小按照512计算
 * @param {*} lngLat 
 * @param {*} zoom 
 * @returns 
 */
export function project (lngLat, zoom = 12) { // 经纬度转墨卡托像素坐标
  let [lng, lat] = lngLat
  let x = (180 + lng) / 360
  let y = (180 - (180 / Math.PI) * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360))) / 360
  return {
    x: x * Math.pow(2, zoom) * 512,
    y: y * Math.pow(2, zoom) * 512
  }
}

/**
 * 经纬度转为指定级别的瓦片坐标
 * @param {*} zoom 
 * @param {*} lngLat 
 * @returns 
 */
export function getTileCoord (zoom, lngLat) {
  let [lng, lat] = lngLat
  let x = (180 + lng) / 360
  let y = (180 - (180 / Math.PI) * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360))) / 360
  return {
    x: Math.floor(x * Math.pow(2, zoom)),
    y: Math.floor(y * Math.pow(2, zoom)),
    z: zoom
  }
}

/**
 * 经纬度转为指定级别的瓦片上的像素坐标
 * @param {*} zoom 
 * @param {*} lngLat 
 * @returns 返回的坐标是在瓦片坐标系的坐标
 */
export function getTilePixelCoord (zoom, lngLat) {
  let [lng, lat] = lngLat
  let x = (180 + lng) / 360
  let y = (180 - (180 / Math.PI) * Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360))) / 360
  return {
    x: Math.round(x * Math.pow(2, zoom) * 512) % 512,
    y: Math.round(y * Math.pow(2, zoom) * 512) % 512
  }
}

// 根据传入的经度，计算所属UTM投影带
export function getUTMProjection (lng) {
  let initCode = 32600
  let n = Math.ceil((lng + 180) / 6)
  return {
    type: 3,
    type_name: 'Projected',
    label: `WGS 84 / UTM zone ${n}N`,
    epsg_code: initCode + n
  }
}

/**
 * 通用的坐标转换函数，将一个坐标由源坐标系转到目标坐标系
 * @param {Array} coordinate 带转换的坐标
 * @param {*} sourceCrs 原坐标系
 * @param {*} targetCrs 目标坐标系
 * @returns Array | null
 */
export function transformCoordinate (coordinate, sourceCrs, targetCrs) {
  if (!coordinate) {
    return null
  }
  coordinate = [...coordinate]
  if (!coordinate[2]) {
    coordinate[2] = 0
  }
  if (!sourceCrs || !targetCrs) {
    return coordinate
  }
  if (targetCrs.type === 1 || sourceCrs.type === 1) { // any => Local 或者 Local => any，无法转换
    return coordinate
  } else if (sourceCrs.type === 0 && targetCrs.type === 0) { // LocalEnu => LocalEnu
    let p = satellite_geo_calc.coordinateTransfer.lla_to_enu({ lon:sourceCrs.origin_point[0], lat:sourceCrs.origin_point[1], alt: sourceCrs.origin_point[2]}, {lon: targetCrs.origin_point[0], lat: targetCrs.origin_point[1], alt: targetCrs.origin_point[2]})
    return [p.x + coordinate[0], p.y + coordinate[1], p.z + coordinate[2]]
  } else if (sourceCrs.type === 0) { // LocalEnu => geo
    let lla = satellite_geo_calc.coordinateTransfer.enu_to_lla({x: coordinate[0], y: coordinate[1], z: coordinate[2]}, { lon: sourceCrs.origin_point[0], lat: sourceCrs.origin_point[1], alt: sourceCrs.origin_point[2] })
    let coord = [lla.lon, lla.lat, lla.alt]
    let {wkt, epsg_code} = targetCrs
    if (epsg_code === 4326 || epsg_code === 4490) {
      return coord
    }
    if (!wkt && epsg_code) {
      wkt = getWKTString(targetCrs)
    }
    if (!wkt) {
      return null
    }
    return proj4('EPSG:4326', wkt, coord)
  } else if (targetCrs.type === 0) { // geo => LocalEnu
    let {wkt, epsg_code} = sourceCrs
    let lla = coordinate
    if (epsg_code !== 4326) {
      if (!wkt && epsg_code) {
        wkt = getWKTString(sourceCrs)
      }
      if (!wkt) {
        return null
      }
      lla = proj4(wkt, 'EPSG:4326', coordinate)
    }
    let p = satellite_geo_calc.coordinateTransfer.lla_to_enu({ lon:lla[0], lat:lla[1], alt: lla[2]}, {lon: targetCrs.origin_point[0], lat: targetCrs.origin_point[1], alt: targetCrs.origin_point[2]})
    return [p.x, p.y, p.z]
  } else { // geo => geo
    let sourceWkt = sourceCrs.wkt
    let targetWkt = targetCrs.wkt
    if (!sourceWkt && sourceCrs.epsg_code) {
      sourceWkt = getWKTString(sourceCrs)
    }
    if (!targetWkt && targetCrs.epsg_code) {
      targetWkt = getWKTString(targetCrs)
    }
    if (!sourceWkt || !targetWkt) {
      return null
    }
    return proj4(sourceWkt, targetWkt, coordinate)
  }
}

/**
 * 将指定坐标系下的某个坐标转到WGS84经纬度
 * @param {*} lngLat 
 * @param {*} sourceCrs 
 * @returns 
 */
export function transformCoordinateToLngLat (lngLat, sourceCrs) {
  return transformCoordinate(lngLat, sourceCrs, {
    type: 2,
    type_name: 'Geographic',
    epsg_code: 4326
  })
}

/**
 * 将经纬度转为其他坐标系
 */
export function transformLnglat (lngLat, coordinate_system) {
  return transformCoordinate(lngLat, {
    type: 2,
    type_name: 'Geographic',
    epsg_code: 4326
  }, coordinate_system)
}

/**
 * 将经纬度转为笛卡尔地心坐标系
 * @param {*} coord 
 * @returns 
 */
export function geographicToCartesian (coord) {
  let p1 = '+proj=longlat +datum=WGS84'
  let p2 = '+proj=geocent +datum=WGS84'
  return proj4(p1, p2, coord)
}

