import { OutStatistic } from '../base'
import { VideoSourceType } from '../base/enum'
/**
 * @description 将目标对象的值给元对象
 * @param {Object} destination 源对象
 * @param {Object} source 目标对象
 * @returns {Object} 源对象
 */
function extend(destination, source) {
  destination = destination || {}
  if (source) {
    for (const property in source) {
      const value = source[property]
      if (value !== undefined) {
        destination[property] = value
      }
    }
  }
  return destination
}
/**
 * @description 判断一个对象是否是数组。
 * @param array - {Object} 对象。
 * @return {boolean} 是否是数组。
 */
function isArray(array) {
  return Object.prototype.toString.call(array) === '[object Array]'
}
/**
 * @description 判断一个 URL 请求是否在当前域中。
 * @param url - {string}  URL 请求字符串。
 * @return {boolean} URL请求是否在当前域中。
 */
function isInTheSameDomain(url) {
  if (!url) {
    return true
  }
  let index = url.indexOf('//')
  const documentUrl = document.location.toString()
  let documentIndex = documentUrl.indexOf('//')
  if (index === -1) {
    return true
  } else {
    const protocol = url.substring(0, index)
    let substring = protocol
    const documentSubString = documentUrl.substring(documentIndex + 2)
    documentIndex = documentSubString.indexOf('/')
    const documentPortIndex = documentSubString.indexOf(':')
    let documentDomainWithPort = documentSubString.substring(0, documentIndex)
    // let documentPort;
    const documentprotocol = document.location.protocol
    if (documentPortIndex !== -1) {
      // documentPort = +documentSubString.substring(documentPortIndex, documentIndex);
    } else {
      documentDomainWithPort += `:${
        documentprotocol.toLowerCase() === 'http:' ? 80 : 443
      }`
    }
    if (documentprotocol.toLowerCase() !== substring.toLowerCase()) {
      return false
    }
    substring = url.substring(index + 2)
    const portIndex = substring.indexOf(':')
    index = substring.indexOf('/')
    let domainWithPort = substring.substring(0, index)
    let domain
    if (portIndex !== -1) {
      domain = substring.substring(0, portIndex)
    } else {
      domain = substring.substring(0, index)
      domainWithPort += `:${protocol.toLowerCase() === 'http:' ? 80 : 443}`
    }
    const documentDomain = document.domain
    if (
      domain === documentDomain &&
      domainWithPort === documentDomainWithPort
    ) {
      return true
    }
  }
  return false
}
/**
 * @description 转换查询结果。
 * @param result - {Object} 查询结果。
 * @return {Object} 转换后的查询结果。
 */
function transformResult(result) {
  if (result.responseText && typeof result.responseText === 'string') {
    result = JSON.parse(result.responseText)
  }
  return result
}
/**
 * @description 给url追加参数。
 * @param url - {string} 待追加参数的url字符串。
 * @param paramStr - {string} 待追加的参数。
 * @return {string} The new url
 */
function urlAppend(url, paramStr) {
  let newUrl = url
  if (paramStr) {
    const parts = `${url} `.split(/[?&]/)
    newUrl +=
      // eslint-disable-next-line no-nested-ternary
      parts.pop() === ' '
        ? paramStr
        : parts.length
        ? `&${paramStr}`
        : `?${paramStr}`
  }
  return newUrl
}
/**
 * @param params - {Object} 参数对象。
 * @return {string} HTTP的GEI请求中的参数字符串。
 * @description 将参数对象转换为HTTP的GEI请求中的参数字符串。例如:"key1=value1&key2=value2&key3=value3"。
 */
function getParameterString(params) {
  const paramsArray = []
  for (const key in params) {
    const value = params[key]
    if (value !== null && typeof value !== 'function') {
      let encodedValue
      if (typeof value === 'object' && value.constructor === Array) {
        /* value is an array; encode items and separate with "," */
        const encodedItemArray = []
        let item
        for (
          let itemIndex = 0, len = value.length;
          itemIndex < len;
          itemIndex++
        ) {
          item = value[itemIndex]
          encodedItemArray.push(
            encodeURIComponent(item === null || item === undefined ? '' : item)
          )
        }
        encodedValue = encodedItemArray.join(',')
      } else {
        /* value is a string; simply encode */
        encodedValue = encodeURIComponent(value)
      }
      paramsArray.push(`${encodeURIComponent(key)}=${encodedValue}`)
    }
  }
  return paramsArray.join('&')
}
function bind(func, object) {
  // create a reference to all arguments past the second one
  const args = Array.prototype.slice.apply(arguments, [2])
  return function () {
    // Push on any additional arguments from the actual function call.
    // These will come after those sent to the bind call.
    const newArgs = args.concat(Array.prototype.slice.apply(arguments, [0]))
    return func.apply(object, newArgs)
  }
}
/**
 * @function getGUID
 * @private
 * @description 生成GUID
 * @return String GUID
 */
function getGUID() {
  let guid = ''
  for (let i = 1; i <= 32; i++) {
    const n = Math.floor(Math.random() * 16.0).toString(16)
    guid += n
    if (i === 8 || i === 12 || i === 16 || i === 20) guid += '-'
  }
  return guid
}
function defaultValue(a, b) {
  if (a !== undefined && a !== null) {
    return a
  }
  return b
}
/**
 * @function
 * @param {*} value The object.
 * @returns {Boolean} Returns true if the object is defined, returns false otherwise.
 */
function defined(value) {
  return value !== undefined && value !== null
}
/**
 * @description 判断是否为数字
 * @param num
 * @return {boolean} 是否为数字。
 */
function isNumber(num) {
  return (
    !isNaN(num) &&
    num !== null &&
    !Array.isArray(num) &&
    typeof num !== 'object' &&
    typeof num !== 'string'
  )
}
/**
 * @description 判断是否为Boolean
 * @param value
 * @return {boolean} 是否为Boolean
 */
function isBoolean(value) {
  return typeof value === 'boolean'
}
/**
 * @description 判断是否为空
 * @param value 要判断的对象
 * @return {boolean} 是否为空
 */
function isNull(value) {
  return (
    value === undefined ||
    value === null ||
    value === '' ||
    (JSON.stringify(value) === '{}' && Object.keys(value).length === 0)
  )
}
/**
 * 判断是否为函数
 * @returns {boolean}
 */
function isFunction(item) {
  return typeof item === 'function'
}
/**
 * 获取几何查询条件
 * @private
 * @param geometry 几何对象
 * @return String 几何查询参数
 * */
function getGeometryParameter(geometry) {
  let url = ''
  if (geometry) {
    url += `&geometry=${geometry.toString()}&geometryType=${geometry.getIGSType()}`
  }
  return url
}
/**
 * 获取字段统计参数
 * @private
 * @param outStatistics 字段统计参数数组
 * @return String 字段统计参数
 * */
function getOutStatisticsParameter(outStatistics) {
  let url = ''
  // 必须师数组
  if (outStatistics instanceof Array && outStatistics.length > 0) {
    url += '&outStatistics=['
    for (let i = 0; i < outStatistics.length; i++) {
      let outStatistic
      // 如果不是OutStatistic类型,转为OutStatistic类型
      if (!(outStatistics[i] instanceof OutStatistic)) {
        // 获取outStatistics[i]里的内容
        const options = defaultValue(outStatistics[i], {})
        outStatistic = new OutStatistic({
          statisticType: options.statisticType,
          onStatisticField: options.onStatisticField,
          outStatisticFieldName: options.outStatisticFieldName
        })
      }
      // outStatistic存在
      if (outStatistic) {
        // 使用toString方法获取字符串
        url += outStatistic.toString()
      }
    }
    url += ']'
  }
  return url
}
/**
 * 是否是字符串
 * @private
 * @param value 字符串的值
 * */
function isString(value) {
  return typeof value === 'string'
}
/**
 * 深拷贝
 * @param value 被拷贝的对象
 * @return {Object} 深拷贝后的对象
 * */
function deepClone(value) {
  const success = value.success
  const failure = value.failure
  const newValue = JSON.parse(JSON.stringify(value))
  newValue.success = success
  newValue.failure = failure
  return newValue
}
function extendDeep(destination, source) {
  let i
  const toStr = Object.prototype.toString
  const astr = '[object Array]'
  destination = destination || {}
  for (i in source) {
    if (source.hasOwnProperty(i)) {
      if (typeof source[i] === 'object') {
        if (
          toStr.call(destination[i]) === '[object Null]' ||
          toStr.call(destination[i]) === '[object Undefined]'
        ) {
          destination[i] = toStr.call(source[i]) === astr ? [] : {}
        }
        extendDeep(destination[i], source[i])
      } else {
        destination[i] = source[i]
      }
      if (source[i] === '' || source[i] === null) {
        destination[i] = source[i]
      }
    }
  }
  return destination
}
function newGuid() {
  let guid = ''
  for (let i = 1; i <= 32; i++) {
    const n = Math.floor(Math.random() * 16.0).toString(16)
    guid += n
    if (i === 8 || i === 12 || i === 16 || i === 20) guid += '-'
  }
  return guid
}
function formatPoints(points) {
  for (let i = 0; i < points.length; i++) {
    if (points[i] instanceof Array) {
      formatPoints(points[i])
    } else if (points[i] instanceof Object) {
      points[i] = points[i].toArray()
    }
  }
  return points
}
function cloneObject(obj) {
  // Handle the 3 simple types, and null or undefined
  if (null === obj || 'object' !== typeof obj) {
    return obj
  }
  // Handle Date
  if (obj instanceof Date) {
    const copy = new Date()
    copy.setTime(obj.getTime())
    return copy
  }
  // Handle Array
  if (obj instanceof Array) {
    const copy = obj.slice(0)
    return copy
  }
  // Handle Object
  if (obj instanceof Object) {
    const copy = {}
    for (const attr in obj) {
      if (obj.hasOwnProperty(attr) && attr !== 'layer') {
        if (obj[attr] instanceof Array) {
          copy[attr] = JSON.parse(JSON.stringify(obj[attr]))
        } else {
          copy[attr] = cloneObject(obj[attr])
        }
      }
    }
    return copy
  }
  throw new Error("Unable to copy obj! Its type isn't supported.")
}
function returnPoint(constructor, geometry, point) {
  return new constructor({
    longitude: point[0],
    latitude: point[1],
    z: point[2],
    spatialReference: cloneObject(geometry.spatialReference)
  })
}
function notNULL(obj) {
  return obj !== '' && obj !== null && obj !== undefined
}
function isObject(obj) {
  return (
    obj instanceof Object && !(obj instanceof Array) && !(obj instanceof Array)
  )
}
/**
 * 获取视频源的类型
 * @private
 * @param {String} source 视频源
 * @returns {VideoSourceType | null}
 */
function getVideoType(source) {
  if (source instanceof HTMLVideoElement) {
    return VideoSourceType.videoHTML
  }
  if (typeof source === 'object') {
    return VideoSourceType.videoPlayer
  }
  if (typeof source !== 'string') {
    return null
  }
  const words = source.split(':')
  if (words[0] === 'http' || words[0] === 'https') {
    const suffix = source.split('.').pop()
    if (suffix === VideoSourceType.hls) {
      return VideoSourceType.hls
    } else if (suffix === VideoSourceType.mp4) {
      return VideoSourceType.mp4
    }
  } else if (words[0] === VideoSourceType.rtmp) {
    return VideoSourceType.rtmp
  }
  return null
}
/**
 * 获取url基地址
 * @private
 * @param {String} url url链接
 * @returns {String} 基地址
 */
function getBaseUrl(url) {
  const a = document.createElement('a')
  a.setAttribute('href', url)
  const { hostname, port, protocol } = a
  const origin = `${protocol}//${hostname}${port.length ? `:${port}` : ''}`
  return origin
}
/**
 * @description: 添加css样式
 * @param {*} cssText
 * @return {*}
 */
function inputCss(cssText) {
  if (window.document) {
    const css = window.document.createElement('style')
    css.type = 'text/css'
    css.innerHTML = cssText
    window.document.body.appendChild(css)
  }
}
/**
 * 导出json的通用工具函数
 * @param {Object} value 要导出的对象
 * @param {Object} construct 构造函数
 * @return {Object} json数据
 * */
function toJSON(value, construct) {
  return value instanceof construct ? value.toJSON() : value
}
/**
 * 通过JSON的方式克隆对象
 * @param {Object} json 要克隆的数据
 * @return {Object} 克隆后的数据
 * */
function jsonClone(json) {
  json = defaultValue(json, {})
  return JSON.parse(JSON.stringify(json))
}
export {
  extend,
  isArray,
  isInTheSameDomain,
  transformResult,
  urlAppend,
  getParameterString,
  bind,
  getGUID,
  defaultValue,
  defined,
  isNumber,
  isNull,
  getGeometryParameter,
  getOutStatisticsParameter,
  isString,
  deepClone,
  isBoolean,
  extendDeep,
  newGuid,
  formatPoints,
  returnPoint,
  cloneObject,
  notNULL,
  isObject,
  getVideoType,
  isFunction,
  getBaseUrl,
  inputCss,
  toJSON,
  jsonClone
}