text: module.exports = Polygon;
  
  var util = require('util');
  
  var Geometry = require('./geometry');
  var Types = require('./types');
  var Point = require('./point');
  var BinaryWriter = require('./binarywriter');
  
  function Polygon(exteriorRing, interiorRings, srid) {
      Geometry.call(this);
  
      this.exteriorRing = exteriorRing || [];
      this.interiorRings = interiorRings || [];
  	this.srid = srid;
  
      if (this.exteriorRing.length > 0) {
          this.hasZ = this.exteriorRing[0].hasZ;
          this.hasM = this.exteriorRing[0].hasM;
      }
  }
  
  util.inherits(Polygon, Geometry);
  
  Polygon.Z = function (exteriorRing, interiorRings, srid) {
      var polygon = new Polygon(exteriorRing, interiorRings, srid);
      polygon.hasZ = true;
      return polygon;
  };
  
  Polygon.M = function (exteriorRing, interiorRings, srid) {
      var polygon = new Polygon(exteriorRing, interiorRings, srid);
      polygon.hasM = true;
      return polygon;
  };
  
  Polygon.ZM = function (exteriorRing, interiorRings, srid) {
      var polygon = new Polygon(exteriorRing, interiorRings, srid);
      polygon.hasZ = true;
      polygon.hasM = true;
      return polygon;
  };
  
  Polygon._parseWkt = function (value, options) {
      var polygon = new Polygon();
      polygon.srid = options.srid;
      polygon.hasZ = options.hasZ;
      polygon.hasM = options.hasM;
  
      if (value.isMatch(['EMPTY']))
          return polygon;
  
      value.expectGroupStart();
  
      value.expectGroupStart();
      polygon.exteriorRing.push.apply(polygon.exteriorRing, value.matchCoordinates(options));
      value.expectGroupEnd();
  
      while (value.isMatch([','])) {
          value.expectGroupStart();
          polygon.interiorRings.push(value.matchCoordinates(options));
          value.expectGroupEnd();
      }
  
      value.expectGroupEnd();
  
      return polygon;
  };
  
  Polygon._parseWkb = function (value, options) {
      var polygon = new Polygon();
      polygon.srid = options.srid;
      polygon.hasZ = options.hasZ;
      polygon.hasM = options.hasM;
  
      var ringCount = value.readUInt32();
  
      if (ringCount > 0) {
          var exteriorRingCount = value.readUInt32();
  
          for (var i = 0; i < exteriorRingCount; i++)
              polygon.exteriorRing.push(Point._readWkbPoint(value, options));
  
          for (i = 1; i < ringCount; i++) {
              var interiorRing = [];
  
              var interiorRingCount = value.readUInt32();
  
              for (var j = 0; j < interiorRingCount; j++)
                  interiorRing.push(Point._readWkbPoint(value, options));
  
              polygon.interiorRings.push(interiorRing);
          }
      }
  
      return polygon;
  };
  
  Polygon._parseTwkb = function (value, options) {
      var polygon = new Polygon();
      polygon.hasZ = options.hasZ;
      polygon.hasM = options.hasM;
  
      if (options.isEmpty)
          return polygon;
  
      var previousPoint = new Point(0, 0, options.hasZ ? 0 : undefined, options.hasM ? 0 : undefined);
      var ringCount = value.readVarInt();
      var exteriorRingCount = value.readVarInt();
  
      for (var i = 0; i < exteriorRingCount; i++)
          polygon.exteriorRing.push(Point._readTwkbPoint(value, options, previousPoint));
  
      for (i = 1; i < ringCount; i++) {
          var interiorRing = [];
  
          var interiorRingCount = value.readVarInt();
  
          for (var j = 0; j < interiorRingCount; j++)
              interiorRing.push(Point._readTwkbPoint(value, options, previousPoint));
  
          polygon.interiorRings.push(interiorRing);
      }
  
      return polygon;
  };
  
  Polygon._parseGeoJSON = function (value) {
      var polygon = new Polygon();
  
      if (value.coordinates.length > 0 && value.coordinates[0].length > 0)
          polygon.hasZ = value.coordinates[0][0].length > 2;
  
      for (var i = 0; i < value.coordinates.length; i++) {
          if (i > 0)
              polygon.interiorRings.push([]);
  
          for (var j = 0; j  < value.coordinates[i].length; j++) {
              if (i === 0)
                  polygon.exteriorRing.push(Point._readGeoJSONPoint(value.coordinates[i][j]));
              else
                  polygon.interiorRings[i - 1].push(Point._readGeoJSONPoint(value.coordinates[i][j]));
          }
      }
  
      return polygon;
  };
  
  Polygon.prototype.toWkt = function () {
      if (this.exteriorRing.length === 0)
          return this._getWktType(Types.wkt.Polygon, true);
  
      return this._getWktType(Types.wkt.Polygon, false) + this._toInnerWkt();
  };
  
  Polygon.prototype._toInnerWkt = function () {
      var innerWkt = '((';
  
      for (var i = 0; i < this.exteriorRing.length; i++)
          innerWkt += this._getWktCoordinate(this.exteriorRing[i]) + ',';
  
      innerWkt = innerWkt.slice(0, -1);
      innerWkt += ')';
  
      for (i = 0; i < this.interiorRings.length; i++) {
          innerWkt += ',(';
  
          for (var j = 0; j < this.interiorRings[i].length; j++) {
              innerWkt += this._getWktCoordinate(this.interiorRings[i][j]) + ',';
          }
  
          innerWkt = innerWkt.slice(0, -1);
          innerWkt += ')';
      }
  
      innerWkt += ')';
  
      return innerWkt;
  };
  
  Polygon.prototype.toWkb = function (parentOptions) {
      var wkb = new BinaryWriter(this._getWkbSize());
  
      wkb.writeInt8(1);
  
      this._writeWkbType(wkb, Types.wkb.Polygon, parentOptions);
  
      if (this.exteriorRing.length > 0) {
          wkb.writeUInt32LE(1 + this.interiorRings.length);
          wkb.writeUInt32LE(this.exteriorRing.length);
      }
      else {
          wkb.writeUInt32LE(0);
      }
  
      for (var i = 0; i < this.exteriorRing.length; i++)
          this.exteriorRing[i]._writeWkbPoint(wkb);
  
      for (i = 0; i < this.interiorRings.length; i++) {
          wkb.writeUInt32LE(this.interiorRings[i].length);
  
          for (var j = 0; j < this.interiorRings[i].length; j++)
              this.interiorRings[i][j]._writeWkbPoint(wkb);
      }
  
      return wkb.buffer;
  };
  
  Polygon.prototype.toTwkb = function () {
      var twkb = new BinaryWriter(0, true);
  
      var precision = Geometry.getTwkbPrecision(5, 0, 0);
      var isEmpty = this.exteriorRing.length === 0;
  
      this._writeTwkbHeader(twkb, Types.wkb.Polygon, precision, isEmpty);
  
      if (this.exteriorRing.length > 0) {
          twkb.writeVarInt(1 + this.interiorRings.length);
  
          twkb.writeVarInt(this.exteriorRing.length);
  
          var previousPoint = new Point(0, 0, 0, 0);
          for (var i = 0; i < this.exteriorRing.length; i++)
              this.exteriorRing[i]._writeTwkbPoint(twkb, precision, previousPoint);
  
          for (i = 0; i < this.interiorRings.length; i++) {
              twkb.writeVarInt(this.interiorRings[i].length);
  
              for (var j = 0; j < this.interiorRings[i].length; j++)
                  this.interiorRings[i][j]._writeTwkbPoint(twkb, precision, previousPoint);
          }
      }
  
      return twkb.buffer;
  };
  
  Polygon.prototype._getWkbSize = function () {
      var coordinateSize = 16;
  
      if (this.hasZ)
          coordinateSize += 8;
      if (this.hasM)
          coordinateSize += 8;
  
      var size = 1 + 4 + 4;
  
      if (this.exteriorRing.length > 0)
          size += 4 + (this.exteriorRing.length * coordinateSize);
  
      for (var i = 0; i < this.interiorRings.length; i++)
          size += 4 + (this.interiorRings[i].length * coordinateSize);
  
      return size;
  };
  
  Polygon.prototype.toGeoJSON = function (options) {
      var geoJSON = Geometry.prototype.toGeoJSON.call(this, options);
      geoJSON.type = Types.geoJSON.Polygon;
      geoJSON.coordinates = [];
  
      if (this.exteriorRing.length > 0) {
          var exteriorRing = [];
  
          for (var i = 0; i < this.exteriorRing.length; i++) {
              if (this.hasZ)
                  exteriorRing.push([this.exteriorRing[i].x, this.exteriorRing[i].y, this.exteriorRing[i].z]);
              else
                  exteriorRing.push([this.exteriorRing[i].x, this.exteriorRing[i].y]);
          }
  
          geoJSON.coordinates.push(exteriorRing);
      }
  
      for (var j = 0; j < this.interiorRings.length; j++) {
          var interiorRing = [];
  
          for (var k = 0; k < this.interiorRings[j].length; k++) {
              if (this.hasZ)
                  interiorRing.push([this.interiorRings[j][k].x, this.interiorRings[j][k].y, this.interiorRings[j][k].z]);
              else
                  interiorRing.push([this.interiorRings[j][k].x, this.interiorRings[j][k].y]);
          }
  
          geoJSON.coordinates.push(interiorRing);
      }
  
      return geoJSON;
  };
  