var bindings = require('bindings')('groove.node');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var DB_SCALE = Math.log(10.0) * 0.05;

/* "C++ modules aren't really for doing complex things that need to be
 * strewn across multiple modules.  Just get your binding done as quick
 * as possible, get out of there, and then wrap it in JS for all the fancy stuff
 *
 * -isaacs
 */

// hi-jack some of the native methods
var bindingsCreatePlayer = bindings.createPlayer;
var bindingsCreateLoudnessDetector = bindings.createLoudnessDetector;
var bindingsCreateFingerprinter = bindings.createFingerprinter;
var bindingsCreateEncoder = bindings.createEncoder;

bindings.createPlayer = jsCreatePlayer;
bindings.createEncoder = jsCreateEncoder;
bindings.createLoudnessDetector = jsCreateLoudnessDetector;
bindings.createFingerprinter = jsCreateFingerprinter;
bindings.loudnessToReplayGain = loudnessToReplayGain;
bindings.dBToFloat = dBToFloat;
bindings.DUMMY_DEVICE = -2;

module.exports = bindings;

function jsCreateEncoder() {
  var encoder = bindingsCreateEncoder(eventCb);

  postHocInherit(encoder, EventEmitter);
  EventEmitter.call(encoder);

  return encoder;

  function eventCb() {
    encoder.emit('buffer');
  }
}

function jsCreatePlayer() {
  var player = bindingsCreatePlayer(eventCb);

  postHocInherit(player, EventEmitter);
  EventEmitter.call(player);

  return player;

  function eventCb(id) {
    switch (id) {
    case bindings._EVENT_NOWPLAYING:
      player.emit('nowplaying');
      break;
    case bindings._EVENT_BUFFERUNDERRUN:
      player.emit('bufferunderrun');
      break;
    case bindings._EVENT_DEVICEREOPENED:
      player.emit('devicereopened');
      break;
    }
  }
}

function jsCreateLoudnessDetector() {
  var detector = bindingsCreateLoudnessDetector(eventCb);

  postHocInherit(detector, EventEmitter);
  EventEmitter.call(detector);

  return detector;

  function eventCb() {
    detector.emit('info');
  }
}

function jsCreateFingerprinter() {
  var printer = bindingsCreateFingerprinter(eventCb);
  postHocInherit(printer, EventEmitter);
  EventEmitter.call(printer);

  return printer;

  function eventCb() {
    printer.emit('info');
  }
}

function postHocInherit(baseInstance, Super) {
  var baseProto = Object.getPrototypeOf(baseInstance);
  var superProto = Super.prototype;
  Object.keys(superProto).forEach(function(method) {
    if (!baseProto[method]) baseProto[method] = superProto[method];
  });
}

function clamp_rg(x) {
  if (x > 51.0) return 51.0;
  else if (x < -51.0) return -51.0;
  else return x;
}

function loudnessToReplayGain(loudness) {
  return clamp_rg(-18.0 - loudness);
}

function dBToFloat(dB) {
  return Math.exp(dB * DB_SCALE);
}
