/**
 * echarts地图一般投射算法
 * modify from GeoMap v0.5.3 https://github.com/x6doooo/GeoMap
 * 
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */
module.exports = function () {
  function getBbox(json, specialArea) {
    specialArea = specialArea || {};

    if (!json.srcSize) {
      parseSrcSize(json, specialArea);
    }

    return json.srcSize;
  }

  function parseSrcSize(json, specialArea) {
    specialArea = specialArea || {};
    convertorParse.xmin = 360;
    convertorParse.xmax = -360;
    convertorParse.ymin = 180;
    convertorParse.ymax = -180;
    var shapes = json.features;
    var geometries;
    var shape;

    for (var i = 0, len = shapes.length; i < len; i++) {
      shape = shapes[i];

      if (shape.properties.name && specialArea[shape.properties.name]) {
        continue;
      }

      switch (shape.type) {
        case 'Feature':
          convertorParse[shape.geometry.type](shape.geometry.coordinates);
          break;

        case 'GeometryCollection':
          geometries = shape.geometries;

          for (var j = 0, len2 = geometries.length; j < len2; j++) {
            convertorParse[geometries[j].type](geometries[j].coordinates);
          }

          break;
      }
    }

    json.srcSize = {
      left: convertorParse.xmin.toFixed(4) * 1,
      top: convertorParse.ymin.toFixed(4) * 1,
      width: (convertorParse.xmax - convertorParse.xmin).toFixed(4) * 1,
      height: (convertorParse.ymax - convertorParse.ymin).toFixed(4) * 1
    };
    return json;
  }

  var convertor = {
    //调整俄罗斯东部到地图右侧与俄罗斯相连
    formatPoint: function (p) {
      return [(p[0] < -168.5 && p[1] > 63.8 ? p[0] + 360 : p[0]) + 168.5, 90 - p[1]];
    },
    makePoint: function (p) {
      var self = this;
      var point = self.formatPoint(p); // for cp

      if (self._bbox.xmin > p[0]) {
        self._bbox.xmin = p[0];
      }

      if (self._bbox.xmax < p[0]) {
        self._bbox.xmax = p[0];
      }

      if (self._bbox.ymin > p[1]) {
        self._bbox.ymin = p[1];
      }

      if (self._bbox.ymax < p[1]) {
        self._bbox.ymax = p[1];
      }

      var x = (point[0] - convertor.offset.x) * convertor.scale.x + convertor.offset.left;
      var y = (point[1] - convertor.offset.y) * convertor.scale.y + convertor.offset.top;
      return [x, y];
    },
    Point: function (coordinates) {
      coordinates = this.makePoint(coordinates);
      return coordinates.join(',');
    },
    LineString: function (coordinates) {
      var str = '';
      var point;

      for (var i = 0, len = coordinates.length; i < len; i++) {
        point = convertor.makePoint(coordinates[i]);

        if (i === 0) {
          str = 'M' + point.join(',');
        } else {
          str = str + 'L' + point.join(',');
        }
      }

      return str;
    },
    Polygon: function (coordinates) {
      var str = '';

      for (var i = 0, len = coordinates.length; i < len; i++) {
        str = str + convertor.LineString(coordinates[i]) + 'z';
      }

      return str;
    },
    MultiPoint: function (coordinates) {
      var arr = [];

      for (var i = 0, len = coordinates.length; i < len; i++) {
        arr.push(convertor.Point(coordinates[i]));
      }

      return arr;
    },
    MultiLineString: function (coordinates) {
      var str = '';

      for (var i = 0, len = coordinates.length; i < len; i++) {
        str += convertor.LineString(coordinates[i]);
      }

      return str;
    },
    MultiPolygon: function (coordinates) {
      var str = '';

      for (var i = 0, len = coordinates.length; i < len; i++) {
        str += convertor.Polygon(coordinates[i]);
      }

      return str;
    }
  };
  var convertorParse = {
    formatPoint: convertor.formatPoint,
    makePoint: function (p) {
      var self = this;
      var point = self.formatPoint(p);
      var x = point[0];
      var y = point[1];

      if (self.xmin > x) {
        self.xmin = x;
      }

      if (self.xmax < x) {
        self.xmax = x;
      }

      if (self.ymin > y) {
        self.ymin = y;
      }

      if (self.ymax < y) {
        self.ymax = y;
      }
    },
    Point: function (coordinates) {
      this.makePoint(coordinates);
    },
    LineString: function (coordinates) {
      for (var i = 0, len = coordinates.length; i < len; i++) {
        this.makePoint(coordinates[i]);
      }
    },
    Polygon: function (coordinates) {
      for (var i = 0, len = coordinates.length; i < len; i++) {
        this.LineString(coordinates[i]);
      }
    },
    MultiPoint: function (coordinates) {
      for (var i = 0, len = coordinates.length; i < len; i++) {
        this.Point(coordinates[i]);
      }
    },
    MultiLineString: function (coordinates) {
      for (var i = 0, len = coordinates.length; i < len; i++) {
        this.LineString(coordinates[i]);
      }
    },
    MultiPolygon: function (coordinates) {
      for (var i = 0, len = coordinates.length; i < len; i++) {
        this.Polygon(coordinates[i]);
      }
    }
  };

  function geoJson2Path(json, transform, specialArea) {
    specialArea = specialArea || {};
    convertor.scale = null;
    convertor.offset = null;

    if (!json.srcSize) {
      parseSrcSize(json, specialArea);
    }

    transform.offset = {
      x: json.srcSize.left,
      y: json.srcSize.top,
      left: transform.OffsetLeft || 0,
      top: transform.OffsetTop || 0
    };
    convertor.scale = transform.scale;
    convertor.offset = transform.offset;
    var shapes = json.features;
    var geometries;
    var pathArray = [];
    var val;
    var shape;

    for (var i = 0, len = shapes.length; i < len; i++) {
      shape = shapes[i];

      if (shape.properties.name && specialArea[shape.properties.name]) {
        // 忽略specialArea
        continue;
      }

      if (shape.type == 'Feature') {
        pushApath(shape.geometry, shape);
      } else if (shape.type == 'GeometryCollection') {
        geometries = shape.geometries;

        for (var j = 0, len2 = geometries.length; j < len2; j++) {
          val = geometries[j];
          pushApath(val, val);
        }
      }
    }

    var shapeType;
    var shapeCoordinates;
    var str;

    function pushApath(gm, shape) {
      shapeType = gm.type;
      shapeCoordinates = gm.coordinates;
      convertor._bbox = {
        xmin: 360,
        xmax: -360,
        ymin: 180,
        ymax: -180
      };
      str = convertor[shapeType](shapeCoordinates);
      pathArray.push({
        // type: shapeType,
        path: str,
        cp: shape.properties.cp ? convertor.makePoint(shape.properties.cp) : convertor.makePoint([(convertor._bbox.xmin + convertor._bbox.xmax) / 2, (convertor._bbox.ymin + convertor._bbox.ymax) / 2]),
        properties: shape.properties,
        id: shape.id
      });
    }

    return pathArray;
  }
  /**
   * 平面坐标转经纬度
   * @param {Array} p
   */


  function pos2geo(obj, p) {
    var x;
    var y;

    if (p instanceof Array) {
      x = p[0] * 1;
      y = p[1] * 1;
    } else {
      x = p.x * 1;
      y = p.y * 1;
    }

    x = x / obj.scale.x + obj.offset.x - 168.5;
    x = x > 180 ? x - 360 : x;
    y = 90 - (y / obj.scale.y + obj.offset.y);
    return [x, y];
  }
  /**
   * 经纬度转平面坐标
   * @param {Array | Object} p
   */


  function geo2pos(obj, p) {
    convertor.offset = obj.offset;
    convertor.scale = obj.scale;
    return p instanceof Array ? convertor.makePoint([p[0] * 1, p[1] * 1]) : convertor.makePoint([p.x * 1, p.y * 1]);
  }

  return {
    getBbox: getBbox,
    geoJson2Path: geoJson2Path,
    pos2geo: pos2geo,
    geo2pos: geo2pos
  };
}();