import { v4 as uuidv4 } from 'uuid'
import moment from 'moment'
import _ from 'lodash'
import cameras from './constants/cameras'
import {getType, distance as calcDistance, midpoint as getMidpoint} from '@turf/turf'
import { layerGroups } from './constants'
import { mat4 } from 'gl-matrix'
import {transformCoordinateToLngLat, project} from './transform'
import * as turf from '@turf/turf'

/**
 * 生成uuid随机字符串
 * @returns uuid字符串
 */
export function generateId () { // 生成随机id
  return uuidv4()
}

/**
 * 将给定的日期格式化
 * @param {*} date 
 * @returns 
 */
export function formatDate (date) {
  if (!date) {
    return '-'
  }
  return moment(date).format('YYYY-MM-DD HH:mm:ss')
}

/**
 * 另一种日期格式化
 * @returns 
 */
export function formatLocalDate () {
  return moment().format('YYYY/M/D HH:mm:ss')
}

/**
 * 将文件大小格式化，自动换算kb\mb\gb
 * @param {*} value 文件大小，单位“字节”
 * @returns 
 */
export function formatFileSize (value) {
  value = parseInt(value)
  let level = Math.floor(Math.log(value) / Math.log(1024))  // 整数部分
  let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
  return _.round(value / Math.pow(1024, level), 2) + units[level] 
}

/**
 * 根据exif中的经纬度标志判断正负号
 * @param {*} flag 
 * @returns 
 */
export function getGeoCoordOperator (flag) { // 将照片的中的经纬度方位字母换成正负号
  if (flag === 'N' || flag === 'E') {
    return 1
  }
  return -1
}

/**
 *  度分秒转弧度
 * @param {*} arr 度分秒数组
 * @returns 
 */
export function toRadian (arr) {
  return arr[0] + (arr[1] / 60) + (arr[2] / 3600)
}

/**
 * 任务详情中的任务成果树进行差异计算，获取图层更新的操作
 * @param {*} newList 
 * @param {*} oldList 
 * @returns 
 */
export function diffLayers (newList, oldList = []) {
  newList = _formatTree(newList)
  oldList = _formatTree(oldList)
  let cmds = []
  for(let group of newList) {
    let {visible: groupVisible} = group
    for(let layer of group.items || []) {
      let {id, type, opacity, visible} = layer
      if (!groupVisible) {
        visible = false
      }
      if (['point', 'line', 'polygon'].includes(type) && cmds.find(d => d[0] === 'updateAnnotations')) {
        continue
      }
      let oldNode = findNodeInTree(id, oldList)
      if (!oldNode && !visible) {
        continue
      }
      if (!oldNode) {
        if (['point', 'line', 'polygon'].includes(type)) {
          cmds.push(['updateAnnotations', layer])
        } else {
          cmds.push(['addLayer', layer])
        }
        continue
      }
      let {opacity: oldOpacity, visible: oldVisible} = oldNode
      if (_.isEqual(layer, oldNode)) {
        continue
      }
      // 有属性更新
      if (['point', 'line', 'polygon'].includes(type)) {
        cmds.push(['updateAnnotations', layer])
      } else if (opacity !== oldOpacity || (!_.isEqual(layer.metadata, oldNode.metadata))) {
        cmds.push(['updateLayer', layer])
      } else if (visible !== oldVisible){
        cmds.push([visible ? 'addLayer' : 'removeLayer', layer])
      }
    }
  }
  for(let group of oldList) {
    for(let layer of group.items || []) {
      let {id, type} = layer
      let newNode = findNodeInTree(id, newList)
      if (!newNode) {
        if (!['point', 'line', 'polygon'].includes(type)) {
          cmds.push(['removeLayer', layer])
        } else if (!cmds.find(d => d[0] === 'updateAnnotations')){
          cmds.push(['updateAnnotations', layer])
        }
      }
    }
  }
  return cmds
}

/**
 * 从图层树结构中查找指定id的节点
 * @param {*} id 需要查找的节点id
 * @param {*} tree 图层树
 * @returns 
 */
export function findNodeInTree (id, tree) { // 从图层树中查找指定
  for(let node of tree) {
    if (node.id === id) {
      return node
    }
    if (node.items?.length) {
      let subNode = findNodeInTree(id, node.items)
      if (subNode) {
        return subNode
      }
    }
  }
  return null
}
function _formatTree (treeList) {
  let tree = _.cloneDeep(treeList)
  for(let group of tree) {
    for(let layer of group.items || []) {
      if (!group.visible) {
        layer.visible = false
      }
    }
  }
  return tree
}

/**
 * 在mapbox中获取即将插入图层的位置，根据图层metadata中的图层类型来确定图层顺序
 * @param {*} map 当前地图对象
 * @param {*} layer 即将插入的图层
 * @returns 
 */
export function getBeforeId (map,layer){
  let layersForFilter = map.style._layers
  let orders = map.style.order
  let {layerType:insertType} = layer.metadata
  // 获取到level比当前大的
  // 如果同级，则添加到最后
  let beforeLayer = orders.find(id=>{
    let targetData = layersForFilter[id].metadata
    if(_.isEmpty(targetData)) {
      targetData = {
        layerType: 'dsm'
      }
    }
    return getLevelByLayerType(insertType,layer.id) < getLevelByLayerType(targetData.layerType,id) ? true : false
  })
  return beforeLayer || ''

  function getLevelByLayerType (type,id){
    // 此数组维护一个图层类型的先后关系
    // 越在前，则代表此图层层级越低
    const layerTypeLevel = [
      ['bmap'],
      ['dom'],
      ['ndvi'],
      ['dsm'],
      ['circle'],
      ['annotation'],
      ['label']
    ]
    let level = -1
    if (id.startsWith('gl-draw')) {
      level = 5
    } else {
      level = layerTypeLevel.findIndex(types=>{
        return types.includes(type)
      })
    }
    
    if(level === -1){
      //console.error(`id为${id}的图层层级未知`)
      return 100
    }
    return level
  }
}

/**
 * 判断当前响应是否为token过期
 * @param {*} response 
 * @returns 
 */
export function isTokenExpired (response) {
  if (!response) {
    return false
  }
  let {message = '', msg = ''} = response.data
  return response.status == 401 && (msg.toLowerCase().includes('expired') || message.toLowerCase().includes('expired'))
}

/**
 * 根据相机型号获取照片的类型
 * @param {*} model 相机型号
 * @returns 
 */
export function getDataType (model) {
  for(let type in cameras) {
    if (cameras[type].includes(model)) {
      return type
    }
  }
  return 'normal'
}

/**
 * 下载base64格式的图像快照
 * @param {*} base64 
 */
export function downloadImage (base64) {
  let filename = `快照${moment().format('YYYYMMDD')}${new Date().getTime()}.png`
  let a = document.createElement('a')
  a.href = base64
  a.download = filename
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
}

/**
 * 获取geojon要素的几何类型
 * @param {*} feature 
 * @returns point/line/polygon
 */
export function getGeometryType (feature) {
  let type = getType(feature)
  if (['Point', 'MultiPoint'].includes(type)) {
    return 'point'
  } else if (['LineString', 'MultiLineString'].includes(type)) {
    return 'line'
  } else {
    return 'polygon'
  }
}

/**
 * 将png图片中一个像素（四字节的数组）转为float32浮点数
 * @param {*} v 
 * @returns 
 */
export function rgbaToFloat (v) {
  let a = v[3] > 128.0 ? 0 : 1
  let sign = -1 * (1 - a) + a
  let expo = Math.floor((v[3] + 0.1) % 128.0) * 2.0 + Math.floor((v[2] + 0.1) / 128.0) - 127.0
  let sig = v[0] + v[1] * 256.0 + Math.floor((v[2] + 0.1) % 128.0) * 256.0 * 256.0
  return sign * (1.0 + sig / 8388607.0) * Math.pow(2.0, expo)
}

/**
 * 将标注的数据库对象转为GeoJSON对象
 * @param {*} annotationItem 
 * @returns 
 */
export function getAnnotationFeature (annotationItem) {
  let {geometry} = annotationItem
  let properties = _.cloneDeep(annotationItem)
  delete properties.geometry
  return {
    id: properties.id,
    type: 'Feature',
    properties,
    geometry: _.cloneDeep(geometry)
  }
}

/**
 * 根据已有的标注列表计算一个默认的标注名称，确保名称唯一
 * @param {*} annotations 当前已有的标注列表
 * @param {*} type 标注类型
 * @returns 
 */
export function getAnnotationName (annotations, type) {
  let indexs = []
  let name = type === 'point' ? '点注记' : (type === 'line' ? '线注记' : '面注记')
  annotations.forEach(item => {
    if (item.name === name) {
      indexs.push(0)
      return
    }
    if (item.name.startsWith(`${name} `)) {
      let i = Number(item.name.split(' ').pop())
      if (!Number.isNaN(i)) {
        indexs.push(i)
      }
    }
  })
  indexs = indexs.sort((a, b) => a - b)
  for(let i = 0;i < indexs.length;i++) {
    if (indexs[i] !== i) {
      return i === 0 ? name : `${name} ${i}`
    }
  }
  return indexs.length === 0 ? name : `${name} ${indexs.length}`
}

/**
 * 计算线要素标注的文字注记绘制的位置和方向
 * @param {*} lineFeature 
 * @returns 返回一个GeoJSON点要素，其集合属性中是文字标注的位置，要素属性中是标注的方向
 */
export function getLineLabelFeature (lineFeature) {
  let coordinates = lineFeature.geometry.coordinates
  let temps = []
  for(let i = 1;i < coordinates.length;i++) {
    let distance = calcDistance(coordinates[i - 1], coordinates[i], {units: 'meters'})
    if (distance > 30) {
      temps.push([coordinates[i - 1], coordinates[i]])
    }
  }
  let labelLine = []
  if (!temps.length) {
    let mid = Math.floor(coordinates.length / 2 - 1)
    labelLine = [coordinates[mid], coordinates[mid + 1]]
  } else {
    let mid = Math.floor(temps.length / 2)
    labelLine = temps[mid]
  }
  let start = project(labelLine[0])
  let end = project(labelLine[1])    
  let angle = 360 - (Math.atan((start.y - end.y) / (end.x - start.x)) * 180) / Math.PI
  let midPoint = getMidpoint(labelLine[0], labelLine[1])
  midPoint.properties = lineFeature.properties
  midPoint.properties.angle = angle
  midPoint.geometry.coordinates.push(((labelLine[0][2] || 0) + (labelLine[1][2] || 0)) / 2)
  return midPoint
}

/**
 * 将图标文件当作mapbox地图的符号添加到地图对象
 * @param {*} map 当前地图对象
 * @param {*} icons 要添加的图标列表，包含图标的url以及是否作为sdf符号
 */
export function addIcons2Map (map, icons) {
  return new Promise((resolve, reject) => {
    let cnt = icons.length
    icons.forEach(({url, sdf}) => {
      map.loadImage(url, (error, image) => {
        cnt--
        let iconName = url.split('/').pop().split('.')[0]
        if(map.hasImage(iconName)) return
        if (error) {
          reject(error)
          return
        }
        map.addImage(iconName, image, {sdf})
        if (cnt <= 0) resolve(true)
      })
    })
  })
}

/**
 * 构造任务成果树的结构
 * @param {*} taskInfo 
 * @param {*} annotations 
 * @param {*} layers 
 * @returns 
 */
export function getTaskTreeList (taskInfo, annotations, layers) {
  let groups = _.cloneDeep(taskInfo.metadata?.groups || layerGroups)
  groups.forEach(g => g.items = [])
  annotations.forEach(item => {
    let info = _.cloneDeep(item)
    if (!info.group) {
      info.group === 'annotation'
    }
    let groupItem = groups.find(d => d.id === info.group)
    groupItem.items.push(info)
  })
  let lyrs = taskInfo.status === 'complete' ? layers : []
  lyrs.forEach(d => {
    let {group} = d
    let groupItem = groups.find(d => d.id === group)
    if (!groupItem) {
      return
    }
    groupItem.items.push(d)
  })
  return groups
}

/**
 * 判断当前数组中是否包含非数值要素
 * @param {*} arr 
 * @returns 
 */
export function hasNaN (arr) {
  for (let i = 0; i < arr.length; i++) {
    if (Number.isNaN(arr[i])) {
      return true
    }
  }
  return false
}

export function flatObject (obj) {
  let arr = {}
  for(let key in obj) {
    if (key.startsWith('xmlns')) {
      continue
    }
    if (Array.isArray(obj[key])) {
      continue
    }
    if (typeof obj[key] === 'object') {
      Object.assign(arr, flatObject(obj[key]))
    } else {
      arr[key] = obj[key]
    }
  }
  return arr
}

/**
 * 根据pry参数计算旋转矩阵
 * @param {*} pitch 
 * @param {*} roll 
 * @param {*} yaw 
 * @returns 
 */
export function getRotationMatrix (pitch, roll, yaw) {
  let orientation = []
  let enu2ned = mat4.fromValues(0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)
  let ned2cam = mat4.fromValues(0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1)
  let m1 = mat4.fromZRotation(mat4.create(), yaw * Math.PI / 180)
  let m2 = mat4.fromYRotation(mat4.create(), pitch * Math.PI / 180)
  let m3 = mat4.fromXRotation(mat4.create(), roll * Math.PI / 180)
  let rota = mat4.multiply(mat4.create(), mat4.multiply(mat4.create(), m1, m2), m3)
  let rot = mat4.multiply(mat4.create(), mat4.multiply(mat4.create(), ned2cam, mat4.transpose(mat4.create(), rota)), enu2ned)
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      orientation[i * 3 + j] = rot[i + j * 4]
    }
  }
  return orientation
}

/**
 * 将LocalENU坐标系的ROI转为经纬度
 * @param {*} roi 带转换的roi结构
 * @param {*} crs 目标坐标系，当前仅支持LocalENU
 * @returns 
 */
export function transformRoi (roi, crs) {
  let {boundary, min_z, max_z} = roi
  let {type, origin_point} = crs
  if (type === 0) { // LocalENU
    let newBoundary = []
    boundary.forEach(coord => {
      let lla = transformCoordinateToLngLat([...coord, 0], crs)
      newBoundary.push(lla)
    })
    min_z += origin_point[2]
    max_z += origin_point[2]
    return {boundary: newBoundary, min_z, max_z}
  }
  return roi // Local系，不做转换
}

/**
 * 根据当前pos精度描述获得精度的具体数值
 * @param {*} accurency 
 * @returns 
 */
export function getPosSigma (accurency) {
  if (accurency === 'high') {
    return [0.03, 0.03, 0.06]
  } else if (accurency === 'low') {
    return [10, 10, 10]
  } else {
    return [1, 1, 1]
  }
}

/**
 * 根据照片列表中解析的pos，将照片列表转为GeoJSON
 * @param {*} photoList 
 * @returns 
 */
export function photoPos2Geojson (photoList) {
  let features = []
  let item = photoList.find(d => d.meta_data.pos)
  if (item) {
    let coordinate_system = _.cloneDeep(item.meta_data.coordinate_system)
    photoList.forEach(photo => {
      let {meta_data = {}} = photo
      let {pos} = meta_data
      let position = transformCoordinateToLngLat(pos, coordinate_system)
      if (!position) {
        return
      }
      let properties = _.cloneDeep(photo)
      properties.type = 'photo'
      features.push(turf.point(position, properties))
    })
  }
  
  let geojson = turf.featureCollection(features)
  let bbox = null
  if (features.length) {
    bbox = turf.bbox(geojson)
  }
  return {geojson, bbox}
}

export function getDistance (p1, p2) {
  return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]))
}

export function getMaxValue (arr, field) {
  let value = -Infinity
  arr.forEach(item => {
    value = Math.max(item[field], value)
  })
  return value
}

export function getAverageValue (arr, field) {
  let sum = 0
  arr.forEach(item => {
    sum += item[field]
  })
  return sum / arr.length
}

export function getAppVersion (taskInfo) {
  let {appVersion, environment} = taskInfo.metadata
  if (appVersion) {
    return appVersion
  }
  if (environment) {
    return `${environment.platform}(${environment.appVersion})`
  }
  return '暂无'
}

/**
 * 将重建质量的数值转为名称
 * @param {*} quality 重建质量-1/2/3
 * @returns 
 */
export function getResolutionLevelByQuality (quality){
  switch (quality) {
    case 'low':
      return 3
    case 'high':
      return 1
    case 'medium':
    default:
      return 2
  }
}

export function getQualityAliasByLevel (resolutionLevel) {
  let alias = '高'
  if (resolutionLevel === 1) {
    alias = '超高'
  } else if (resolutionLevel === 3) {
    alias = '中'
  }
  return alias
}

/**
 * 将数据类型的数值转字符串
 * @param {*} dataType 
 * @returns 
 */
export function getImageTypeByDataType (dataType){
  switch (dataType) {
    case 'normal':
      return 1
    case 'multispectral':
      return 2
    case 'infrared':
    default:
      return 3
  }
}

export function getDataTypeAliasByImageType (type) {
  if (type === 'infrared') {
    return '热红外'
  } else if (type === 'multispectral') {
    return '多光谱'
  } else {
    return '可见光'
  }
}

export function getTaskStatusAlias (status) {
  if (status === 'complete') {
    return '建模完成'
  } else if (status === 'error') {
    return '建模出错'
  } else if (status === 'none') {
    return '未开始'
  } else if (status === 'waiting') {
    return '正在排队'
  } else if (status === 'aerotriangulation') {
    return '空三完成'
  } else if (status.startsWith('processing')){
    return '正在处理'
  } else if (status === 'stop'){
    return '任务终止'
  } else {
    return '未知状态'
  }
}

/**
 * 根据重建参数的输出属性判断是否开启了3d建模
 */
export function include3d (output = []) {
  if (output.length && output.includes('mesh3d')) {
    return true
  }
  return false
}

export function extend (dest, ...sources) {
  for (const src of sources) {
    for (const k in src) {
      dest[k] = src[k]
    }
  }
  return dest
}
export function endsWith (string, suffix) {
  return string.indexOf(suffix, string.length - suffix.length) !== -1
}

