{"size": 3383, "ext": "js", "max_stars_repo_path": "src/layers/udp/index.js", "max_stars_repo_name": "jmmoser/node-drivers", "max_stars_repo_stars_event_min_datetime": "2017-05-29T02:33:48.000Z", "max_stars_repo_stars_event_max_datetime": "2022-02-22T12:11:25.000Z", "max_issues_repo_path": "src/layers/udp/index.js", "max_issues_repo_name": "jmmoser/node-drivers", "max_issues_repo_issues_event_min_datetime": "2017-10-31T20:04:57.000Z", "max_issues_repo_issues_event_max_datetime": "2022-01-18T23:09:30.000Z", "max_forks_repo_path": "src/layers/udp/index.js", "max_forks_repo_name": "jmmoser/node-drivers", "max_forks_repo_forks_event_min_datetime": "2017-10-31T19:49:41.000Z", "max_forks_repo_forks_event_max_datetime": "2022-02-22T11:59:06.000Z"}
{"max_stars_count": 20, "max_issues_count": 6, "max_forks_count": 6, "avg_line_length": 22.1111111111, "max_line_length": 85, "alphanum_fraction": 0.560744901}
'use strict';

const dgram = require('dgram');
const Layer = require('../Layer');
const { CallbackPromise } = require('../../utils');

async function setup(layer) {
  /** Only await layer._settingUp if not null */
  if (layer._settingUp != null) {
    await layer._settingUp;
  }

  if (layer._listening === true) {
    return;
  }

  layer._settingUp = new Promise((resolve) => {
    const socket = dgram.createSocket(layer.options.type || 'udp4', (data, info) => {
      layer.emit('data', data, info);
      layer.forward(data, info);
    });

    socket.once('listening', () => {
      layer._listening = true;
      socket.setBroadcast(layer.options.broadcast);
      resolve(true);
      layer.sendNextMessage();
    });

    socket.once('error', (err) => {
      console.log('UDP layer Error:');
      console.log(err);
      layer._listening = false;
      resolve(false);
      layer.destroy(err);
    });

    socket.once('close', () => {
      layer._listening = false;
      resolve(false);
    });

    socket.bind(layer.options.listen);

    layer.socket = socket;
  });
}

function removeSocketListeners(socket) {
  ['error', 'listening'].forEach((eventName) => {
    socket.removeAllListeners(eventName);
  });
}

class UDPLayer extends Layer {
  constructor(options) {
    super('udp');

    this.options = {
      target: {
        host: '127.0.0.1',
        // port: 0
      },
      listen: {
        address: null,
        port: 0,
      },
      broadcast: true,
    };

    if (options) {
      if (typeof options === 'string') {
        this.options.target.host = options;
      }

      if (options.broadcast != null) {
        this.options.broadcast = !!options.broadcast;
      }

      if (options.host) {
        this.options.target.host = options.host;
      }

      if (options.port) {
        this.options.target.port = options.port;
      }

      if (typeof options.target === 'object') {
        this.options.target = options.target;
      }
      if (typeof options.listen === 'object') {
        this.options.listen = options.listen;
      }
    }

    setup(this);
  }

  handleDefaultOptions(defaultOptions /* , layer */) {
    if (
      this.options.target.port == null
      && defaultOptions.target
      && defaultOptions.target.port != null
    ) {
      this.options.target.port = defaultOptions.target.port;
    }
  }

  sendNextMessage() {
    if (this._listening) {
      const request = this.getNextRequest();
      if (request) {
        const { message, info } = request;
        const { target } = this.options;

        const port = info.port || target.port;
        const host = info.host || target.host;

        // console.log(`UDPLayer sending to ${host}:${port}`);

        this.socket.send(message, port, host, (err) => {
          if (err) {
            console.log('UDPLayer Send Error:');
            console.log(err);
          }
        });

        setImmediate(() => this.sendNextMessage());
      }
    } else {
      setup(this);
    }
  }

  disconnect(callback) {
    return CallbackPromise(callback, (resolver) => {
      this._listening = false;
      this.socket.close(resolver.resolve);
    });
  }

  handleDestroy() {
    this._listening = false;
    if (this.socket) {
      this.socket.unref();
      removeSocketListeners(this.socket);
      this.socket = null;
    }
  }
}

module.exports = UDPLayer;
