    import { OutStatistic } from '../base'
    import { VideoSourceType } from '../base/enum'
    import Log from './Log'

    /**
     * @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
    }

    /**
     * 导出一个JSON对像
     * @private
     * @param {Object} self 要获取json的对象
     * @return {Object} JSON对像
     */
    function toJSON(self) {
      const _json = {}
      Object.keys(self).forEach(function (key) {
        if (
          self.hasOwnProperty(key) &&
          !(self[key] instanceof Function) &&
          key.indexOf('_') < 0
        ) {
          _json[key] = self[key]
        }
      })
      return _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,
      toJSON
    }