#include "MultiTracksPlayer.h"

static SuperpoweredAndroidAudioIO *androidAudioIo;

static bool audioProcessing(
  void *__unused clientdata,
  short int *audioIO,
  int numFrames,
  int samplerate
) {
  return ((MultiTracksPlayer *) clientdata)
    ->process(audioIO, (unsigned int) numFrames, (unsigned int) samplerate);
}

MultiTracksPlayer::MultiTracksPlayer(
  const char **filePathsArray,
  int tracksAmount,
  int samplerate,
  int buffersize
) : tracksAmount(tracksAmount) {
  tracks = (Track **) calloc((unsigned int) tracksAmount, sizeof(Track));

  timeStretching = new Superpowered::TimeStretching(samplerate, 0.5);

  audioMixer.inputGain[1] = audioMixer.inputGain[2] = 0;

  for (int i = 0; i < tracksAmount; i++) tracks[i] = new Track(samplerate, filePathsArray[i]);

  androidAudioIo = new SuperpoweredAndroidAudioIO(
    samplerate,
    buffersize,
    false,
    true,
    audioProcessing,
    this,
    -1,
    SL_ANDROID_STREAM_MEDIA
  );
}

MultiTracksPlayer::~MultiTracksPlayer() {
  delete androidAudioIo;

  for (int i = 0; i < tracksAmount; i++) delete tracks[i];
  free(tracks);
}

void MultiTracksPlayer::pause() {
  for (int i = 0; i < tracksAmount; i++) {
    tracks[i]->pause();
  }
  Superpowered::CPU::setSustainedPerformanceMode(false);
}

double MultiTracksPlayer::play() {
  tracks[0]->play();
  for (int i = 1; i < tracksAmount; i++) {
    tracks[i]->playSynchronized();
  }
  Superpowered::CPU::setSustainedPerformanceMode(true);
  return tracks[0]->getDisplayPositionMs();
}

bool MultiTracksPlayer::process(
  short int *output,
  unsigned int numberOfFrames,
  unsigned int samplerate
) {
  int i = 0;
  for (i = 0; i < tracksAmount; i++) {
    tracks[i]->outputSamplerate = samplerate;
    tracks[i]->syncMode = Superpowered::SyncMode_TempoAndBeat;
  }

  float buffer[3][numberOfFrames * 8 + 64];
  bool silence[3];

  silence[0] = silence[1] = silence[2] = true;

  for (i = tracksAmount - 1; i >= 0; i--) {
    if (tracks[i]->leftVolume > 0) {
      if (tracks[i]->rightVolume > 0) {
        if (tracks[i]->processStereo(buffer[2], !silence[2], numberOfFrames))
          silence[2] = false;
      } else if (tracks[i]->processStereo(buffer[0], !silence[0], numberOfFrames))
        silence[0] = false;
    } else if (tracks[i]->processStereo(buffer[1], !silence[1], numberOfFrames, true))
      silence[1] = false;
  }

  audioMixer.process(
    silence[0] ? nullptr : buffer[0],
    silence[1] ? nullptr : buffer[1],
    silence[2] ? nullptr : buffer[2],
    nullptr,
    buffer[0],
    numberOfFrames
  );

  silence[0] &= silence[1] && silence[2];

  timeStretching->addInput(buffer[0], (int) numberOfFrames);
  timeStretching->getOutput(buffer[0], (int) numberOfFrames);

  if (!silence[0])
    Superpowered::FloatToShortInt(buffer[0], output, numberOfFrames);
  return !silence[0];
}

void MultiTracksPlayer::setPosition(double second, bool andStop) {
  double ms = second * 1000;
  for (int i = 0; i < tracksAmount; i++) {
    tracks[i]->setPosition(ms, andStop, true, false, true);
  }
}

double MultiTracksPlayer::getBufferedEndPercent(int index) {
  if (!tracksAmount) return 0;
  if (index > -1) return tracks[index]->getBufferedEndPercent();

  double bufferedEndPercent = 0;
  for (int i = 0; i < tracksAmount; i++) {
    bufferedEndPercent += tracks[i]->getBufferedEndPercent();
  }
  return bufferedEndPercent / tracksAmount;
}

void MultiTracksPlayer::setVolume(int index, float left, float right) {
  tracks[index]->setVolume(left, right);
}

void MultiTracksPlayer::setRate(float rate) {
  for (int i = 0; i < tracksAmount; i++) {
    tracks[i]->playbackRate = rate;
  }
}

void MultiTracksPlayer::setPitchShiftCents(int pitchShiftCents) {
  timeStretching->pitchShiftCents = pitchShiftCents;
}

double MultiTracksPlayer::getDisplayPosition() {
    return tracks[0]->getDisplayPositionSeconds();
}
