// Generated by CoffeeScript 1.8.0
(function() {
  var ProtoBuf, Scan, async, builder, debug, getFilter, proto, utils,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

  debug = (require('debug'))('hbase-client');

  utils = require('./utils');

  async = require('async');

  ProtoBuf = require("protobufjs");

  ProtoBuf.convertFieldsToCamelCase = true;

  builder = ProtoBuf.loadProtoFile("" + __dirname + "/../proto/Filter.proto");

  proto = builder.build();

  module.exports.getFilter = getFilter = function(filter) {
    var FilterList, filterList, filterName, filterNameUpper, o, serialized;
    FilterList = require('./filter-list');
    if (filter instanceof FilterList) {
      filterList = new proto.FilterList(filter.get());
      o = {
        name: 'org.apache.hadoop.hbase.filter.FilterList',
        serializedFilter: filterList.encode()
      };
      return o;
    }
    filterName = Object.keys(filter)[0];
    filterNameUpper = "" + (filterName[0].toUpperCase()) + filterName.slice(1);
    if (!proto[filterNameUpper]) {
      throw new Error("Invalid filter " + filterNameUpper);
    }
    if (filterNameUpper === 'SingleColumnValueFilter') {
      filter[filterName].comparator = getFilter(filter[filterName].comparator);
    }
    o = {
      name: "org.apache.hadoop.hbase.filter." + filterNameUpper
    };
    serialized = 'serializedFilter';
    if (filterNameUpper.indexOf('Comparator') >= 0) {
      serialized = 'serializedComparator';
    }
    o[serialized] = new proto[filterNameUpper](filter[filterName]).encode();
    return o;
  };

  module.exports.Scan = Scan = (function() {
    function Scan(table, startRow, stopRow, client) {
      this.table = table;
      this.startRow = startRow;
      this.stopRow = stopRow;
      this.client = client;
      this.toArray = __bind(this.toArray, this);
      this.each = __bind(this.each, this);
      this.close = __bind(this.close, this);
      this.next = __bind(this.next, this);
      this.getServerAndLocation = __bind(this.getServerAndLocation, this);
      this._processResponse = __bind(this._processResponse, this);
      this._getData = __bind(this._getData, this);
      this.setFilter = __bind(this.setFilter, this);
      this.closed = false;
      this.numCached = 100;
      this.cached = [];
      this.server = null;
      this.location = null;
      this.timeout = null;
      this.row = 0;
    }

    Scan.prototype.setFilter = function(filter) {
      this.filter = getFilter(filter);
      if (!this.filter) {
        return false;
      }
      return true;
    };

    Scan.prototype._getData = function(nextStartRow, cb) {
      if (this.closed) {
        return cb(null, {});
      }
      return this.getServerAndLocation(this.table, nextStartRow, (function(_this) {
        return function(err, server, location) {
          var region, req;
          _this.server = server;
          _this.location = location;
          if (err) {
            return cb(err);
          }
          req = {
            region: {
              type: "REGION_NAME",
              value: _this.location.name
            },
            numberOfRows: _this.numCached,
            scan: {}
          };
          if (_this.scannerId) {
            req.scannerId = _this.scannerId;
          } else if (_this.startRow && _this.stopRow) {
            req.scan = {
              startRow: _this.startRow,
              stopRow: _this.stopRow
            };
          }
          if (_this.filter) {
            req.scan.filter = _this.filter;
          }
          _this.row++;
          region = _this.location.name.toString();
          debug("scan on: " + _this.table + " row: " + _this.row + " region: " + region + " startRow: " + _this.startRow + " stopRow: " + _this.stopRow);
          return _this.server.rpc.Scan(req, function(err, response) {
            if (err) {
              return cb(err);
            }
            return _this._processResponse(response, cb);
          });
        };
      })(this));
    };

    Scan.prototype._processResponse = function(response, cb) {
      var len, nextRegion;
      nextRegion = true;
      this.nextStartRow = null;
      this.scannerId = response.scannerId;
      if (this.timeout) {
        clearTimeout(this.timeout);
      }
      this.timeout = setTimeout(this.close, response.ttl);
      len = response.results.length;
      if (len === this.numCached) {
        nextRegion = false;
      }
      if (this.location.endKey.length === 0) {
        nextRegion = false;
      }
      if (this.stopRow && utils.bufferCompare(this.location.endKey, new Buffer(this.stopRow)) > 0 && len !== this.numCached) {
        nextRegion = false;
      }
      if (len < this.numCached) {
        this.nextStartRow = utils.bufferIncrement(this.location.endKey);
        this.nextStartRow = this.nextStartRow.toString();
      }
      if (nextRegion) {
        this.closeScan(this.server, this.location, this.scannerId);
        this.server = this.location = this.scannerId = null;
        if (len === 0) {
          return this._getData(this.nextStartRow, cb);
        }
      }
      this.cached = response.results.map((function(_this) {
        return function(result) {
          return _this.client._parseResponse(result);
        };
      })(this));
      if (this.cached.length === 0) {
        this.close();
      }
      return cb();
    };

    Scan.prototype.getServerAndLocation = function(table, startRow, cb) {
      if (this.server && this.location) {
        return cb(null, this.server, this.location);
      }
      return this.client.locateRegion(table, startRow, (function(_this) {
        return function(err, location) {
          if (err) {
            return cb(err);
          }
          return _this.client.getRegionConnection(location.server.toString(), function(err, server) {
            if (err) {
              return cb(err);
            }
            return cb(null, server, location);
          });
        };
      })(this));
    };

    Scan.prototype.next = function(cb) {
      var startRow;
      if (this.cached.length) {
        return cb(null, (this.cached.splice(0, 1))[0]);
      }
      startRow = this.nextStartRow;
      if (startRow == null) {
        startRow = this.startRow;
      }
      return this._getData(startRow, (function(_this) {
        return function(err) {
          if (err) {
            return cb(err);
          }
          if (!_this.cached.length) {
            return cb(null, {});
          }
          return cb(null, (_this.cached.splice(0, 1))[0]);
        };
      })(this));
    };

    Scan.prototype.closeScan = function(server, location, scannerId) {
      var req;
      if (!location) {
        return;
      }
      req = {
        region: {
          type: "REGION_NAME",
          value: location.name
        },
        closeScanner: true,
        scannerId: scannerId
      };
      return server.rpc.Scan(req, function(err, response) {});
    };

    Scan.prototype.close = function() {
      if (this.closed) {
        return;
      }
      this.closeScan(this.server, this.location, this.scannerId);
      return this.closed = true;
    };

    Scan.prototype.each = function(f, cb) {
      var work;
      work = true;
      return async.whilst(function() {
        return work;
      }, (function(_this) {
        return function(done) {
          return _this.next(function(err, row) {
            if (err) {
              return done(err);
            }
            if (!row.row) {
              work = false;
              return process.nextTick(done);
            }
            if (f.length === 3) {
              return f(null, row, done);
            }
            f(null, row);
            return process.nextTick(done);
          });
        };
      })(this), function(err) {
        if (cb) {
          return cb(err);
        }
        return f(err);
      });
    };

    Scan.prototype.toArray = function(cb) {
      var out;
      out = [];
      return this.each(function(err, row) {
        if (!row) {
          return cb(err, out);
        }
        return out.push(row);
      });
    };

    return Scan;

  })();

}).call(this);
