// Generated by IcedCoffeeScript 1.7.1-b
(function() {
  var C, Packer, PpBuffer, U32MAX, is_array, is_int, u64max_minus_i;

  C = require('./const').C;

  PpBuffer = require('./buffer').PpBuffer;

  U32MAX = require('./util').U32MAX;

  is_array = function(x) {
    return Object.prototype.toString.call(x) === '[object Array]';
  };

  is_int = function(f) {
    return Math.floor(f) === f;
  };

  u64max_minus_i = function(i) {
    var a, b, x, y;
    x = Math.floor(i / U32MAX);
    y = i % U32MAX;
    a = U32MAX - x - (y > 0 ? 1 : 0);
    b = y === 0 ? 0 : U32MAX - y;
    return [a, b];
  };

  exports.Packer = Packer = (function() {
    function Packer(_opts) {
      this._opts = _opts != null ? _opts : {};
      this._buffer = new PpBuffer();
    }

    Packer.prototype.output = function() {
      return this._buffer.freeze();
    };

    Packer.prototype.p = function(o) {
      switch (typeof o) {
        case 'number':
          return this.p_number(o);
        case 'string':
          return this.p_str(o);
        case 'boolean':
          return this.p_boolean(o);
        case 'undefined':
          return this.p_null();
        case 'object':
          if (o == null) {
            return this.p_null();
          } else if (is_array(o)) {
            return this.p_array(o);
          } else if (PpBuffer.isBuffer(o)) {
            return this.p_bin(o);
          } else if (!this.p_ext(o)) {
            return this.p_obj(o);
          }
      }
    };

    Packer.prototype.p_number = function(n) {
      if (!is_int(n)) {
        return this.p_pack_double(n);
      } else if (n >= 0) {
        return this.p_positive_int(n);
      } else {
        return this.p_negative_int(n);
      }
    };

    Packer.prototype.p_pack_double = function(d) {
      if (this._opts.floats != null) {
        this.p_uint8(C.float);
        return this._buffer.push_float32(d);
      } else {
        this.p_uint8(C.double);
        return this._buffer.push_float64(d);
      }
    };

    Packer.prototype.p_uint8 = function(b) {
      return this._buffer.push_uint8(b);
    };

    Packer.prototype.p_uint16 = function(s) {
      return this._buffer.push_uint16(s);
    };

    Packer.prototype.p_uint32 = function(w) {
      return this._buffer.push_uint32(w);
    };

    Packer.prototype.p_int8 = function(b) {
      return this._buffer.push_int8(b);
    };

    Packer.prototype.p_int16 = function(s) {
      return this._buffer.push_int16(s);
    };

    Packer.prototype.p_int32 = function(w) {
      return this._buffer.push_int32(w);
    };

    Packer.prototype.p_neg_int64 = function(i) {
      var a, abs_i, b, _ref;
      abs_i = 0 - i;
      _ref = u64max_minus_i(abs_i), a = _ref[0], b = _ref[1];
      this.p_uint32(a);
      return this.p_uint32(b);
    };

    Packer.prototype.p_boolean = function(b) {
      return this.p_uint8(b ? C["true"] : C["false"]);
    };

    Packer.prototype.p_null = function() {
      return this.p_uint8(C["null"]);
    };

    Packer.prototype.p_array = function(a) {
      var e, _i, _len, _results;
      this.p_len(a.length, C.fix_array_min, C.fix_array_max, C.array16, C.array32);
      _results = [];
      for (_i = 0, _len = a.length; _i < _len; _i++) {
        e = a[_i];
        _results.push(this.p(e));
      }
      return _results;
    };

    Packer.prototype.p_obj = function(o) {
      var k, keys, n, _i, _len, _results;
      keys = Object.keys(o);
      n = keys.length;
      this.p_len(n, C.fix_map_min, C.fix_map_max, C.map16, C.map32);
      if (this._opts.sort_keys) {
        keys.sort();
      }
      _results = [];
      for (_i = 0, _len = keys.length; _i < _len; _i++) {
        k = keys[_i];
        this.p(k);
        _results.push(this.p(o[k]));
      }
      return _results;
    };

    Packer.prototype.p_positive_int = function(i) {
      if (i <= 0x7f) {
        return this.p_uint8(i);
      } else if (i <= 0xff) {
        this.p_uint8(C.uint8);
        return this.p_uint8(i);
      } else if (i <= 0xffff) {
        this.p_uint8(C.uint16);
        return this.p_uint16(i);
      } else if (i < U32MAX) {
        this.p_uint8(C.uint32);
        return this.p_uint32(i);
      } else {
        this.p_uint8(C.uint64);
        this.p_uint32(Math.floor(i / U32MAX));
        return this.p_uint32(i % U32MAX);
      }
    };

    Packer.prototype.p_negative_int = function(i) {
      if (i >= -32) {
        return this.p_int8(i);
      } else if (i >= -128) {
        this.p_uint8(C.int8);
        return this.p_int8(i);
      } else if (i >= -32768) {
        this.p_uint8(C.int16);
        return this.p_int16(i);
      } else if (i >= -2147483648) {
        this.p_uint8(C.int32);
        return this.p_int32(i);
      } else {
        this.p_uint8(C.int64);
        return this.p_neg_int64(i);
      }
    };

    Packer.prototype.p_buffer = function(b) {
      return this._buffer.push_buffer(b);
    };

    Packer.prototype.p_bin = function(r) {
      this.p_len(r.length, null, null, C.bin16, C.bin32, C.bin8);
      return this.p_buffer(r);
    };

    Packer.prototype.p_str = function(s) {
      var b, str8;
      b = this._buffer.prepare_utf8(s);
      str8 = this._opts.no_str8 ? null : C.str8;
      this.p_len(b.length, C.fix_str_min, C.fix_str_max, C.str16, C.str32, str8);
      return this.p_buffer(b);
    };

    Packer.prototype.p_len = function(l, fixmin, fixmax, m, b, s) {
      if (s == null) {
        s = null;
      }
      if ((fixmin != null) && (fixmax != null) && l <= (fixmax - fixmin)) {
        return this.p_uint8(l | fixmin);
      } else if ((s != null) && l <= 0xff) {
        this.p_uint8(s);
        return this.p_uint8(l);
      } else if (l <= 0xffff) {
        this.p_uint8(m);
        return this.p_uint16(l);
      } else {
        this.p_uint8(b);
        return this.p_uint32(l);
      }
    };

    Packer.prototype.p_ext = function(o) {
      var buf, l, ret, type;
      if (this._opts.ext == null) {
        return false;
      } else if (!(ret = this._opts.ext(o))) {
        return false;
      } else {
        type = ret[0], buf = ret[1];
        switch ((l = buf.length)) {
          case 1:
            this.p_uint8(C.fix_ext1);
            break;
          case 2:
            this.p_uint8(C.fix_ext2);
            break;
          case 4:
            this.p_uint8(C.fix_ext4);
            break;
          case 8:
            this.p_uint8(C.fix_ext8);
            break;
          case 16:
            this.p_uint8(C.fix_ext16);
            break;
          default:
            if (l <= 0xff) {
              this.p_uint8(C.ext8);
              this.p_uint8(l);
            } else if (l <= 0xffff) {
              this.p_uint8(C.ext16);
              this.p_uint16(l);
            } else {
              this.p_uint8(C.ext32);
              this.p_uint32(l);
            }
        }
        this.p_uint8(type);
        this.p_buffer(buf);
        return true;
      }
    };

    return Packer;

  })();

  exports.pack = function(x, opts) {
    var packer;
    if (opts == null) {
      opts = {};
    }
    packer = new Packer(opts);
    packer.p(x);
    return packer.output();
  };

}).call(this);
