///
import * as rx from "rx";
import {IAPINavIm} from "../API";
import {MyGraph, Node, TilesService} from "../Graph";
interface IGraphOperation extends Function {
(myGraph: MyGraph): MyGraph;
}
export class GraphService {
public updates: rx.Subject = new rx.Subject();
public cache: rx.Subject = new rx.Subject();
public cachedNode: rx.Observable;
public graph: rx.Observable;
public tilesService: TilesService;
private prisitine: boolean;
constructor (clientId: string) {
this.prisitine = true;
this.tilesService = new TilesService(clientId);
// operation pattern updating the graph
this.graph = this.updates
.scan(
(myGraph: MyGraph, operation: IGraphOperation): MyGraph => {
let newMyGraph: MyGraph = operation(myGraph);
newMyGraph.evictNodeCache();
return newMyGraph;
},
new MyGraph())
.shareReplay(1);
// stream of cached nodes, uses distinct to not cache a node more than once
this.cachedNode = this.cache.distinct((node: Node): string => {
return node.key + node.lastCacheEvict;
}).flatMap((node: Node): rx.Observable => {
return node.cacheAssets();
});
// make tilesservice aware of that a new node is beeing cached
this.cachedNode.subscribe(this.tilesService.cacheNode);
// save the cached node to the graph, cache its edges
this.cachedNode.map((node: Node) => {
return (myGraph: MyGraph): MyGraph => {
myGraph.cacheNode(node);
return myGraph;
};
}).subscribe(this.updates);
// feedback from tiles service adding fresh tiles to the graph
this.tilesService.tiles.map((data: IAPINavIm): IGraphOperation => {
return (myGraph: MyGraph): MyGraph => {
myGraph.addNodesFromAPI(data);
return myGraph;
};
}).subscribe(this.updates);
}
public getNode(key: string, cacheEdges: boolean = true): rx.Observable {
let ret: rx.Observable = this.graph.skipWhile((myGraph: MyGraph) => {
let node: Node = myGraph.getNode(key);
if (node == null || !node.worthy) {
this.tilesService.cacheIm.onNext(key);
return true;
}
if (!node.cached) {
this.cache.onNext(node);
return true;
}
return false;
}).map((myGraph: MyGraph): Node => {
return myGraph.getNode(key);
});
// hack to start of the whole graph fetching process, a better trigger is needed
if (this.prisitine) {
this.tilesService.cacheIm.onNext(key);
this.prisitine = false;
}
return ret;
}
public getNextNode(node: Node, dir: number): rx.Observable {
if (!node.cached) {
rx.Observable.throw(new Error("node is not yet cached"));
}
// go find the next node
return this.graph.map((myGraph: MyGraph): string => {
let nextNode: Node = myGraph.nextNode(node, dir);
if (nextNode == null) {
return null;
}
return nextNode.key;
}).distinct().flatMap((key: string): rx.Observable => {
if (key == null) {
return rx.Observable.throw(new Error("there is no node in that direction"));
}
return this.getNode(key);
});
}
}
export default GraphService;