if (typeof(org) === 'undefined') org = {};
if (typeof(org.ziniki) === 'undefined') org.ziniki = {};

org.ziniki.Domain = function(_cxt) {
  this._cxt = _cxt;
  this.state = _cxt.fields();
  this.state.set('_type', 'org.ziniki.Domain');
}

function Entity() {
}
Entity.prototype._field_id = function(cx, args) {
  return this.state.get('_id');
}
Entity.prototype._field_id.nfargs = function() {
  return 0;
}

function ContainerDataStore(_cxt) {
  console.log("created a container datastore service");
}

ContainerDataStore.prototype.subscribe = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.subscribe.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.domain = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.domain.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.put = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.put.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.attachPersonalArena = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.attachPersonalArena.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.createArena = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.createArena.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.addBench = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.addBench.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.join = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.join.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.publish = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.publish.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.my = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.my.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.putMy = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.putMy.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.attachSecondaryKeyset = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.attachSecondaryKeyset.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.attachSecondaryIndex = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.attachSecondaryIndex.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.secondaryByType = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.secondaryByType.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.secondaryKey = function(...args) {
  this.ensureServer(args[0]);
  this.serverSvc.secondaryKey.apply(this.serverSvc, args);
}

ContainerDataStore.prototype.secondaryIndex = function(cx, arena, indexName, fields, ch) {
  this.ensureServer(cx);
  var cuh = new OnCrobagUpdate(cx, this, ch);
  this.serverSvc.secondaryIndex.call(this.serverSvc, cx, arena, indexName, fields, cuh);
}

function OnCrobagUpdate(_cxt, cds, ch) {
  org.ziniki.CrobagUpdateHandler.call(this, _cxt);
  this.cds = cds;
  this.ch = ch;
  this.card = ch._card;
}

function PendingUpdate(_cxt, ocu, key) {
  org.ziniki.DataHandler.call(this, _cxt);
  this.ocu = ocu;
  this.key = key;
}

function _define_handlers() {
  OnCrobagUpdate.prototype = new org.ziniki.CrobagUpdateHandler();
  OnCrobagUpdate.prototype.constructor = OnCrobagUpdate;
  PendingUpdate.prototype = new org.ziniki.DataHandler();
  PendingUpdate.prototype.constructor = PendingUpdate;

  OnCrobagUpdate.prototype.missing = function(...args) {
    return this.ch.missing.apply(this.ch, args);
  }

  OnCrobagUpdate.prototype.present = function(cx, crobag, cb) {
    this.crobag = crobag;
    this.presentCB = cb;
    this.crobag._card = this.card;
    var pending = [];
    for (var i=0;i<crobag._entries.length;i++) {
      var ce = crobag._entries[i];
      if (ce.val instanceof ZiIdURI) {
        var pu = new PendingUpdate(cx, this, ce.key);
        this.cds.subscribe(cx, ce.val, pu);
        pending.push(pu);
      }
    }
    this.cancelPending();
    if (pending.length == 0)
      return this.ch.present.call(this.ch, cx, crobag, cb);
    else {
      this.pending = pending;
      return null;
    }
  }

  OnCrobagUpdate.prototype.cancelPending = function() {
    this.pending = null;
    this.changes = [];
  }

  OnCrobagUpdate.prototype.addkey = function(cx, key, eid, ih) {
    if (eid instanceof ZiIdURI) {
      var pu = new PendingUpdate(cx, this, key);
      this.cds.subscribe(cx, eid, pu);
      this.changes.push(pu);
      return null;
    } else {
      var ret = this.crobag.insert(cx, key, eid);
      return [ret, new UpdatesDisplay(cx, this.card)];
    }
  }

  OnCrobagUpdate.prototype.removekey = function(...args) {
    debugger;
  }

  OnCrobagUpdate.prototype._clz = function() {
    return 'org.ziniki.CrobagUpdateHandler';
  }
  
  OnCrobagUpdate.prototype._clz.nfargs = function() { return -1; }

  OnCrobagUpdate.prototype.getDone = function(cx, pu) {
    if (this.pending) {
      for (var i=0;i<this.pending.length;i++) {
        if (this.pending[i] == pu) {
          this.pending.splice(i, 1);
          if (this.pending.length == 0) {
            cx.env.queueMessages(cx, this.ch.present(cx, this.crobag, this.presentCB));
            cx.env.queueMessages(cx, new UpdateDisplay(cx, this.card));
            this.pending = null;
          }
          return;
        }
      }
    }
    for (var i=0;i<this.changes.length;i++) {
      if (this.changes[i] == pu) {
        this.changes.splice(i, 1);
        cx.env.queueMessages(cx, new UpdateDisplay(cx, this.card));
        return;
      }
    }
    cx.log("ignoring", pu, "as it was not found");
}

  PendingUpdate.prototype._clz = function() {
    return 'org.ziniki.DataHandler';
  }
  PendingUpdate.prototype._clz.nfargs = function() { return -1; }

  PendingUpdate.prototype.missing = function(cx, key, ih) {
    this.ocu.getDone(cx, this);
    return null;
  }
  PendingUpdate.prototype.missing.nfargs = function() { return 1; }
  PendingUpdate.prototype.value = function(cx, val, ih) {
    this.ocu.crobag._change(cx, "put", this.key, null, val);
    this.ocu.getDone(cx, this);
    return null;
  }
  PendingUpdate.prototype.value.nfargs = function() { return 1; }
}

ContainerDataStore.prototype.ensureServer = function(cx) {
  if (this.serverSvc == null) {
    var svc = cx.broker.server.unmarshalContract("org.ziniki.DataStore");
    this.serverSvc = new MarshallerProxy(cx.broker.logger, cx.broker.contracts["org.ziniki.DataStore"], svc).proxy
  }
}

const ZinikiSecurityModule = function() {
}

ZinikiSecurityModule.prototype.requireLogin = function(_cxt, appl, div) {
  if (_cxt.broker.awaitingServerConnection()) {
		// we can't do anything yet that needs the server, so put a login button in the div
		const login = document.createElement("button");
		login.innerText = "Log In";
		login.onclick = function() {
			window.haveit = function(token, secret) {
				env.broker.updateConnection(zinikiServer + '/' + token + '/' + secret);
				div.innerHTML = '';
        appl.nowLoggedIn(env.newContext());
			}
			window.open(zinikiLogin);
		}
		div.appendChild(login);
    return false;
	} else {
		return true;
	}
}

org.ziniki._builtin_init = function(_cxt) {
  _define_handlers();
  _cxt.registerStruct("org.ziniki.Domain", org.ziniki.Domain);
  _cxt.broker.register("org.ziniki.DataStore", new ContainerDataStore(_cxt));
}