// Generated by CoffeeScript 1.7.1
(function() {
  var Struct, VersionedStruct,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  Struct = require('./Struct');

  VersionedStruct = (function(_super) {
    __extends(VersionedStruct, _super);

    function VersionedStruct(type, versions) {
      this.type = type;
      this.versions = versions != null ? versions : {};
      if (typeof this.type === 'string') {
        this.versionGetter = new Function('parent', "return parent." + this.type);
        this.versionSetter = new Function('parent', 'version', "return parent." + this.type + " = version");
      }
    }

    VersionedStruct.prototype.decode = function(stream, parent, length) {
      var fields, res, _ref;
      if (length == null) {
        length = 0;
      }
      res = this._setup(stream, parent, length);
      if (typeof this.type === 'string') {
        res.version = this.versionGetter(parent);
      } else {
        res.version = this.type.decode(stream);
      }
      if (this.versions.header) {
        this._parseFields(stream, res, this.versions.header);
      }
      fields = this.versions[res.version];
      if (fields == null) {
        throw new Error("Unknown version " + res.version);
      }
      if (fields instanceof VersionedStruct) {
        return fields.decode(stream, parent);
      }
      this._parseFields(stream, res, fields);
      if ((_ref = this.process) != null) {
        _ref.call(res, stream);
      }
      return res;
    };

    VersionedStruct.prototype.size = function(val, parent, includePointers) {
      var ctx, fields, key, size, type, _ref;
      if (includePointers == null) {
        includePointers = true;
      }
      if (!val) {
        throw new Error('Not a fixed size');
      }
      ctx = {
        parent: parent,
        val: val,
        pointerSize: 0
      };
      size = 0;
      if (typeof this.type !== 'string') {
        size += this.type.size(val.version, ctx);
      }
      if (this.versions.header) {
        _ref = this.versions.header;
        for (key in _ref) {
          type = _ref[key];
          if (type.size != null) {
            size += type.size(val[key], ctx);
          }
        }
      }
      fields = this.versions[val.version];
      if (fields == null) {
        throw new Error("Unknown version " + val.version);
      }
      for (key in fields) {
        type = fields[key];
        if (type.size != null) {
          size += type.size(val[key], ctx);
        }
      }
      if (includePointers) {
        size += ctx.pointerSize;
      }
      return size;
    };

    VersionedStruct.prototype.encode = function(stream, val, parent) {
      var ctx, fields, i, key, ptr, type, _ref, _ref1;
      if ((_ref = this.preEncode) != null) {
        _ref.call(val, stream);
      }
      ctx = {
        pointers: [],
        startOffset: stream.pos,
        parent: parent,
        val: val,
        pointerSize: 0
      };
      ctx.pointerOffset = stream.pos + this.size(val, ctx, false);
      if (typeof this.type !== 'string') {
        this.type.encode(stream, val.version);
      }
      if (this.versions.header) {
        _ref1 = this.versions.header;
        for (key in _ref1) {
          type = _ref1[key];
          if (type.encode != null) {
            type.encode(stream, val[key], ctx);
          }
        }
      }
      fields = this.versions[val.version];
      for (key in fields) {
        type = fields[key];
        if (type.encode != null) {
          type.encode(stream, val[key], ctx);
        }
      }
      i = 0;
      while (i < ctx.pointers.length) {
        ptr = ctx.pointers[i++];
        ptr.type.encode(stream, ptr.val, ptr.parent);
      }
    };

    return VersionedStruct;

  })(Struct);

  module.exports = VersionedStruct;

}).call(this);
