abstract class RelationHandler {
    protected relations : Array<Relation>;

    public constructor (...relations : Array <Relation>) {
        relations.forEach(relation => {
            relation.setHandler(this);
        });
        this.relations = relations;
    }

    public addRelation (relation : Relation) {
        this.relations.push(relation);
        relation.setHandler(this);
    }

    public abstract setRelation (relation : Relation, left : any, right : any, value? : any) : boolean;
}

abstract class Relation {
    public handler : RelationHandler;

    public setHandler (handler : RelationHandler) {
        this.handler = handler;
    }

    public hasHandler () {
        return this.handler != undefined;
    }

    public abstract setRelation (left : any, right : any, value? : any) : void;
    public abstract unsetRight (right : any) : void;
    public abstract unsetLeft (left : any) : void;
    public abstract getLeft (right : any) : any;
    public abstract getRight (left : any) : any;

    public static createString (obj1 : any, obj2 : any) : string {
        if (obj1 instanceof Object && obj2 instanceof Object) {
            let id1 = Relation.getId(obj1);
            let id2 = Relation.getId(obj2);
            return id1 < id2 ?
                (id1 + ";" + id2) :
                (id2 + ";" + id1);
        }
    }

    private static objectCount = 0;
    private static objectIdField = "_RELATIONINTERNALID";

    /**
     * if getId >= 0, then obj is NOT an Object
     * if getId  < 0, then obj is an Object
     * @param obj
     * @returns {number}
     */
    public static getId (obj : Object) : number {
        if (obj[Relation.objectIdField] == undefined) {
            obj[Relation.objectIdField] = Relation.objectCount++;
        }
        return obj[Relation.objectIdField];
    }
}