
import { ObjectsConnector } from "core-3nweb-client-lib/build/ipc";
import { promiseClientSideW3N } from "../../ts-code/core/client-side-w3n";
import { SocketConnectInfo } from "../../ts-code/ipc-with-core/socket-ipc";

const { core } = Deno;

globalThis.Deno = undefined;

const signalByteToEventName: {
  [byteValue: number]: IpcEvent['event'];
} = {
  1: 'connected',
  2: 'data',
  3: 'error',
  4: 'exit',
  5: 'ipc-not-set',
  6: 'signal-sent'
};

type IpcNextOp = (timeoutMillis?: number) => Promise<Buffer>;
type IpcExitOp = (timeoutMillis?: number) => Promise<void>;
type IpcSendOp = (bytes: Buffer, timeoutMillis?: number) => Promise<Buffer>;

interface IpcEventWithData {
  event: 'data'|'error';
  data: Buffer;
}

type IpcEvent = {
  event: 'connected'|'exit'|'ipc-not-set'|'signal-sent';
} | IpcEventWithData;

function signalBytesToEvent(sig: Buffer): IpcEvent|undefined {
  const event = signalByteToEventName[sig[0]];
  switch (event) {
    case 'data':
      return { event, data: sig.subarray(1) };
    case 'connected':
    case 'exit':
      return { event };
    case 'error':
      throw new Error(sig.subarray(1).toString('utf8'));
    case 'ipc-not-set':
      throw new Error(`IPC is not set`);
    default: {
      passErrorAndExitIpc(`Unexpected signal byte: ${sig[0]}`);
      break;
    }
  }
}

function passErrorAndExitIpc(err: string): void {
  // XXX
  // - pass error to ts, and close ipc

  exitIpc();
}

// XXX
// - have connector here
// - connector should consume nextIpcEvents and send

async function nextIpcEvents(timeoutMillis = undefined) {
  const sig = await (core.ops.op_ipc_next as IpcNextOp)(timeoutMillis);
  return signalBytesToEvent(sig);
}

async function send(data: Buffer, timeoutMillis = undefined): Promise<void> {
  const opStatus = await (core.ops.op_ipc_send as IpcSendOp)(data, timeoutMillis);
  const status = signalByteToEventName[opStatus[0]];
  console.log(`sending opStatus is`, opStatus[0], `leading to status string`, status);
  switch (status) {
    case 'signal-sent':
      return;
    case 'error':
      throw new Error(opStatus.subarray(1).toString('utf-8'));
    case 'ipc-not-set':
      throw new Error(`IPC is not set`);
    default:
      throw new Error(`Unexpected signal byte: ${opStatus[0]}`);
  }
}

async function exitIpc(timeoutMillis = undefined): Promise<void> {
  await (core.ops.op_ipc_exit as IpcExitOp)(timeoutMillis);
}

function subscribeToIpcEventsFromCore(): { unsubscribe: () => void; } {

}

const eventsFromCore = {
  subscribe: () => {
    const unsubscribe = () => {};
    return { unsubscribe };
  }
}

const eventsFromClient = {
  next,
  error,
  complete
};

async function listCoreObjAsync(path: string[]): Promise<string[]> {
  // XXX
  throw new Error(`need implementation`);
}

globalThis.w3n = await promiseClientSideW3N(ObjectsConnector.makeClientSide(
  eventsFromClient, eventsFromCore, undefined, listCoreObjAsync
));
