import {SuperMap} from '../SuperMap'
import './BaseTypes'

export var Util = SuperMap.Util = SuperMap.Util || {}
/**
 * @name Util
 * @memberOf SuperMap
 * @namespace
 * @description common工具类。
 */

/**
 * @description 复制源对象的所有属性到目标对象上，源对象上的没有定义的属性在目标对象上也不会被设置。
 * @example
 * 要复制SuperMap.Size对象的所有属性到自定义对象上，使用方法如下:
 *     var size = new SuperMap.Size(100, 100)
 *     var obj = {}；
 *     SuperMap.Util.extend(obj, size)
 * @param destination - {Object} 目标对象。
 * @param source - {Object} 源对象，其属性将被设置到目标对象上。
 * @return {Object} 目标对象。
 */

SuperMap.Util.extend = function (destination, source) {
  destination = destination || {}
  if (source) {
    for (var property in source) {
      var value = source[property]
      if (value !== undefined) {
        destination[property] = value
      }
    }

        /**
         * IE doesn't include the toString property when iterating over an object's
         * properties with the for(property in object) syntax.  Explicitly check if
         * the source has its own toString property.
         */

        /*
         * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
         * prototype object" when calling hawOwnProperty if the source object
         * is an instance of window.Event.
         */

    var sourceIsEvt = typeof window.Event === 'function' &&
            source instanceof window.Event

    if (!sourceIsEvt &&
            source.hasOwnProperty && source.hasOwnProperty('toString')) {
      destination.toString = source.toString
    }
  }
  return destination
}
/**
 * @description 对象拷贝。
 * @param des - {Object} 目标对象。
 * @param soc - {Object} 源对象
 */
SuperMap.Util.copy = function (des, soc) {
  des = des || {}
  var v
  if (soc) {
    for (var p in des) {
      v = soc[p]
      if (typeof v !== 'undefined') {
        des[p] = v
      }
    }
  }
}
/**
 * @description 销毁对象，将其属性置空
 * @param obj - {Object} 目标对象。
 */
SuperMap.Util.reset = function (obj) {
  obj = obj || {}
  for (var p in obj) {
    if (obj.hasOwnProperty(p)) {
      if (typeof obj[p] === 'object' && obj[p] instanceof Array) {
        for (var i in obj[p]) {
          if (obj[p][i].destroy) {
            obj[p][i].destroy()
          }
        }
        obj[p].length = 0
      } else if (typeof obj[p] === 'object' && obj[p] instanceof Object) {
        if (obj[p].destroy) {
          obj[p].destroy()
        }
      }
      obj[p] = null
    }
  }
}

/**
 * @description 获取HTML元素数组。
 * @param argument - {String | HTMLElement | Window}
 * @return {Array<HTMLElement>} HTML元素数组。
 */
SuperMap.Util.getElement = function () {
  var elements = []

  for (var i = 0, len = arguments.length; i < len; i++) {
    var element = arguments[i]
    if (typeof element === 'string') {
      element = document.getElementById(element)
    }
    if (arguments.length === 1) {
      return element
    }
    elements.push(element)
  }
  return elements
}

/**
 * @description instance of的跨浏览器实现。
 * @param o - {Object} 对象。
 * @return {boolean} 是否是页面元素
 */
SuperMap.Util.isElement = function (o) {
  return !!(o && o.nodeType === 1)
}

/**
 * @description 判断一个对象是否是数组。
 * @param a - {Object} 对象。
 * @return {boolean} 是否是数组。
 */
SuperMap.Util.isArray = function (a) {
  return (Object.prototype.toString.call(a) === '[object Array]')
}

/**
 * @description 从数组中删除某一项。
 * @param array - {Array} 数组。
 * @param item - {Object} 数组中要删除的一项。
 * @return {Array} 执行删除操作后的数组。
 */
SuperMap.Util.removeItem = function (array, item) {
  for (var i = array.length - 1; i >= 0; i--) {
    if (array[i] === item) {
      array.splice(i, 1)
            // breakmore than once??
    }
  }
  return array
}

/**
 * @description 获取某对象再数组中的索引值。
 * @param array - {Array} 数组。
 * @param obj - {Object} 对象。
 * @return {number} 某对象再数组中的索引值。
 */
SuperMap.Util.indexOf = function (array, obj) {
  if (array == null) {
    return -1
  } else {
        // use the build-in function if available.
    if (typeof array.indexOf === 'function') {
      return array.indexOf(obj)
    } else {
      for (var i = 0, len = array.length; i < len; i++) {
        if (array[i] === obj) {
          return i
        }
      }
      return -1
    }
  }
}

/**
 * @description 修改某DOM元素的许多属性。
 * @param element - {HTMLElement} 待修改的DOM元素。
 * @param id - {string} DOM元素的id。
 * @param px - {SuperMap.Pixel} 包含DOM元素的style属性的left和top属性。
 * @param sz - {SuperMap.Size} 包含DOM元素的width和height属性。
 * @param position - {string} DOM元素的position属性。
 * @param border - {string} DOM元素的style属性的border属性。
 * @param overflow - {string} DOM元素的style属性的overflow属性。
 * @param opacity - {number} 不透明度值。取值范围为 (0.0 - 1.0)。
 */
SuperMap.Util.modifyDOMElement = function (element, id, px, sz, position,
                                           border, overflow, opacity) {
  if (id) {
    element.id = id
  }
  if (px) {
    element.style.left = px.x + 'px'
    element.style.top = px.y + 'px'
  }
  if (sz) {
    element.style.width = sz.w + 'px'
    element.style.height = sz.h + 'px'
  }
  if (position) {
    element.style.position = position
  }
  if (border) {
    element.style.border = border
  }
  if (overflow) {
    element.style.overflow = overflow
  }
  if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
    element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')'
    element.style.opacity = opacity
  } else if (parseFloat(opacity) === 1.0) {
    element.style.filter = ''
    element.style.opacity = ''
  }
}

/**
 * @description Takes an object and copies any properties that don't exist from
 *     another properties, by analogy with SuperMap.Util.extend() from
 *     Prototype.js.
 *
 * @param to -{Object} The destination object.
 * @param from -{Object} The source object.  Any properties of this object that
 *     are undefined in the to object will be set on the to object.
 *
 * @return {Object} A reference to the to object.  Note that the to argument is modified
 *     in place and returned by this function.
 */
SuperMap.Util.applyDefaults = function (to, from) {
  to = to || {}
    /*
     * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
     * prototype object" when calling hawOwnProperty if the source object is an
     * instance of window.Event.
     */
  var fromIsEvt = typeof window.Event === 'function' &&
        from instanceof window.Event

  for (var key in from) {
    if (to[key] === undefined ||
            (!fromIsEvt && from.hasOwnProperty &&
                from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
      to[key] = from[key]
    }
  }
    /**
     * IE doesn't include the toString property when iterating over an object's
     * properties with the for(property in object) syntax.  Explicitly check if
     * the source has its own toString property.
     */
  if (!fromIsEvt && from && from.hasOwnProperty &&
        from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
    to.toString = from.toString
  }

  return to
}

/**
 * @param params - {Object} 参数对象。
 * @return {string} HTTP的GEI请求中的参数字符串。
 * @description 将参数对象转换为HTTP的GEI请求中的参数字符串。例如："key1=value1&key2=value2&key3=value3"。
 */
SuperMap.Util.getParameterString = function (params) {
  var paramsArray = []

  for (var key in params) {
    var value = params[key]
    if ((value != null) && (typeof value !== 'function')) {
      var encodedValue
      if (typeof value === 'object' && value.constructor === Array) {
                /* value is an array encode items and separate with "," */
        var encodedItemArray = []
        var item
        for (var 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('&')
}

/**
 * @description 给url追加参数。
 * @param url - {string} 待追加参数的url字符串。
 * @param paramStr - {string} 待追加的参数。
 * @return {string} The new url
 */
SuperMap.Util.urlAppend = function (url, paramStr) {
  var newUrl = url
  if (paramStr) {
    var parts = (url + ' ').split(/[?&]/)
    newUrl += (parts.pop() === ' '
            ? paramStr
            : parts.length ? '&' + paramStr : '?' + paramStr)
  }
  return newUrl
}

/**
 * @description 为了避免浮点精度错误而保留的有效位数。
 * @type {number}
 * @default 14
 */
SuperMap.Util.DEFAULT_PRECISION = 14

/**
 * @description 将字符串以接近的精度转换为数字。
 * @param number - {string} 字符串。
 * @param precision - {number} 精度。
 * @return {number} 数字。
 */
SuperMap.Util.toFloat = function (number, precision) {
  if (precision == null) {
    precision = SuperMap.Util.DEFAULT_PRECISION
  }
  if (typeof number !== 'number') {
    number = parseFloat(number)
  }
  return precision === 0 ? number
        : parseFloat(number.toPrecision(precision))
}

/**
 * @description 角度转弧度。
 * @param x - {number} 角度。
 * @return {number} 弧度。
 */
SuperMap.Util.rad = function (x) {
  return x * Math.PI / 180
}

/**
 * @description 从URL字符串中解析出参数对象。
 * @param url - {string} url。
 * @return {Object} 解析出的参数对象。
 */
SuperMap.Util.getParameters = function (url) {
    // if no url specified, take it from the location bar
  url = (url === null || url === undefined) ? window.location.href : url

    // parse out parameters portion of url string
  var paramsString = ''
  if (SuperMap.String.contains(url, '?')) {
    var start = url.indexOf('?') + 1
    var end = SuperMap.String.contains(url, '#')
            ? url.indexOf('#') : url.length
    paramsString = url.substring(start, end)
  }

  var parameters = {}
  var pairs = paramsString.split(/[&]/)
  for (var i = 0, len = pairs.length; i < len; ++i) {
    var keyValue = pairs[i].split('=')
    if (keyValue[0]) {
      var key = keyValue[0]
      try {
        key = decodeURIComponent(key)
      } catch (err) {
        key = unescape(key)
      }

            // being liberal by replacing "+" with " "
      var value = (keyValue[1] || '').replace(/\+/g, ' ')

      try {
        value = decodeURIComponent(value)
      } catch (err) {
        value = unescape(value)
      }

            // follow OGC convention of comma delimited values
      value = value.split(',')

            // if there's only one value, do not return as array
      if (value.length === 1) {
        value = value[0]
      }

      parameters[key] = value
    }
  }
  return parameters
}

/**
 * @description 不断递增计数变量，用于生成唯一ID。
 * @type {number}
 * @default 0
 */
SuperMap.Util.lastSeqID = 0

/**
 * @description 创建唯一ID值。
 * @param prefix {string} 前缀。
 * @return {string} 唯一的ID值。
 */
SuperMap.Util.createUniqueID = function (prefix) {
  if (prefix == null) {
    prefix = 'id_'
  }
  SuperMap.Util.lastSeqID += 1
  return prefix + SuperMap.Util.lastSeqID
}

/**
 * @memberOf SuperMap
 * @description 每单位的英尺数。
 * @type {Object}
 * @constant
 */
SuperMap.INCHES_PER_UNIT = {
  'inches': 1.0,
  'ft': 12.0,
  'mi': 63360.0,
  'm': 39.3701,
  'km': 39370.1,
  'dd': 4374754,
  'yd': 36
}
SuperMap.INCHES_PER_UNIT['in'] = SuperMap.INCHES_PER_UNIT.inches
SuperMap.INCHES_PER_UNIT['degrees'] = SuperMap.INCHES_PER_UNIT.dd
SuperMap.INCHES_PER_UNIT['nmi'] = 1852 * SuperMap.INCHES_PER_UNIT.m

// Units from CS-Map
SuperMap.METERS_PER_INCH = 0.02540005080010160020
SuperMap.Util.extend(SuperMap.INCHES_PER_UNIT, {
  'Inch': SuperMap.INCHES_PER_UNIT.inches,
  'Meter': 1.0 / SuperMap.METERS_PER_INCH,   // EPSG:9001
  'Foot': 0.30480060960121920243 / SuperMap.METERS_PER_INCH,   // EPSG:9003
  'IFoot': 0.30480000000000000000 / SuperMap.METERS_PER_INCH,   // EPSG:9002
  'ClarkeFoot': 0.3047972651151 / SuperMap.METERS_PER_INCH,   // EPSG:9005
  'SearsFoot': 0.30479947153867624624 / SuperMap.METERS_PER_INCH,   // EPSG:9041
  'GoldCoastFoot': 0.30479971018150881758 / SuperMap.METERS_PER_INCH,   // EPSG:9094
  'IInch': 0.02540000000000000000 / SuperMap.METERS_PER_INCH,
  'MicroInch': 0.00002540000000000000 / SuperMap.METERS_PER_INCH,
  'Mil': 0.00000002540000000000 / SuperMap.METERS_PER_INCH,
  'Centimeter': 0.01000000000000000000 / SuperMap.METERS_PER_INCH,
  'Kilometer': 1000.00000000000000000000 / SuperMap.METERS_PER_INCH,   // EPSG:9036
  'Yard': 0.91440182880365760731 / SuperMap.METERS_PER_INCH,
  'SearsYard': 0.914398414616029 / SuperMap.METERS_PER_INCH,   // EPSG:9040
  'IndianYard': 0.91439853074444079983 / SuperMap.METERS_PER_INCH,   // EPSG:9084
  'IndianYd37': 0.91439523 / SuperMap.METERS_PER_INCH,   // EPSG:9085
  'IndianYd62': 0.9143988 / SuperMap.METERS_PER_INCH,   // EPSG:9086
  'IndianYd75': 0.9143985 / SuperMap.METERS_PER_INCH,   // EPSG:9087
  'IndianFoot': 0.30479951 / SuperMap.METERS_PER_INCH,   // EPSG:9080
  'IndianFt37': 0.30479841 / SuperMap.METERS_PER_INCH,   // EPSG:9081
  'IndianFt62': 0.3047996 / SuperMap.METERS_PER_INCH,   // EPSG:9082
  'IndianFt75': 0.3047995 / SuperMap.METERS_PER_INCH,   // EPSG:9083
  'Mile': 1609.34721869443738887477 / SuperMap.METERS_PER_INCH,
  'IYard': 0.91440000000000000000 / SuperMap.METERS_PER_INCH,   // EPSG:9096
  'IMile': 1609.34400000000000000000 / SuperMap.METERS_PER_INCH,   // EPSG:9093
  'NautM': 1852.00000000000000000000 / SuperMap.METERS_PER_INCH,   // EPSG:9030
  'Lat-66': 110943.316488932731 / SuperMap.METERS_PER_INCH,
  'Lat-83': 110946.25736872234125 / SuperMap.METERS_PER_INCH,
  'Decimeter': 0.10000000000000000000 / SuperMap.METERS_PER_INCH,
  'Millimeter': 0.00100000000000000000 / SuperMap.METERS_PER_INCH,
  'Dekameter': 10.00000000000000000000 / SuperMap.METERS_PER_INCH,
  'Decameter': 10.00000000000000000000 / SuperMap.METERS_PER_INCH,
  'Hectometer': 100.00000000000000000000 / SuperMap.METERS_PER_INCH,
  'GermanMeter': 1.0000135965 / SuperMap.METERS_PER_INCH,   // EPSG:9031
  'CaGrid': 0.999738 / SuperMap.METERS_PER_INCH,
  'ClarkeChain': 20.1166194976 / SuperMap.METERS_PER_INCH,   // EPSG:9038
  'GunterChain': 20.11684023368047 / SuperMap.METERS_PER_INCH,   // EPSG:9033
  'BenoitChain': 20.116782494375872 / SuperMap.METERS_PER_INCH,   // EPSG:9062
  'SearsChain': 20.11676512155 / SuperMap.METERS_PER_INCH,   // EPSG:9042
  'ClarkeLink': 0.201166194976 / SuperMap.METERS_PER_INCH,   // EPSG:9039
  'GunterLink': 0.2011684023368047 / SuperMap.METERS_PER_INCH,   // EPSG:9034
  'BenoitLink': 0.20116782494375872 / SuperMap.METERS_PER_INCH,   // EPSG:9063
  'SearsLink': 0.2011676512155 / SuperMap.METERS_PER_INCH,   // EPSG:9043
  'Rod': 5.02921005842012 / SuperMap.METERS_PER_INCH,
  'IntnlChain': 20.1168 / SuperMap.METERS_PER_INCH,   // EPSG:9097
  'IntnlLink': 0.201168 / SuperMap.METERS_PER_INCH,   // EPSG:9098
  'Perch': 5.02921005842012 / SuperMap.METERS_PER_INCH,
  'Pole': 5.02921005842012 / SuperMap.METERS_PER_INCH,
  'Furlong': 201.1684023368046 / SuperMap.METERS_PER_INCH,
  'Rood': 3.778266898 / SuperMap.METERS_PER_INCH,
  'CapeFoot': 0.3047972615 / SuperMap.METERS_PER_INCH,
  'Brealey': 375.00000000000000000000 / SuperMap.METERS_PER_INCH,
  'ModAmFt': 0.304812252984505969011938 / SuperMap.METERS_PER_INCH,
  'Fathom': 1.8288 / SuperMap.METERS_PER_INCH,
  'NautM-UK': 1853.184 / SuperMap.METERS_PER_INCH,
  '50kilometers': 50000.0 / SuperMap.METERS_PER_INCH,
  '150kilometers': 150000.0 / SuperMap.METERS_PER_INCH
})

// unit abbreviations supported by PROJ.4
SuperMap.Util.extend(SuperMap.INCHES_PER_UNIT, {
  'mm': SuperMap.INCHES_PER_UNIT['Meter'] / 1000.0,
  'cm': SuperMap.INCHES_PER_UNIT['Meter'] / 100.0,
  'dm': SuperMap.INCHES_PER_UNIT['Meter'] * 100.0,
  'km': SuperMap.INCHES_PER_UNIT['Meter'] * 1000.0,
  'kmi': SuperMap.INCHES_PER_UNIT['nmi'],    // International Nautical Mile
  'fath': SuperMap.INCHES_PER_UNIT['Fathom'], // International Fathom
  'ch': SuperMap.INCHES_PER_UNIT['IntnlChain'],  // International Chain
  'link': SuperMap.INCHES_PER_UNIT['IntnlLink'], // International Link
  'us-in': SuperMap.INCHES_PER_UNIT['inches'], // U.S. Surveyor's Inch
  'us-ft': SuperMap.INCHES_PER_UNIT['Foot'],    // U.S. Surveyor's Foot
  'us-yd': SuperMap.INCHES_PER_UNIT['Yard'],    // U.S. Surveyor's Yard
  'us-ch': SuperMap.INCHES_PER_UNIT['GunterChain'], // U.S. Surveyor's Chain
  'us-mi': SuperMap.INCHES_PER_UNIT['Mile'],   // U.S. Surveyor's Statute Mile
  'ind-yd': SuperMap.INCHES_PER_UNIT['IndianYd37'],  // Indian Yard
  'ind-ft': SuperMap.INCHES_PER_UNIT['IndianFt37'],  // Indian Foot
  'ind-ch': 20.11669506 / SuperMap.METERS_PER_INCH  // Indian Chain
})

/**
 * @memberOf SuperMap
 * @description 分辨率与比例尺之间转换的常量，默认值96。
 * @type {Object}
 * @default 96
 */
SuperMap.DOTS_PER_INCH = 96

/**
 * @param scale - {number}
 * @return {number} 返回正常的scale值
 */
SuperMap.Util.normalizeScale = function (scale) {
  var normScale = (scale > 1.0) ? (1.0 / scale) : scale
  return normScale
}

/**
 * @param scale - {number} 比例尺。
 * @param units - {string} 比例尺单位。
 * @return {number} 分辨率。
 */
SuperMap.Util.getResolutionFromScale = function (scale, units) {
  var resolution
  if (scale) {
    if (units == null) {
      units = 'degrees'
    }
    var normScale = SuperMap.Util.normalizeScale(scale)
    resolution = 1 / (normScale * SuperMap.INCHES_PER_UNIT[units] *
            SuperMap.DOTS_PER_INCH)
  }
  return resolution
}

/**
 * @description 分辨率转比例尺。
 * @param resolution - {number} 分辨率。
 * @param units - {string} 分辨率单位。
 * @return {number} 比例尺。
 */
SuperMap.Util.getScaleFromResolution = function (resolution, units) {
  if (units == null) {
    units = 'degrees'
  }

  var scale = resolution * SuperMap.INCHES_PER_UNIT[units] *
        SuperMap.DOTS_PER_INCH
  return scale
}

/**
 * @memberOf SuperMap
 * @description 如果userAgent捕获到浏览器使用的是Gecko引擎则返回true。
 * @constant
 */
SuperMap.IS_GECKO = (function () {
  var ua = navigator.userAgent.toLowerCase()
  return ua.indexOf('webkit') === -1 && ua.indexOf('gecko') !== -1
})()

/**
 * @memberOf SuperMap
 * @description 浏览器名称，依赖于userAgent属性，BROWSER_NAME可以是空，或者以下浏览器：
 *     * "opera" -- Opera
 *     * "msie"  -- Internet Explorer
 *     * "safari" -- Safari
 *     * "firefox" -- Firefox
 *     * "mozilla" -- Mozilla
 * @constant
 */
SuperMap.Browser = (function () {
  var name = ''
  var version = ''
  var device = 'pc'
  var uaMatch
    // 以下进行测试
  var ua = navigator.userAgent.toLowerCase()
  if (ua.indexOf('msie') > -1 || (ua.indexOf('trident') > -1 && ua.indexOf('rv') > -1)) {
    name = 'msie'
    uaMatch = ua.match(/msie ([\d.]+)/) || ua.match(/rv:([\d.]+)/)
  } else if (ua.indexOf('chrome') > -1) {
    name = 'chrome'
    uaMatch = ua.match(/chrome\/([\d.]+)/)
  } else if (ua.indexOf('firefox') > -1) {
    name = 'firefox'
    uaMatch = ua.match(/firefox\/([\d.]+)/)
  } else if (ua.indexOf('opera') > -1) {
    name = 'opera'
    uaMatch = ua.match(/version\/([\d.]+)/)
  } else if (ua.indexOf('safari') > -1) {
    name = 'safari'
    uaMatch = ua.match(/version\/([\d.]+)/)
  }
  version = uaMatch ? uaMatch[1] : ''

  if (ua.indexOf('ipad') > -1 || ua.indexOf('ipod') > -1 || ua.indexOf('iphone') > -1) {
    device = 'apple'
  } else if (ua.indexOf('android') > -1) {
    uaMatch = ua.match(/version\/([\d.]+)/)
    version = uaMatch ? uaMatch[1] : ''
    device = 'android'
  }
  return {name: name, version: version, device: device}
})()

/**
 * @description 获取浏览器相关信息。支持的浏览器包括：Opera，Internet Explorer，Safari，Firefox。
 * @return {Object} 获取浏览器名称、版本、设备名称。对应的属性分别为 name, version, device。
 */
SuperMap.Util.getBrowser = function () {
  return SuperMap.Browser
}

/**
 * @description 浏览器是否支持Canvas。
 * @return {boolean} 获取当前浏览器是否支持 HTML5 Canvas 。
 */
SuperMap.Util.isSupportCanvas = (function () {
  var checkRes = true
  var broz = SuperMap.Util.getBrowser()
  if (document.createElement('canvas').getContext) {
    if (broz.name === 'firefox' && parseFloat(broz.version) < 5) {
      checkRes = false
    }
    if (broz.name === 'safari' && parseFloat(broz.version) < 4) {
      checkRes = false
    }
    if (broz.name === 'opera' && parseFloat(broz.version) < 10) {
      checkRes = false
    }
    if (broz.name === 'msie' && parseFloat(broz.version) < 9) {
      checkRes = false
    }
  } else {
    checkRes = false
  }
  return checkRes
})()

/**
 * @description 判断；浏览器是否支持Canvas。
 * @return {boolean} 获取当前浏览器是否支持 HTML5 Canvas 。
 */
SuperMap.Util.supportCanvas = function () {
  return SuperMap.Util.isSupportCanvas
}

// 将服务端的地图单位转成SuperMap的地图单位
SuperMap.INCHES_PER_UNIT['degree'] = SuperMap.INCHES_PER_UNIT.dd
SuperMap.INCHES_PER_UNIT['meter'] = SuperMap.INCHES_PER_UNIT.m
SuperMap.INCHES_PER_UNIT['foot'] = SuperMap.INCHES_PER_UNIT.ft
SuperMap.INCHES_PER_UNIT['inch'] = SuperMap.INCHES_PER_UNIT.inches
SuperMap.INCHES_PER_UNIT['mile'] = SuperMap.INCHES_PER_UNIT.mi
SuperMap.INCHES_PER_UNIT['kilometer'] = SuperMap.INCHES_PER_UNIT.km
SuperMap.INCHES_PER_UNIT['yard'] = SuperMap.INCHES_PER_UNIT.yd

/**
 * @description 判断一个 URL 请求是否在当前域中。
 * @param url - {string}  URL 请求字符串。
 * @return {boolean} URL请求是否在当前域中。
 */
SuperMap.Util.isInTheSameDomain = function (url) {
  if (!url) {
    return true
  }
  var index = url.indexOf('//')
  var documentUrl = document.location.toString()
  var documentIndex = documentUrl.indexOf('//')
  if (index === -1) {
    return true
  } else {
    var protocol
    var substring = protocol = url.substring(0, index)
    var documentSubString = documentUrl.substring(documentIndex + 2)
    documentIndex = documentSubString.indexOf('/')
    var documentPortIndex = documentSubString.indexOf(':')
    var documentDomainWithPort = documentSubString.substring(0, documentIndex)
        // var documentPort

    var 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)
    var portIndex = substring.indexOf(':')
    index = substring.indexOf('/')
    var domainWithPort = substring.substring(0, index)
    var domain
    if (portIndex !== -1) {
      domain = substring.substring(0, portIndex)
    } else {
      domain = substring.substring(0, index)
      domainWithPort += ':' + (protocol.toLowerCase() === 'http:' ? 80 : 443)
    }
    var documentDomain = document.domain
    if (domain === documentDomain && domainWithPort === documentDomainWithPort) {
      return true
    }
  }
  return false
}

/**
 * @description 计算iServer服务的REST图层的显示分辨率，需要从iServer的REST图层表述中获取viewBounds、viewer、scale、coordUnit、datumAxis 五个参数，来进行计算。
 * @param viewBounds - {SuperMap.Bounds} 地图的参照可视范围，即地图初始化时默认的地图显示范围。
 * @param viewer - {SuperMap.Size} 地图初始化时默认的地图图片的尺寸。
 * @param scale - {number} 地图初始化时默认的显示比例尺。
 * @param coordUnit - {string} 投影坐标系统的地图单位。
 * @param datumAxis - {number} 地理坐标系统椭球体长半轴。用户自定义地图的Options时，若未指定该参数的值，则系统默认为WGS84参考系的椭球体长半轴6378137。
 * @return {number} 返回图层显示分辨率。
 */
SuperMap.Util.calculateDpi = function (viewBounds, viewer, scale, coordUnit, datumAxis) {
    // 10000 是 0.1毫米与米的转换。DPI的计算公式：Viewer / DPI *  0.0254 * 10000 = ViewBounds * scale ，公式中的10000是为了提高计算结果的精度，以下出现的ratio皆为如此。
  if (!viewBounds || !viewer || !scale) {
    return
  }
  var ratio = 10000
  var rvbWidth = viewBounds.getWidth()
  var rvbHeight = viewBounds.getHeight()
  var rvWidth = viewer.w
  var rvHeight = viewer.h
    // 用户自定义地图的Options时，若未指定该参数的值，则系统默认为6378137米，即WGS84参考系的椭球体长半轴。
  datumAxis = datumAxis || 6378137
  coordUnit = coordUnit || 'degrees'
  var dpi
  if (coordUnit.toLowerCase() === 'degree' || coordUnit.toLowerCase() === 'degrees' || coordUnit.toLowerCase() === 'dd') {
    let num1 = rvbWidth / rvWidth
    let num2 = rvbHeight / rvHeight
    let resolution = num1 > num2 ? num1 : num2
    dpi = 0.0254 * ratio / resolution / scale / ((Math.PI * 2 * datumAxis) / 360) / ratio
  } else {
    let resolution = rvbWidth / rvWidth
    dpi = 0.0254 * ratio / resolution / scale / ratio
  }
  return dpi
}

/**
 * @description 将对象转换成 JSON 字符串。
 * @param obj - {Object} 要转换成 JSON 的 Object 对象。
 * @return {string} 返回转换后的 JSON 对象。
 */
SuperMap.Util.toJSON = function (obj) {
  var objInn = obj
  if (objInn == null) {
    return null
  }
  switch (objInn.constructor) {
    case String:
            // s = "'" + str.replace(/(["\\])/g, "\\$1") + "'"   string含有单引号出错
      objInn = '"' + objInn.replace(/(["\\])/g, '\\$1') + '"'
      objInn = objInn.replace(/\n/g, '\\n')
      objInn = objInn.replace(/\r/g, '\\r')
      objInn = objInn.replace('<', '&lt')
      objInn = objInn.replace('>', '&gt')
      objInn = objInn.replace(/%/g, '%25')
      objInn = objInn.replace(/&/g, '%26')
      return objInn
    case Array:
      var arr = []
      for (var i = 0, len = objInn.length; i < len; i++) {
        arr.push(SuperMap.Util.toJSON(objInn[i]))
      }
      return '[' + arr.join(',') + ']'
    case Number:
      return isFinite(objInn) ? String(objInn) : null
    case Boolean:
      return String(objInn)
    case Date:
      var dateStr = '{' + "'__type':\"System.DateTime\"," +
                "'Year':" + objInn.getFullYear() + ',' +
                "'Month':" + (objInn.getMonth() + 1) + ',' +
                "'Day':" + objInn.getDate() + ',' +
                "'Hour':" + objInn.getHours() + ',' +
                "'Minute':" + objInn.getMinutes() + ',' +
                "'Second':" + objInn.getSeconds() + ',' +
                "'Millisecond':" + objInn.getMilliseconds() + ',' +
                "'TimezoneOffset':" + objInn.getTimezoneOffset() + '}'
      return dateStr
    default:
      if (objInn['toJSON'] != null && typeof objInn['toJSON'] === 'function') {
        return objInn.toJSON()
      }
      if (typeof objInn === 'object') {
        if (objInn.length) {
          let arr = []
          for (let i = 0, len = objInn.length; i < len; i++) {
            arr.push(SuperMap.Util.toJSON(objInn[i]))
          }
          return '[' + arr.join(',') + ']'
        }
        let arr = []
        for (let attr in objInn) {
                    // 为解决SuperMap.Geometry类型头json时堆栈溢出的问题，attr == "parent"时不进行json转换
          if (typeof objInn[attr] !== 'function' && attr !== 'CLASS_NAME' && attr !== 'parent') {
            arr.push("'" + attr + "':" + SuperMap.Util.toJSON(objInn[attr]))
          }
        }

        if (arr.length > 0) {
          return '{' + arr.join(',') + '}'
        } else {
          return '{}'
        }
      }
      return objInn.toString()
  }
}

/**
 * @description 根据比例尺和dpi计算屏幕分辨率。
 * @param scale - {number} 比例尺。
 * @param dpi - {number} 图像分辨率，表示每英寸内的像素个数。
 * @param coordUnit - {string} 投影坐标系统的地图单位。
 * @param datumAxis - {number} 地理坐标系统椭球体长半轴。用户自定义地图的Options时，若未指定该参数的值，则DPI默认按照WGS84参考系的椭球体长半轴6378137来计算。
 * @return {number} 返回当前比例尺下的屏幕分辨率。
 */
SuperMap.Util.getResolutionFromScaleDpi = function (scale, dpi, coordUnit, datumAxis) {
  var resolution = null
  var ratio = 10000
    // 用户自定义地图的Options时，若未指定该参数的值，则系统默认为6378137米，即WGS84参考系的椭球体长半轴。
  datumAxis = datumAxis || 6378137
  coordUnit = coordUnit || ''
  if (scale > 0 && dpi > 0) {
    scale = SuperMap.Util.normalizeScale(scale)
    if (coordUnit.toLowerCase() === 'degree' || coordUnit.toLowerCase() === 'degrees' || coordUnit.toLowerCase() === 'dd') {
            // scale = SuperMap.Util.normalizeScale(scale)
      resolution = 0.0254 * ratio / dpi / scale / ((Math.PI * 2 * datumAxis) / 360) / ratio
      return resolution
    } else {
      resolution = 0.0254 * ratio / dpi / scale / ratio
      return resolution
    }
  }
  return -1
}

/**
 * @description 根据resolution、dpi、coordUnit和datumAxis计算比例尺。
 * @param resolution - {number} 用于计算比例尺的地图分辨率。
 * @param dpi - {number} 图像分辨率，表示每英寸内的像素个数。
 * @param coordUnit - {string} 投影坐标系统的地图单位。
 * @param datumAxis - {number} 地理坐标系统椭球体长半轴。用户自定义地图的Options时，若未指定该参数的值，则DPI默认按照WGS84参考系的椭球体长半轴6378137来计算。
 * @return {number} 返回当前屏幕分辨率下的比例尺。
 */
SuperMap.Util.getScaleFromResolutionDpi = function (resolution, dpi, coordUnit, datumAxis) {
  var scale = null
  var ratio = 10000
    // 用户自定义地图的Options时，若未指定该参数的值，则系统默认为6378137米，即WGS84参考系的椭球体长半轴。
  datumAxis = datumAxis || 6378137
  coordUnit = coordUnit || ''
  if (resolution > 0 && dpi > 0) {
    if (coordUnit.toLowerCase() === 'degree' || coordUnit.toLowerCase() === 'degrees' || coordUnit.toLowerCase() === 'dd') {
      scale = 0.0254 * ratio / dpi / resolution / ((Math.PI * 2 * datumAxis) / 360) / ratio
      return scale
    } else {
      scale = 0.0254 * ratio / dpi / resolution / ratio
      return scale
    }
  }
  return -1
}

/**
 * @description 转换查询结果。
 * @param result - {Object} 查询结果。
 * @return {Object} 转换后的查询结果。
 */
SuperMap.Util.transformResult = function (result) {
  if (result.responseText && typeof result.responseText === 'string') {
    result = JSON.parse(result.responseText)
  }
  return result
}

/**
 * @description 属性拷贝，不拷贝方法类名(CLASS_NAME)等。
 * @param destination - {Object} 拷贝目标。
 * @param source - {Object} 源对象。
 *
 */
SuperMap.Util.copyAttributes = function (destination, source) {
  destination = destination || {}
  if (source) {
    for (var property in source) {
      var value = source[property]
      if (value !== undefined && property !== 'CLASS_NAME' && typeof value !== 'function') {
        destination[property] = value
      }
    }
  }
  return destination
}

/**
 * @description 将源对象上的属性拷贝到目标对象上。（不拷贝 CLASS_NAME 和方法）
 * @param destination - {Object} 目标对象。
 * @param source - {Object} 源对象。
 * @param clip - {Array<string>} 源对象中禁止拷贝到目标对象的属性，目的是防止目标对象上不可修改的属性被篡改。
 *
 */
SuperMap.Util.copyAttributesWithClip = function (destination, source, clip) {
  destination = destination || {}
  if (source) {
    for (var property in source) {
            // 去掉禁止拷贝的属性
      var isInClip = false
      if (clip && clip.length) {
        for (var i = 0, len = clip.length; i < len; i++) {
          if (property === clip[i]) {
            isInClip = true
            break
          }
        }
      }
      if (isInClip === true) {
        continue
      }

      var value = source[property]
      if (value !== undefined && property !== 'CLASS_NAME' && typeof value !== 'function') {
        destination[property] = value
      }
    }
  }
  return destination
}

/**
 * @description 克隆一份Object对象
 * @param obj - {Object}  需要克隆的对象。
 * @return {Object} 返回对象的拷贝对象，注意是新的对象，不是指向。
 */
SuperMap.Util.cloneObject = function (obj) {
    // Handle the 3 simple types, and null or undefined
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

    // Handle Date
  if (obj instanceof Date) {
    let copy = new Date()
    copy.setTime(obj.getTime())
    return copy
  }

    // Handle Array
  if (obj instanceof Array) {
    let copy = obj.slice(0)
    return copy
  }

    // Handle Object
  if (obj instanceof Object) {
    let copy = {}
    for (var attr in obj) {
      if (obj.hasOwnProperty(attr)) {
        copy[attr] = SuperMap.Util.cloneObject(obj[attr])
      }
    }
    return copy
  }

  throw new Error("Unable to copy obj! Its type isn't supported.")
}

/**
 * @description 判断两条线段是不是有交点。
 * @param a1 - {SuperMap.Geometry.Point}  第一条线段的起始节点。
 * @param a2 - {SuperMap.Geometry.Point}  第一条线段的结束节点。
 * @param b1 - {SuperMap.Geometry.Point}  第二条线段的起始节点。
 * @param b2 - {SuperMap.Geometry.Point}  第二条线段的结束节点。
 * @return {Object} 如果相交返回交点，如果不相交返回两条线段的位置关系。
 */
SuperMap.Util.lineIntersection = function (a1, a2, b1, b2) {
  var intersectValue = null
  var k1
  var k2
  var b = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x)
  var a = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x)
  var ab = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y)
    // ab==0代表两条线断的斜率一样
  if (ab !== 0) {
    k1 = b / ab
    k2 = a / ab

    if (k1 >= 0 && k2 <= 1 && k1 <= 1 && k2 >= 0) {
      intersectValue = new SuperMap.Geometry.Point(a1.x + k1 * (a2.x - a1.x), a1.y + k1 * (a2.y - a1.y))
    } else {
      intersectValue = 'No Intersection'
    }
  } else {
    if (b === 0 && a === 0) {
      var maxy = Math.max(a1.y, a2.y)
      var miny = Math.min(a1.y, a2.y)
      var maxx = Math.max(a1.x, a2.x)
      var minx = Math.min(a1.x, a2.x)
      if (((b1.y >= miny && b1.y <= maxy) || (b2.y >= miny && b2.y <= maxy)) && (b1.x >= minx && b1.x <= maxx) || (b2.x >= minx && b2.x <= maxx)) {
        intersectValue = 'Coincident'// 重合
      } else {
        intersectValue = 'Parallel'// 平行
      }
    } else {
      intersectValue = 'Parallel'// 平行
    }
  }
  return intersectValue
}

/**
 * @description 获取文本外接矩形宽度与高度。
 * @param style - {SuperMap.Style} 文本样式。
 * @param text - {string} 文本内容。
 * @param element - {Object} DOM元素。
 * @return {Object} 返回裁剪后的宽度，高度信息。
 */
SuperMap.Util.getTextBounds = function (style, text, element) {
  document.body.appendChild(element)
  element.style.width = 'auto'
  element.style.height = 'auto'
  if (style.fontSize) {
    element.style.fontSize = style.fontSize
  }
  if (style.fontFamily) {
    element.style.fontFamily = style.fontFamily
  }
  if (style.fontWeight) {
    element.style.fontWeight = style.fontWeight
  }
  element.style.position = 'relative'
  element.style.visibility = 'hidden'
    // fix 在某些情况下，element内的文本变成竖起排列，导致宽度计算不正确的bug
  element.style.display = 'inline-block'
  element.innerHTML = text
  var textWidth = element.clientWidth
  var textHeight = element.clientHeight
  document.body.removeChild(element)
  return {
    textWidth: textWidth,
    textHeight: textHeight
  }
}
