var file = require("./file");

exports.readStream = function(filename) {
  var stream = file.readStream(filename),
      shapeType,
      readShapeType,
      read = stream.read;

  delete stream.read;

  read(100, readFileHeader);

  function readFileHeader(fileHeader) {
    stream.emit("header", {
      fileCode: fileHeader.readInt32BE(0), // TODO verify 9994
      version: fileHeader.readInt32LE(28), // TODO verify 1000
      shapeType: shapeType = fileHeader.readInt32LE(32),
      box: [fileHeader.readDoubleLE(36), fileHeader.readDoubleLE(44), fileHeader.readDoubleLE(52), fileHeader.readDoubleLE(60)]
      // TODO zMin: fileHeader.readDoubleLE(68)
      // TODO zMax: fileHeader.readDoubleLE(76)
      // TODO mMin: fileHeader.readDoubleLE(84)
      // TODO mMax: fileHeader.readDoubleLE(92)
    });
    if (!(shapeType in readShape)) return void stream.emit("error", new Error("unsupported shape type: " + shapeType));
    readShapeType = readShape[shapeType];
    read(8, readRecordHeader);
  }

  function readRecordHeader(recordHeader) {
    // TODO verify var recordNumber = recordHeader.readInt32BE(0);
    read(recordHeader.readInt32BE(4) * 2, function readRecord(record) {
      var shapeType = record.readInt32LE(0);
      stream.emit("record", shapeType ? readShapeType(record) : null);
      read(8, readRecordHeader);
    });
  }

  return stream;
};

var readShape = {
  0: readNull,
  1: readPoint,
  3: readPoly(3), // PolyLine
  5: readPoly(5), // Polygon
  8: readMultiPoint,
  11: readPoint, // PointZ
  13: readPoly(3), // PolyLineZ
  15: readPoly(5), // PolygonZ
  18: readMultiPoint // MultiPointZ
  // 21: TODO readPointM
  // 23: TODO readPolyLineM
  // 25: TODO readPolygonM
  // 28: TODO readMultiPointM
  // 31: TODO readMultiPatch
};

function readNull() {
  return null;
}

function readPoint(record) {
  var x = record.readDoubleLE(4),
      y = record.readDoubleLE(12);
  return {
    shapeType: 1,
    x: x,
    y: y
  };
}

function readPoly(shapeType) {
  return function(record) {
    var box = [record.readDoubleLE(4), record.readDoubleLE(12), record.readDoubleLE(20), record.readDoubleLE(28)],
        numParts = record.readInt32LE(36),
        numPoints = record.readInt32LE(40),
        i = 44,
        parts = [],
        points = [];
    while (numParts-- > 0) parts.push(record.readInt32LE(i)), i += 4;
    while (numPoints-- > 0) points.push([record.readDoubleLE(i), record.readDoubleLE(i + 8)]), i += 16;
    return {
      shapeType: shapeType,
      box: box,
      parts: parts,
      points: points
    };
  };
}

function readMultiPoint(record) {
  var box = [record.readDoubleLE(4), record.readDoubleLE(12), record.readDoubleLE(20), record.readDoubleLE(28)],
      numPoints = record.readInt32LE(36),
      i = 40,
      points = [];
  while (numPoints-- > 0) points.push([record.readDoubleLE(i), record.readDoubleLE(i + 8)]), i += 16;
  return {
    shapeType: 8,
    box: box,
    points: points
  };
}

@@ -2234,14 +2234,35 @@
     
-i = 44
+parts = new Array(numParts)
 ,%0A  
@@ -2272,78 +2272,119 @@
    p
-ar
+oin
 ts = 
-%5B%5D,%0A        points = %5B%5D;%0A    while (numParts-- %3E 0
+new Array(numPoints),%0A        i = 44,%0A        j;%0A    for (j = 0; j %3C numParts; ++j, i += 4
 ) parts
-.push(
+%5Bj%5D = 
 reco
@@ -2404,53 +2404,61 @@
 E(i)
-), i += 4;%0A    while (numPoints-- %3E 0
+;%0A    for (j = 0; j %3C numPoints; ++j, i += 16
 ) points
 .pus
@@ -2445,38 +2445,38 @@
  i += 16) points
-.push(
+%5Bj%5D = 
 %5Brecord.readDoub
@@ -2507,34 +2507,24 @@
 leLE(i + 8)%5D
-), i += 16
 ;%0A    return
@@ -2825,67 +2825,111 @@
     
-i = 40,%0A      points = %5B%5D;%0A  while (numPoints-- %3E 0
+points = new Array(numPoints),%0A      i = 40,%0A      j;%0A  for (j = 0; j %3C numPoints; ++j, i += 16
 ) points
 .pus
@@ -2928,14 +2928,14 @@
 ints
-.push(
+%5Bj%5D = 
 %5Brec
@@ -2986,18 +2986,8 @@
  8)%5D
-), i += 16
 ;%0A  

Optimize point reading by pre-allocating arrays.