import * as LogAbstract from "@terrencecrowley/logabstract";
import * as Context from "@terrencecrowley/context";

interface TestEnv { context: Context.IContext, log: LogAbstract.ILog };

import * as OT from "../lib/ottypes";
import * as OTC from "../lib/otcomposite";
import * as OTS from "../lib/otsession";
import * as OTEngine from "../lib/otserverengine";
import * as OTTestClient from "./ottestclient";

export class OTTestServer
{
	private env: TestEnv;
	private engine: OTEngine.OTServerEngine;
	private clients: OTTestClient.OTTestClient[];
	private editQueue: OTC.OTCompositeResource[];

	// construct
	constructor(env: TestEnv)
		{
			this.env = env;
			this.engine = new OTEngine.OTServerEngine(env.log, "1");
			this.clients = [];
			this.editQueue = [];
		}

	addClient(client: OTTestClient.OTTestClient): void
		{
			this.clients.push(client);
			client.setServer(this);
		}

	receive(edit: OTC.OTCompositeResource): void
		{
			this.editQueue.push(edit.copy());
		}

	private findClient(uid: string): OTTestClient.OTTestClient
		{
			for (let i: number = 0; i < this.clients.length; i++)
				if (this.clients[i].clientID === uid)
					return this.clients[i];
			return null;
		}

	tick(): void
		{
			// Drain the queue of received client events
			while (this.editQueue.length > 0)
			{
				let edit: OTC.OTCompositeResource = this.editQueue.shift();
				let nResult: number = this.engine.addServer(edit);

				if (nResult === OTS.ESuccess)
				{
					edit = this.engine.logServer[this.engine.logServer.length-1];
					for (let i: number = 0; i < this.clients.length; i++)
						this.clients[i].receiveRemoteAction(edit.copy());
				}
				else
				{
					// Clock Unavailable
					if (nResult === OTS.EClockFailure)
						console.log("Clock failure for client " + edit.clientID);
					else if (nResult === OTS.EClockSeen)
						console.log("Sequence failure for client " + edit.clientID);
					edit.clock = nResult;
					let c: OTTestClient.OTTestClient = this.findClient(edit.clientID);
					if (c)
						c.receiveRemoteAction(edit.copy());
				}
			}
		}

	drain(): void
		{
			let bCont: boolean = true;

			while (bCont)
			{
				// Dispatch any events from queue
				this.tick();

				// And then give clients chance to respond.
				for (let i: number = 0; i < this.clients.length; i++)
					this.clients[i].checkForSend();

				// And continue if they responded.
				bCont = this.editQueue.length > 0;
			}
		}

	toValue(): any
		{
			return this.engine.toValue();
		}
};
