'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _index = require('../index.js');

var _index2 = _interopRequireDefault(_index);

var _asserts = require('../asserts.js');

var _asserts2 = _interopRequireDefault(_asserts);

var _feature = require('../feature.js');

var _feature2 = _interopRequireDefault(_feature);

var _feature3 = require('../format/feature.js');

var _feature4 = _interopRequireDefault(_feature3);

var _jsonfeature = require('../format/jsonfeature.js');

var _jsonfeature2 = _interopRequireDefault(_jsonfeature);

var _geometrycollection = require('../geom/geometrycollection.js');

var _geometrycollection2 = _interopRequireDefault(_geometrycollection);

var _linestring = require('../geom/linestring.js');

var _linestring2 = _interopRequireDefault(_linestring);

var _multilinestring = require('../geom/multilinestring.js');

var _multilinestring2 = _interopRequireDefault(_multilinestring);

var _multipoint = require('../geom/multipoint.js');

var _multipoint2 = _interopRequireDefault(_multipoint);

var _multipolygon = require('../geom/multipolygon.js');

var _multipolygon2 = _interopRequireDefault(_multipolygon);

var _point = require('../geom/point.js');

var _point2 = _interopRequireDefault(_point);

var _polygon = require('../geom/polygon.js');

var _polygon2 = _interopRequireDefault(_polygon);

var _obj = require('../obj.js');

var _obj2 = _interopRequireDefault(_obj);

var _proj = require('../proj.js');

var _proj2 = _interopRequireDefault(_proj);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

/**
 * @classdesc
 * Feature format for reading and writing data in the GeoJSON format.
 *
 * @constructor
 * @extends {ol.format.JSONFeature}
 * @param {olx.format.GeoJSONOptions=} opt_options Options.
 * @api
 */
// TODO: serialize dataProjection as crs member when writing
// see https://github.com/openlayers/openlayers/issues/2078

var _ol_format_GeoJSON_ = function (opt_options) {

  var options = opt_options ? opt_options : {};

  _jsonfeature2.default.call(this);

  /**
   * @inheritDoc
   */
  this.defaultDataProjection = _proj2.default.get(options.defaultDataProjection ? options.defaultDataProjection : 'EPSG:4326');

  if (options.featureProjection) {
    this.defaultFeatureProjection = _proj2.default.get(options.featureProjection);
  }

  /**
   * Name of the geometry attribute for features.
   * @type {string|undefined}
   * @private
   */
  this.geometryName_ = options.geometryName;

  /**
   * Look for the geometry name in the feature GeoJSON
   * @type {boolean|undefined}
   * @private
   */
  this.extractGeometryName_ = options.extractGeometryName;
};

_index2.default.inherits(_ol_format_GeoJSON_, _jsonfeature2.default);

/**
 * @param {GeoJSONGeometry|GeoJSONGeometryCollection} object Object.
 * @param {olx.format.ReadOptions=} opt_options Read options.
 * @private
 * @return {ol.geom.Geometry} Geometry.
 */
_ol_format_GeoJSON_.readGeometry_ = function (object, opt_options) {
  if (!object) {
    return null;
  }
  var geometryReader = _ol_format_GeoJSON_.GEOMETRY_READERS_[object.type];
  return (
    /** @type {ol.geom.Geometry} */_feature4.default.transformWithOptions(geometryReader(object), false, opt_options)
  );
};

/**
 * @param {GeoJSONGeometryCollection} object Object.
 * @param {olx.format.ReadOptions=} opt_options Read options.
 * @private
 * @return {ol.geom.GeometryCollection} Geometry collection.
 */
_ol_format_GeoJSON_.readGeometryCollectionGeometry_ = function (object, opt_options) {
  var geometries = object.geometries.map(
  /**
   * @param {GeoJSONGeometry} geometry Geometry.
   * @return {ol.geom.Geometry} geometry Geometry.
   */
  function (geometry) {
    return _ol_format_GeoJSON_.readGeometry_(geometry, opt_options);
  });
  return new _geometrycollection2.default(geometries);
};

/**
 * @param {GeoJSONGeometry} object Object.
 * @private
 * @return {ol.geom.Point} Point.
 */
_ol_format_GeoJSON_.readPointGeometry_ = function (object) {
  return new _point2.default(object.coordinates);
};

/**
 * @param {GeoJSONGeometry} object Object.
 * @private
 * @return {ol.geom.LineString} LineString.
 */
_ol_format_GeoJSON_.readLineStringGeometry_ = function (object) {
  return new _linestring2.default(object.coordinates);
};

/**
 * @param {GeoJSONGeometry} object Object.
 * @private
 * @return {ol.geom.MultiLineString} MultiLineString.
 */
_ol_format_GeoJSON_.readMultiLineStringGeometry_ = function (object) {
  return new _multilinestring2.default(object.coordinates);
};

/**
 * @param {GeoJSONGeometry} object Object.
 * @private
 * @return {ol.geom.MultiPoint} MultiPoint.
 */
_ol_format_GeoJSON_.readMultiPointGeometry_ = function (object) {
  return new _multipoint2.default(object.coordinates);
};

/**
 * @param {GeoJSONGeometry} object Object.
 * @private
 * @return {ol.geom.MultiPolygon} MultiPolygon.
 */
_ol_format_GeoJSON_.readMultiPolygonGeometry_ = function (object) {
  return new _multipolygon2.default(object.coordinates);
};

/**
 * @param {GeoJSONGeometry} object Object.
 * @private
 * @return {ol.geom.Polygon} Polygon.
 */
_ol_format_GeoJSON_.readPolygonGeometry_ = function (object) {
  return new _polygon2.default(object.coordinates);
};

/**
 * @param {ol.geom.Geometry} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry|GeoJSONGeometryCollection} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writeGeometry_ = function (geometry, opt_options) {
  var geometryWriter = _ol_format_GeoJSON_.GEOMETRY_WRITERS_[geometry.getType()];
  return geometryWriter( /** @type {ol.geom.Geometry} */_feature4.default.transformWithOptions(geometry, true, opt_options), opt_options);
};

/**
 * @param {ol.geom.Geometry} geometry Geometry.
 * @private
 * @return {GeoJSONGeometryCollection} Empty GeoJSON geometry collection.
 */
_ol_format_GeoJSON_.writeEmptyGeometryCollectionGeometry_ = function (geometry) {
  return (/** @type {GeoJSONGeometryCollection} */{
      type: 'GeometryCollection',
      geometries: []
    }
  );
};

/**
 * @param {ol.geom.GeometryCollection} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometryCollection} GeoJSON geometry collection.
 */
_ol_format_GeoJSON_.writeGeometryCollectionGeometry_ = function (geometry, opt_options) {
  var geometries = geometry.getGeometriesArray().map(function (geometry) {
    var options = _obj2.default.assign({}, opt_options);
    delete options.featureProjection;
    return _ol_format_GeoJSON_.writeGeometry_(geometry, options);
  });
  return (/** @type {GeoJSONGeometryCollection} */{
      type: 'GeometryCollection',
      geometries: geometries
    }
  );
};

/**
 * @param {ol.geom.LineString} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writeLineStringGeometry_ = function (geometry, opt_options) {
  return (/** @type {GeoJSONGeometry} */{
      type: 'LineString',
      coordinates: geometry.getCoordinates()
    }
  );
};

/**
 * @param {ol.geom.MultiLineString} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writeMultiLineStringGeometry_ = function (geometry, opt_options) {
  return (/** @type {GeoJSONGeometry} */{
      type: 'MultiLineString',
      coordinates: geometry.getCoordinates()
    }
  );
};

/**
 * @param {ol.geom.MultiPoint} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writeMultiPointGeometry_ = function (geometry, opt_options) {
  return (/** @type {GeoJSONGeometry} */{
      type: 'MultiPoint',
      coordinates: geometry.getCoordinates()
    }
  );
};

/**
 * @param {ol.geom.MultiPolygon} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writeMultiPolygonGeometry_ = function (geometry, opt_options) {
  var right;
  if (opt_options) {
    right = opt_options.rightHanded;
  }
  return (/** @type {GeoJSONGeometry} */{
      type: 'MultiPolygon',
      coordinates: geometry.getCoordinates(right)
    }
  );
};

/**
 * @param {ol.geom.Point} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writePointGeometry_ = function (geometry, opt_options) {
  return (/** @type {GeoJSONGeometry} */{
      type: 'Point',
      coordinates: geometry.getCoordinates()
    }
  );
};

/**
 * @param {ol.geom.Polygon} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @private
 * @return {GeoJSONGeometry} GeoJSON geometry.
 */
_ol_format_GeoJSON_.writePolygonGeometry_ = function (geometry, opt_options) {
  var right;
  if (opt_options) {
    right = opt_options.rightHanded;
  }
  return (/** @type {GeoJSONGeometry} */{
      type: 'Polygon',
      coordinates: geometry.getCoordinates(right)
    }
  );
};

/**
 * @const
 * @private
 * @type {Object.<string, function(GeoJSONObject): ol.geom.Geometry>}
 */
_ol_format_GeoJSON_.GEOMETRY_READERS_ = {
  'Point': _ol_format_GeoJSON_.readPointGeometry_,
  'LineString': _ol_format_GeoJSON_.readLineStringGeometry_,
  'Polygon': _ol_format_GeoJSON_.readPolygonGeometry_,
  'MultiPoint': _ol_format_GeoJSON_.readMultiPointGeometry_,
  'MultiLineString': _ol_format_GeoJSON_.readMultiLineStringGeometry_,
  'MultiPolygon': _ol_format_GeoJSON_.readMultiPolygonGeometry_,
  'GeometryCollection': _ol_format_GeoJSON_.readGeometryCollectionGeometry_
};

/**
 * @const
 * @private
 * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (GeoJSONGeometry|GeoJSONGeometryCollection)>}
 */
_ol_format_GeoJSON_.GEOMETRY_WRITERS_ = {
  'Point': _ol_format_GeoJSON_.writePointGeometry_,
  'LineString': _ol_format_GeoJSON_.writeLineStringGeometry_,
  'Polygon': _ol_format_GeoJSON_.writePolygonGeometry_,
  'MultiPoint': _ol_format_GeoJSON_.writeMultiPointGeometry_,
  'MultiLineString': _ol_format_GeoJSON_.writeMultiLineStringGeometry_,
  'MultiPolygon': _ol_format_GeoJSON_.writeMultiPolygonGeometry_,
  'GeometryCollection': _ol_format_GeoJSON_.writeGeometryCollectionGeometry_,
  'Circle': _ol_format_GeoJSON_.writeEmptyGeometryCollectionGeometry_
};

/**
 * Read a feature from a GeoJSON Feature source.  Only works for Feature or
 * geometry types.  Use {@link ol.format.GeoJSON#readFeatures} to read
 * FeatureCollection source. If feature at source has an id, it will be used
 * as Feature id by calling {@link ol.Feature#setId} internally.
 *
 * @function
 * @param {Document|Node|Object|string} source Source.
 * @param {olx.format.ReadOptions=} opt_options Read options.
 * @return {ol.Feature} Feature.
 * @api
 */
_ol_format_GeoJSON_.prototype.readFeature;

/**
 * Read all features from a GeoJSON source.  Works for all GeoJSON types.
 * If the source includes only geometries, features will be created with those
 * geometries.
 *
 * @function
 * @param {Document|Node|Object|string} source Source.
 * @param {olx.format.ReadOptions=} opt_options Read options.
 * @return {Array.<ol.Feature>} Features.
 * @api
 */
_ol_format_GeoJSON_.prototype.readFeatures;

/**
 * @inheritDoc
 */
_ol_format_GeoJSON_.prototype.readFeatureFromObject = function (object, opt_options) {
  /**
   * @type {GeoJSONFeature}
   */
  var geoJSONFeature = null;
  if (object.type === 'Feature') {
    geoJSONFeature = /** @type {GeoJSONFeature} */object;
  } else {
    geoJSONFeature = /** @type {GeoJSONFeature} */{
      type: 'Feature',
      geometry: /** @type {GeoJSONGeometry|GeoJSONGeometryCollection} */object
    };
  }

  var geometry = _ol_format_GeoJSON_.readGeometry_(geoJSONFeature.geometry, opt_options);
  var feature = new _feature2.default();
  if (this.geometryName_) {
    feature.setGeometryName(this.geometryName_);
  } else if (this.extractGeometryName_ && geoJSONFeature.geometry_name !== undefined) {
    feature.setGeometryName(geoJSONFeature.geometry_name);
  }
  feature.setGeometry(geometry);
  if (geoJSONFeature.id !== undefined) {
    feature.setId(geoJSONFeature.id);
  }
  if (geoJSONFeature.properties) {
    feature.setProperties(geoJSONFeature.properties);
  }
  return feature;
};

/**
 * @inheritDoc
 */
_ol_format_GeoJSON_.prototype.readFeaturesFromObject = function (object, opt_options) {
  var geoJSONObject = /** @type {GeoJSONObject} */object;
  /** @type {Array.<ol.Feature>} */
  var features = null;
  if (geoJSONObject.type === 'FeatureCollection') {
    var geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */
    object;
    features = [];
    var geoJSONFeatures = geoJSONFeatureCollection.features;
    var i, ii;
    for (i = 0, ii = geoJSONFeatures.length; i < ii; ++i) {
      features.push(this.readFeatureFromObject(geoJSONFeatures[i], opt_options));
    }
  } else {
    features = [this.readFeatureFromObject(object, opt_options)];
  }
  return features;
};

/**
 * Read a geometry from a GeoJSON source.
 *
 * @function
 * @param {Document|Node|Object|string} source Source.
 * @param {olx.format.ReadOptions=} opt_options Read options.
 * @return {ol.geom.Geometry} Geometry.
 * @api
 */
_ol_format_GeoJSON_.prototype.readGeometry;

/**
 * @inheritDoc
 */
_ol_format_GeoJSON_.prototype.readGeometryFromObject = function (object, opt_options) {
  return _ol_format_GeoJSON_.readGeometry_(
  /** @type {GeoJSONGeometry} */object, opt_options);
};

/**
 * Read the projection from a GeoJSON source.
 *
 * @function
 * @param {Document|Node|Object|string} source Source.
 * @return {ol.proj.Projection} Projection.
 * @api
 */
_ol_format_GeoJSON_.prototype.readProjection;

/**
 * @inheritDoc
 */
_ol_format_GeoJSON_.prototype.readProjectionFromObject = function (object) {
  var geoJSONObject = /** @type {GeoJSONObject} */object;
  var crs = geoJSONObject.crs;
  var projection;
  if (crs) {
    if (crs.type == 'name') {
      projection = _proj2.default.get(crs.properties.name);
    } else {
      _asserts2.default.assert(false, 36); // Unknown SRS type
    }
  } else {
    projection = this.defaultDataProjection;
  }
  return (/** @type {ol.proj.Projection} */projection
  );
};

/**
 * Encode a feature as a GeoJSON Feature string.
 *
 * @function
 * @param {ol.Feature} feature Feature.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @return {string} GeoJSON.
 * @override
 * @api
 */
_ol_format_GeoJSON_.prototype.writeFeature;

/**
 * Encode a feature as a GeoJSON Feature object.
 *
 * @param {ol.Feature} feature Feature.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @return {GeoJSONFeature} Object.
 * @override
 * @api
 */
_ol_format_GeoJSON_.prototype.writeFeatureObject = function (feature, opt_options) {
  opt_options = this.adaptOptions(opt_options);

  var object = /** @type {GeoJSONFeature} */{
    'type': 'Feature'
  };
  var id = feature.getId();
  if (id !== undefined) {
    object.id = id;
  }
  var geometry = feature.getGeometry();
  if (geometry) {
    object.geometry = _ol_format_GeoJSON_.writeGeometry_(geometry, opt_options);
  } else {
    object.geometry = null;
  }
  var properties = feature.getProperties();
  delete properties[feature.getGeometryName()];
  if (!_obj2.default.isEmpty(properties)) {
    object.properties = properties;
  } else {
    object.properties = null;
  }
  return object;
};

/**
 * Encode an array of features as GeoJSON.
 *
 * @function
 * @param {Array.<ol.Feature>} features Features.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @return {string} GeoJSON.
 * @api
 */
_ol_format_GeoJSON_.prototype.writeFeatures;

/**
 * Encode an array of features as a GeoJSON object.
 *
 * @param {Array.<ol.Feature>} features Features.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @return {GeoJSONFeatureCollection} GeoJSON Object.
 * @override
 * @api
 */
_ol_format_GeoJSON_.prototype.writeFeaturesObject = function (features, opt_options) {
  opt_options = this.adaptOptions(opt_options);
  var objects = [];
  var i, ii;
  for (i = 0, ii = features.length; i < ii; ++i) {
    objects.push(this.writeFeatureObject(features[i], opt_options));
  }
  return (/** @type {GeoJSONFeatureCollection} */{
      type: 'FeatureCollection',
      features: objects
    }
  );
};

/**
 * Encode a geometry as a GeoJSON string.
 *
 * @function
 * @param {ol.geom.Geometry} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @return {string} GeoJSON.
 * @api
 */
_ol_format_GeoJSON_.prototype.writeGeometry;

/**
 * Encode a geometry as a GeoJSON object.
 *
 * @param {ol.geom.Geometry} geometry Geometry.
 * @param {olx.format.WriteOptions=} opt_options Write options.
 * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object.
 * @override
 * @api
 */
_ol_format_GeoJSON_.prototype.writeGeometryObject = function (geometry, opt_options) {
  return _ol_format_GeoJSON_.writeGeometry_(geometry, this.adaptOptions(opt_options));
};
exports.default = _ol_format_GeoJSON_;
module.exports = exports['default'];