import { client } from "@coder/ide/src/fill/client";
import { EventEmitter } from "events";
import * as nodePty from "node-pty";
import { ChildProcess } from "@coder/protocol/src/browser/command";

type nodePtyType = typeof nodePty;

/**
 * Implementation of nodePty for the browser.
 */
class Pty implements nodePty.IPty {
	private readonly emitter = new EventEmitter();
	private readonly cp: ChildProcess;

	public constructor(file: string, args: string[] | string, options: nodePty.IPtyForkOptions) {
		this.cp = client.spawn(file, Array.isArray(args) ? args : [args], {
			...options,
			tty: {
				columns: options.cols || 100,
				rows: options.rows || 100,
			},
		});
		this.on("write", (d) => this.cp.send(d));
		this.on("kill", (exitCode) => this.cp.kill(exitCode));
		this.on("resize", (cols, rows) => this.cp.resize!({ columns: cols, rows }));

		this.cp.stdout.on("data", (data) => this.emitter.emit("data", data));
		this.cp.stderr.on("data", (data) => this.emitter.emit("data", data));
		this.cp.on("exit", (code) => this.emitter.emit("exit", code));
	}

	public get pid(): number {
		return this.cp.pid!;
	}

	public get process(): string {
		return this.cp.title!;
	}

	// tslint:disable-next-line no-any
	public on(event: string, listener: (...args: any[]) => void): void {
		this.emitter.on(event, listener);
	}

	public resize(columns: number, rows: number): void {
		this.emitter.emit("resize", columns, rows);
	}

	public write(data: string): void {
		this.emitter.emit("write", data);
	}

	public kill(signal?: string): void {
		this.emitter.emit("kill", signal);
	}
}

const ptyType: nodePtyType = {
	spawn: (file: string, args: string[] | string, options: nodePty.IPtyForkOptions): nodePty.IPty => {
		return new Pty(file, args, options);
	},
};

module.exports = ptyType;