#import "MultiTracksPlayer.h"

@implementation MultiTracksPlayer {
    SuperpoweredIOSAudioIO *output;
    NSArray *tracks;
    Superpowered::StereoMixer *stereoMixer;
    Superpowered::TimeStretching *timeStretching;
}

static bool audioProcessing(void *clientdata, float **inputBuffers, unsigned int inputChannels, float **outputBuffers, unsigned int outputChannels, unsigned int numberOfFrames, unsigned int samplerate, uint64_t hostTime) {
    __unsafe_unretained MultiTracksPlayer *self = (__bridge MultiTracksPlayer *) clientdata;
    return [self audioProcessing:outputBuffers[0] right:outputBuffers[1] numFrames:numberOfFrames samplerate:samplerate];
}

- (bool)audioProcessing:(float *)leftOutput right:(float *)rightOutput numFrames:(unsigned int)numberOfFrames samplerate:(unsigned int)samplerate {
    for (Track *t in tracks) {
        [t setSyncMode:Superpowered::SyncMode_TempoAndBeat];
        [t setOutputSamplerate:samplerate];
    }

    unsigned int numberOfInputFramesNeeded = (unsigned int) (timeStretching->rate * (float) numberOfFrames);

    Float32 buffer[3][numberOfInputFramesNeeded * 8 + 64];
    bool silence[3];
    silence[0] = silence[1] = silence[2] = true;

    for (Track *t in tracks) {
        if (t.leftVolume > 0) {
            if (t.rightVolume > 0) {
                if ([t processLeft:buffer[2] withMixed:!silence[2] withNumberOfFrames:numberOfInputFramesNeeded])
                    silence[2] = false;
            } else if ([t processStereo:buffer[2] withMixed:!silence[2] withNumberOfFrames:numberOfInputFramesNeeded])
                silence[0] = false;
        } else if ([t processRight:buffer[2] withMixed:!silence[2] withNumberOfFrames:numberOfInputFramesNeeded])
            silence[1] = false;
    }

    stereoMixer->process(silence[0] ? nullptr : buffer[0],
            silence[1] ? nullptr : buffer[1],
            silence[2] ? nullptr : buffer[2],
            nullptr,
            buffer[0],
            numberOfInputFramesNeeded);

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

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

    if (!silence[0]) Superpowered::DeInterleave(buffer[0], leftOutput, rightOutput, numberOfFrames);
    return !silence[0];
}

- (id)initWithTracksURLs:(NSArray *)tracksURLs {
    self = [super init];

    if (self) {
        unsigned int samplerate = 44100;

        Superpowered::AdvancedAudioPlayer::setTempFolder([NSTemporaryDirectory() cStringUsingEncoding:NSUTF8StringEncoding]);

        stereoMixer = new Superpowered::StereoMixer();
        stereoMixer->inputGain[1] = stereoMixer->inputGain[2] = 0;

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

        NSMutableArray *tracksMutableArray = [[NSMutableArray alloc] init];
        for (NSString *trackURL in tracksURLs) {
            Track *t = [[Track alloc] initWithUri:trackURL withSamplerate:samplerate];
            [tracksMutableArray addObject:t];
        }
        tracks = [tracksMutableArray copy];

        output = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id <SuperpoweredIOSAudioIODelegate>) self preferredBufferSize:12 preferredSamplerate:44100 audioSessionCategory:AVAudioSessionCategoryPlayback channels:2 audioProcessingCallback:audioProcessing clientdata:(__bridge void *) self];
        [output start];
    }

    return self;
}

- (void)dealloc {
    [output stop];
}

- (void)play {
    Track *metronomeTrack = [tracks firstObject];
    [metronomeTrack play];
    for (Track *t in tracks) {
        if (![t isEqual:metronomeTrack]) {
            [t playSynchronized];
        }
    }
}

- (void)pause {
    for (Track *t in tracks) {
        [t pause];
    }
}

- (int)getDisplayPosition {
    Track *firstTrack = [tracks firstObject];
    return [firstTrack getDisplayPositionSeconds];
}

- (void)setPosition:(double)second andStop:(bool)andStop {
    double ms = second * 1000;
    for (Track *t in tracks) {
        [t setPosition:ms andStop:andStop];
    }
}

- (Float32)getBufferedPercentage {
    Float32 acumulator = 0.0f;
    for (Track *t in tracks) {
        acumulator += [t getBufferedEndPercent];
    }
    Float32 tracksAmount = [[NSNumber numberWithUnsignedInteger:[tracks count]] floatValue];
    return acumulator / tracksAmount;
}

- (Float32)getBufferedIndexPercentage:(unsigned int)index {
    return [tracks[index] getBufferedEndPercent];
}

- (void)setVolume:(unsigned int)index left:(float)left right:(float)right {
    Track *t = [tracks objectAtIndex:index];
    [t setVolume:left volumeRight:right];
}

- (void)setRate:(float)rate {
    timeStretching->rate = rate;
}

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


@end

