import events from 'events';
import Promise from 'bluebird';
import beof from 'beof';
import Reference from './Reference';
import Context from './Context';
import Concern from './Concern';
import Signal from './Signal';
import Address from './Address';
import RefState from './RefState';
import MemoryStateProvider from './MemoryStateProvider';
import ReferenceProvider from './ReferenceProvider';
const FakeBus = {
on: function() {},
emit: function() {}
}
class ConcernFactory {
/**
* create a Concern
* @param {Context} context
*/
create(context) {
}
}
/**
* System represents a collection of related Concerns that share a parent Context.
* Use them to create to represent the root of a tree your application will
* branch into.
* @param {Context} parent
* @implements {Context}
*/
class System {
constructor(name = 'main', parent = null) {
beof({ name }).string();
beof({ parent }).optional().interface(Context);
this._parent = parent;
this.name = `/${name}`;
this._refs = [];
this._bus = FakeBus;
this._memStateProvider = new MemoryStateProvider();
this._protocols = {};
}
/**
* create a new System
* @param {string} name
* @returns {System}
*/
static create(name) {
return (new System(name)).setEventBus(new events.EventEmitter());
}
/**
* setEventBus allows the EventEmitter to be set.
* @param {events.EventEmitter} bus
*/
setEventBus(bus) {
beof({ bus }).instance(events.EventEmitter);
this._bus = bus;
return this;
}
/**
* setReferenceProvider allows a provider to be configured for
* a protocol.
* @param {string} protocol
* @param {ReferenceProvider} provider
*/
setReferenceProvider(protocol, provider) {
beof({ protocol }).string();
beof({ provider }).interface(ReferenceProvider);
}
path() {
return this.name;
}
subscribe(event, handler) {
beof({ event }).function();
beof({ handler }).function();
if(this._parent)
return this._parent.subscribe(event);
this._bus.on(event.name, handler);
return this;
}
publish(event) {
beof({ event }).object();
if(this._parent)
return this._parent.publish(event);
this._bus.emit(event.constructor.name, event);
}
select(path) {
beof({ path }).string();
var address = Address.fromString(path);
var refs = this._refs.slice();
var parent = this.parent;
var next = ref => {
if (!ref) {
if (parent)
return parent.select(address);
if (this._protocols.hasOwnProperty(address.protocol)) {
var r = this._protocols[address.protocol].select(path, this);
this._refs.push(r);
return;
}
return new Reference(this._memStateProvider.provide(RefState.UNKNOWN_STATE,
path, new Concern(), this));
}
if (address.is(ref.path()))
return ref;
if (address.isBelow(ref.path()))
return ref.select(address);
return next(refs.pop());
}
return next(refs.pop());
}
/**
* concernOf considers a Concern part of this system when it activates.
* @param {System.ConcernFactory} factory
* @param {string} name
* @returns {Reference}
*/
concernOf(factory, name) {
beof({ factory }).interface(ConcernFactory);
beof({ name }).string();
var ctx = new System(name, this);
var ref = new Reference(
this._memStateProvider.provide(RefState.READY_STATE,
`${this.name}/${name}`,
factory.create(ctx), ctx));
ctx.setEventBus(this._bus);
this._refs.push(ref);
ref.accept(new Signal.Start());
return ref;
}
}
System.ConcernFactory = ConcernFactory;
export default System;