const JSONRPC = {};
JSONRPC.ClientPluginBase = require("../../ClientPluginBase");
JSONRPC.Utils = require("../../Utils");

const ChildProcess = require("child_process");

module.exports =
class ProcessStdIOTransport extends JSONRPC.ClientPluginBase
{
	/**
	 * @param {string} strEndpointCommand
	 * @param {string} strWorkingDirectoryPath
	 */
	constructor(strEndpointCommand, strWorkingDirectoryPath)
	{
		super();
		
		this._strEndpointCommand = strEndpointCommand;
		this._strWorkingDirectoryPath = strWorkingDirectoryPath;
	}


	/**
	 * Populates the OutgoingRequest class instance (outgoingRequest) with the RAW JSON response and the JSON parsed response object.
	 * 
	 * @param {JSONRPC.OutgoingRequest} outgoingRequest
	 * 
	 * @returns {Promise.<null>}
	 */
	async makeRequest(outgoingRequest)
	{
		if(outgoingRequest.isMethodCalled)
		{
			return;
		}

		outgoingRequest.isMethodCalled = true;
		

		const objExecOptions = {
			cwd: this._strWorkingDirectoryPath,
			maxBuffer: 10 * 1024 * 1024
		};

		const strExePath = this._strEndpointCommand.trim().split(/[\s]+/, 1)[0];
		const strArguments = this._strEndpointCommand.substr(strExePath.length).trim();
		
		const child = ChildProcess.spawn(strExePath, [strArguments], objExecOptions);

		if(!this.constructor._objSpawnedChildren)
		{
			this.constructor._objSpawnedChildren = {};
		}

		this.constructor._objSpawnedChildren[child.pid] = child;

		return new Promise((fnResolve, fnReject) => {
			child.on(
				"close", 
				(code) => {
					child.stdin.end();
					delete this.constructor._objSpawnedChildren[child.pid];

					fnResolve(null);
				}
			);

			outgoingRequest.responseBody = "";
			child.stdout.on(
				"data", 
				(data) => {
					outgoingRequest.responseBody += data;
				}
			);

			function errorHandler(error)
			{
				// Stream errors sometimes don't have a proper stacktrace or enough information to know where they are coming from.
				console.error(outgoingRequest);
				console.error(error);

				delete this.constructor._objSpawnedChildren[child.pid];
				
				fnReject(error);
			}
			
			child.on("error", errorHandler);
			child.stdin.on("error", errorHandler);
			child.stdout.on("error", errorHandler);

			child.stdin.setEncoding("utf-8");
			child.stdin.write(outgoingRequest.requestBody);
		});
	}

	/**
	 * Collection of running spawned child processes.
	 * 
	 * @returns {Object} _objSpawnedChildren
	 */
	static get spawnedChildren()
	{
		return this._objSpawnedChildren;
	}
};


@@ -538,16 +538,120 @@
 ryPath;%0D
+%0A%0D%0A%09%09if(!this.constructor._objSpawnedChildren)%0D%0A%09%09%7B%0D%0A%09%09%09this.constructor._objSpawnedChildren = %7B%7D;%0D%0A%09%09%7D%0D
 %0A%09%7D%0D%0A%0D%0A%0D
@@ -1401,112 +1401,8 @@
 %0D%0A%0D%0A
-%09%09if(!this.constructor._objSpawnedChildren)%0D%0A%09%09%7B%0D%0A%09%09%09this.constructor._objSpawnedChildren = %7B%7D;%0D%0A%09%09%7D%0D%0A%0D%0A
 %09%09th
@@ -2472,16 +2472,21 @@
  %7BObject
+%7Cnull
 %7D _objSp
@@ -2574,16 +2574,50 @@
 Children
+ ? this._objSpawnedChildren : null
 ;%0D%0A%09%7D%0D%0A%7D

initialize static member in constructor