// dtp-media-engine.js
// Copyright (C) 2022 DTP Technologies, LLC
// All Rights Reserved

'use strict';

require('dotenv').config();

const path = require('path');

const mongoose = require('mongoose');
const mediasoup = require('mediasoup');

const { SiteAsync, SiteCommon, SitePlatform, SiteLog } = require(path.join(__dirname, 'lib', 'site-lib'));

module.rootPath = __dirname;
module.pkg = require(path.join(module.rootPath, 'package.json'));
module.config = {
  component: { name: 'dtpMediaEngine', slug: 'dtp-media-engine' },
  root: module.rootPath,
  site: require(path.join(module.rootPath, 'config', 'site')),
  webRtcServer: [
    {
      protocol: 'udp',
      ip: process.env.MEDIASOUP_WEBRTC_BIND_ADDR || '127.0.0.1',
      port: process.env.MEDIASOUP_WEBRTC_BIND_PORT || 20000,
    }
  ]
};

module.log = new SiteLog(module, module.config.component);

class MediaEngineWorker extends SiteCommon {

  constructor ( ) {
    super(module, { name: 'dtpMediaWorker', slug: 'dtp-media-worker' });
    this._id = mongoose.Types.ObjectId();
  }

  async start ( ) {
    await super.start();

    try {
      this.worker = await mediasoup.createWorker({
        logLevel: 'warn',
        dtlsCertificateFile: process.env.HTTPS_SSL_CRT,
        dtlsPrivateKeyFile: process.env.HTTPS_SSL_KEY,
      });
    } catch (error) {
      throw new Error(`failed to start mediasoup worker process: ${error.message}`);
    }

    try {
      const BIND_PORT = 20000 + module.nextWorkerIdx++;
      this.webRtcServer = await this.worker.createWebRtcServer({
        listenInfos: [
          {
            protocol: 'udp',
            ip: '127.0.0.1',
            port: BIND_PORT,
          },
          {
            protocol: 'tcp',
            ip: '127.0.0.1',
            port: BIND_PORT,
          },
        ],
      });
    } catch (error) {
      throw new Error(`failed to start mediasoup WebRTC Server: ${error.message}`);
    }
  }

  async stop ( ) {
    if (this.webRtcServer && !this.webRtcServer.closed) {
      this.log.info('closing mediasoup WebRTC server');
      this.webRtcServer.close();
      delete this.webRtcServer;
    }

    if (this.worker && !this.worker.closed) {
      this.log.info('closing mediasoup worker process');
      this.worker.close();
      delete this.worker;
    }

    await super.stop();
  }
}

module.onNewWorker = async (worker) => {
  module.log.info('new worker created', { worker: worker.pid });
  worker.observer.on('close', ( ) => {
    module.log.info('worker shutting down', { worker: worker.pid });
  });

  worker.observer.on('newrouter', (router) => {
    module.log.info('new router created', { worker: worker.pid, router: router.id });
    router.observer.on('close', ( ) => {
      module.log.info('router shutting down', { worker: worker.pid, router: router.id });
    });
  });
};

module.createWorker = async ( ) => {
  const worker = new MediaEngineWorker();
  module.workers.push(worker);
  await worker.start();
};

module.shutdown = async ( ) => {
  await SiteAsync.each(module.workers, async (worker) => {
    try {
      await worker.stop();
    } catch (error) {
      module.log.error('failed to stop worker', { error });
    }
  });
};

/*
 * SERVER PROCESS INIT
 */

(async ( ) => {

  process.on('unhandledRejection', (error, p) => {
    module.log.error('Unhandled rejection', {
      error: error,
      promise: p,
      stack: error.stack
    });
  });
  
  process.on('warning', (error) => {
    module.log.alert('warning', { error });
  });
  
  process.once('SIGINT', async ( ) => {
    module.log.info('SIGINT received');
    module.log.info('requesting shutdown...');
    await module.shutdown();
    const exitCode = await SitePlatform.shutdown();
    process.nextTick(( ) => {
      process.exit(exitCode);
    });
  });
  
  process.once('SIGUSR2', async ( ) => {
    await SitePlatform.shutdown();
    process.kill(process.pid, 'SIGUSR2');
  });

  try {
    await SitePlatform.startPlatform(module);
  } catch (error) {
    module.log.error(`failed to start DTP ${module.config.component.slug} process`, { error });
    return;
  }
  
  try {
    module.log.info('registering mediasoup observer callbacks');
    mediasoup.observer.on('newworker', module.onNewWorker);

    module.log.info('creating mediasoup worker instance');

    module.nextWorkerIdx = 0;
    module.workers = [ ];

    await module.createWorker();

    module.log.info('DTP Media Engine online');
  } catch (error) {
    module.log.error('failed to start DTP Media Engine', { error });
    process.exit(-1);
  }

})();