/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <assert.h>
#include <iostream>
#include <time.h>

#include "functionTest.h"
#include "event_wrapper.h"
#include "trace.h"
#include "thread_wrapper.h"
#include "webrtc_vad.h"

#if (defined(WEBRTC_LINUX) || defined(WEBRTC_MAC))
   #include <sys/stat.h>
   #define MY_PERMISSION_MASK S_IRWXU | S_IRWXG | S_IRWXO
   #define MKDIR(directory) mkdir(directory,MY_PERMISSION_MASK)
#else // defined(WINDOWS)
   #include <direct.h>
   #define MKDIR(directory) mkdir(directory)
#endif

int main(int /*argc*/, char* /*argv[]*/)
{
    // Initialize random number generator
    //unsigned int seed = 1220716312; // just a seed that can be used
    unsigned int seed = (unsigned)time( NULL );
    srand(seed);
    std::cout << "Starting function test. Seed = " << seed << std::endl;
    std::cout << "Press enter to continue" << std::endl;
    getchar();
    MixerWrapper* testInstance1 = MixerWrapper::CreateMixerWrapper();
    MixerWrapper* testInstance2 = MixerWrapper::CreateMixerWrapper();
    if((testInstance1 == NULL) ||
       (testInstance2 == NULL))
    {
        assert(false);
        return 0;
    }

    char versionString[256] = "";
    WebRtc_UWord32 remainingBufferInBytes = 256;
    WebRtc_UWord32 position = 0;
    AudioConferenceMixer::GetVersion(versionString,remainingBufferInBytes,position);

    int read = 1;
    while(read != 0)
    {
        std::cout << versionString << std::endl;
        std::cout << "--------Menu-----------" << std::endl;
        std::cout << std::endl;
        std::cout << "0. Quit" << std::endl;
        std::cout << "2. StartMixing" << std::endl;
        std::cout << "3. StopMixing" << std::endl;
        std::cout << "4. Create participant(s)" << std::endl;
        std::cout << "5. Delete participant(s)" << std::endl;
        std::cout << "6. List participants " << std::endl;
        std::cout << "7. Print mix status " << std::endl;
        std::cout << "8. Run identical scenario:" << std::endl;
        std::cout << "   a. 1 VIP,       3 regular, amount of mixed = 3"  << std::endl;
        std::cout << "   b. 1 anonymous, 3 regular, amount of mixed = 2"  << std::endl;
        scanf("%i",&read);
        getchar();
        MixerParticipant::ParticipantType participantType;
        int option = 0;
        WebRtc_UWord32 id = 0;
        ListItem* item = NULL;
        ListWrapper participants;
        if(read == 0)
        {
            // donothing
        }
        else if(read == 1)
        {
        }
        else if(read == 2)
        {
            testInstance1->StartMixing();
        }
        else if(read == 3)
        {
            testInstance1->StopMixing();
        }
        else if(read == 4)
        {
            while(true)
            {
                std::cout << "VIP(music)       = " << MixerParticipant::VIP << std::endl;
                std::cout << "Regular(speech)  = " << MixerParticipant::REGULAR << std::endl;
                std::cout << "Anonymous(music) = " << MixerParticipant::MIXED_ANONYMOUS << std::endl;
                std::cout << "Select type of participant: ";
                scanf("%i",&option);
                if(option == MixerParticipant::VIP ||
                   option == MixerParticipant::REGULAR ||
                   option == MixerParticipant::MIXED_ANONYMOUS)
                {
                    break;
                }
            }
            participantType = (MixerParticipant::ParticipantType)option;
            testInstance1->CreateParticipant(participantType);
        }
        else if(read == 5)
        {
            std::cout << "Select participant to delete: ";
            scanf("%i",&option);
            id = option;
            testInstance1->DeleteParticipant(id);
            break;
        }
        else if(read == 6)
        {
            testInstance1->GetParticipantList(participants);
            item = participants.First();
            std::cout << "The following participants have been created: " << std::endl;
            while(item)
            {
                WebRtc_UWord32 id = item->GetUnsignedItem();
                std::cout << id;
                item = participants.Next(item);
                if(item != NULL)
                {
                    std::cout << ", ";
                }
                else
                {
                    std::cout << std::endl;
                }
            }
        }
        else if(read == 7)
        {
            std::cout << "-------------Mixer Status-------------" << std::endl;
            testInstance1->PrintStatus();
            testInstance2->PrintStatus();
            std::cout << "Press enter to continue";
            getchar();
            std::cout << std::endl;
            std::cout << std::endl;
        }
        else if(read == 8)
        {
            const WebRtc_Word32 amountOfParticipants = 4;
            MixerParticipant::ParticipantType instance1Participants[] =
                                                {MixerParticipant::VIP,
                                                 MixerParticipant::REGULAR,
                                                 MixerParticipant::REGULAR,
                                                 MixerParticipant::REGULAR};
            MixerParticipant::ParticipantType instance2Participants[] =
                                               {MixerParticipant::MIXED_ANONYMOUS,
                                                MixerParticipant::REGULAR,
                                                MixerParticipant::REGULAR,
                                                MixerParticipant::REGULAR};
            for(WebRtc_Word32 i = 0; i < amountOfParticipants; i++)
            {
                WebRtc_Word32 startPosition = 0;
                GenerateRandomPosition(startPosition);
                testInstance1->CreateParticipant(instance1Participants[i],startPosition);
                testInstance2->CreateParticipant(instance2Participants[i],startPosition);
            }
            bool success = true;
            success = testInstance1->StartMixing();
            assert(success);
            success = testInstance2->StartMixing(2);
            assert(success);
        }
    }

    std::cout << "Press enter to stop" << std::endl;
    getchar();
    delete testInstance1;
    delete testInstance2;
    return 0;
}

FileWriter::FileWriter()
    :
    _file(NULL)
{
}

FileWriter::~FileWriter()
{
    if(_file)
    {
        fclose(_file);
    }
}

bool
FileWriter::SetFileName(
    const char* fileName)
{
    if(_file)
    {
        fclose(_file);
    }
    _file = fopen(fileName,"wb");
    return _file != NULL;
}

bool
FileWriter::WriteToFile(
    const AudioFrame& audioFrame)
{
    WebRtc_Word32 written = (WebRtc_Word32)fwrite(audioFrame._payloadData,sizeof(WebRtc_Word16),audioFrame._payloadDataLengthInSamples,_file);
    // Do not flush buffers since that will add (a lot of) delay
    return written == audioFrame._payloadDataLengthInSamples;
}

FileReader::FileReader()
    :
    _frequency(kDefaultFrequency),
    _sampleSize((_frequency*kProcessPeriodicityInMs)/1000),
    _timeStamp(0),
    _file(NULL),
    _vadInstr(NULL),
    _automaticVad(false),
    _vad(false)
{
    if(WebRtcVad_Create(&_vadInstr) == 0)
    {
        if(WebRtcVad_Init(_vadInstr) != 0)
        {
            assert(false);
            WebRtcVad_Free(_vadInstr);
            _vadInstr = NULL;
        }
    }
    else
    {
        assert(false);
    }
}

FileReader::~FileReader()
{
    if(_file)
    {
        fclose(_file);
    }
    if(_vadInstr)
    {
        WebRtcVad_Free(_vadInstr);
    }
}

bool
FileReader::SetFileName(
    const char* fileName)
{
    if(_file)
    {
        fclose(_file);
    }
    _file = fopen(fileName,"rb");
    return _file != NULL;
}

bool
FileReader::ReadFromFile(
    AudioFrame& audioFrame)
{

    WebRtc_Word16 buffer[AudioFrame::kMaxAudioFrameSizeSamples];
    LoopedFileRead(buffer,AudioFrame::kMaxAudioFrameSizeSamples,_sampleSize,_file);

    bool vad = false;
    GetVAD(buffer,_sampleSize,vad);
    AudioFrame::VADActivity activity = vad ? AudioFrame::kVadActive :
                                 AudioFrame::kVadPassive;

    _volumeCalculator.ComputeLevel(buffer,_sampleSize);
    const WebRtc_Word32 level = _volumeCalculator.GetLevel();
    return audioFrame.UpdateFrame(  -1,
                                    _timeStamp,
                                    buffer,
                                    _sampleSize,
                                    _frequency,
                                    AudioFrame::kNormalSpeech,
                                    activity,
                                    0,
                                    level) == 0;

}

bool
FileReader::FastForwardFile(
    const WebRtc_Word32 samples)
{
    WebRtc_Word16* tempBuffer = new WebRtc_Word16[samples];
    bool success = LoopedFileRead(tempBuffer,samples,samples,_file);
    delete[] tempBuffer;
    return success;
}

bool
FileReader::EnableAutomaticVAD(
    bool enable,
    int mode)
{
    if(!_automaticVad &&
       enable)
    {
        if(WebRtcVad_Init(_vadInstr) == -1)
        {
            return false;
        }
    }
    WebRtcVad_set_mode(_vadInstr,mode);
    _automaticVad = enable;
    return true;
}

bool
FileReader::SetVAD(
    bool vad)
{
    if(_automaticVad)
    {
        return false;
    }
    _vad = vad;
    return true;
}

bool
FileReader::GetVAD(
    WebRtc_Word16* buffer,
    WebRtc_UWord8 bufferLengthInSamples,
    bool& vad)
{
    if(_automaticVad)
    {
        WebRtc_Word16 result = WebRtcVad_Process(_vadInstr,_frequency,buffer,bufferLengthInSamples);
        if(result == -1)
        {
            assert(false);
            return false;
        }
        _vad = vad = (result == 1);
    }
    vad = _vad;
    return true;
}

MixerParticipant*
MixerParticipant::CreateParticipant(
    const WebRtc_UWord32 id,
    ParticipantType participantType,
    const WebRtc_Word32 startPosition,
    char* outputPath)
{
    if(participantType == RANDOM)
    {
        participantType = (ParticipantType)(rand() % 3);
    }
    MixerParticipant* participant = new MixerParticipant(id,participantType);
    // Randomize the start position so we only need one input file
    // assume file is smaller than 1 minute wideband = 60 * 16000
    // Always start at a multiple of 10ms wideband
    if(!participant->InitializeFileReader(startPosition) ||
       !participant->InitializeFileWriter(outputPath))
    {
        delete participant;
        return NULL;
    }
    return participant;
}

MixerParticipant::MixerParticipant(
    const WebRtc_UWord32 id,
    ParticipantType participantType)
    :
    _id(id),
    _participantType(participantType),
    _fileReader(),
    _fileWriter()
{
}

MixerParticipant::~MixerParticipant()
{
}

WebRtc_Word32
MixerParticipant::GetAudioFrame(
    const WebRtc_Word32 /*id*/,
    AudioFrame& audioFrame)
{
    if(!_fileReader.ReadFromFile(audioFrame))
    {
        return -1;
    }
    audioFrame._id = _id;
    return 0;
}

WebRtc_Word32
MixerParticipant::MixedAudioFrame(
    const AudioFrame& audioFrame)
{
    return _fileWriter.WriteToFile(audioFrame);
}

WebRtc_Word32
MixerParticipant::GetParticipantType(
    ParticipantType& participantType)
{
    participantType = _participantType;
    return 0;
}

bool
MixerParticipant::InitializeFileReader(
    const WebRtc_Word32 startPositionInSamples)
{
    char fileName[128] = "";
    if(_participantType == REGULAR)
    {
        sprintf(fileName,"convFile.pcm");
    }
    else
    {
        sprintf(fileName,"musicFile.pcm");
    }
    if(!_fileReader.SetFileName(fileName))
    {
        return false;
    }
    if(!_fileReader.EnableAutomaticVAD(true,2))
    {
        assert(false);
    }
    return _fileReader.FastForwardFile(startPositionInSamples);
}

bool
MixerParticipant::InitializeFileWriter(
    char* outputPath)
{
    const WebRtc_Word32 stringsize = 128;
    char fileName[stringsize] = "";
    strncpy(fileName,outputPath,stringsize);
    fileName[stringsize-1] = '\0';

    char tempName[stringsize];
    tempName[0] = '\0';
    sprintf(tempName,"outputFile%d.pcm",(int)_id);
    strncat(fileName,tempName,(stringsize - strlen(fileName)));
    fileName[stringsize-1] = '\0';

    return _fileWriter.SetFileName(fileName);
}

StatusReceiver::StatusReceiver(
    const WebRtc_Word32 id)
    :
    _id(id),
    _mixedParticipants(NULL),
    _mixedParticipantsAmount(0),
    _mixedParticipantsSize(0),
    _vadPositiveParticipants(NULL),
    _vadPositiveParticipantsAmount(0),
    _vadPositiveParticipantsSize(0),
    _mixedAudioLevel(0)
{
}

StatusReceiver::~StatusReceiver()
{
    delete[] _mixedParticipants;
    delete[] _vadPositiveParticipants;
}

void
StatusReceiver::MixedParticipants(
    const WebRtc_Word32 id,
    const ParticipantStatistics* participantStatistics,
    const WebRtc_UWord32 size)
{
    if(id != _id)
    {
        assert(false);
    }
    if(_mixedParticipantsSize < size)
    {
        delete[] _mixedParticipants;
        _mixedParticipantsSize = size;
        _mixedParticipants = new ParticipantStatistics[size];
    }
    _mixedParticipantsAmount = size;
    memcpy(_mixedParticipants,participantStatistics,sizeof(ParticipantStatistics)*size);
}

void
StatusReceiver::VADPositiveParticipants(
    const WebRtc_Word32 id,
    const ParticipantStatistics* participantStatistics,
    const WebRtc_UWord32 size)
{
    if(id != _id)
    {
        assert(false);
    }

    if(_vadPositiveParticipantsSize < size)
    {
        delete[] _vadPositiveParticipants;
        _vadPositiveParticipantsSize = size;
        _vadPositiveParticipants = new ParticipantStatistics[size];
    }
    _vadPositiveParticipantsAmount = size;
    memcpy(_vadPositiveParticipants,participantStatistics,sizeof(ParticipantStatistics)*size);
}

void
StatusReceiver::MixedAudioLevel(
    const WebRtc_Word32  id,
    const WebRtc_UWord32 level)
{
    if(id != _id)
    {
        assert(false);
    }
    _mixedAudioLevel = level;
}

void
StatusReceiver::PrintMixedParticipants()
{
    std::cout << "Mixed participants" << std::endl;
    if(_mixedParticipantsAmount == 0)
    {
        std::cout << "N/A" << std::endl;
    }
    for(WebRtc_UWord16 i = 0; i < _mixedParticipantsAmount; i++)
    {
        std::cout << i + 1 << ". Participant " << _mixedParticipants[i].participant << ": level = " << _mixedParticipants[i].level << std::endl;
    }
}

void
StatusReceiver::PrintVadPositiveParticipants()
{
    std::cout << "VAD positive participants" << std::endl;
    if(_mixedParticipantsAmount == 0)
    {
        std::cout << "N/A"  << std::endl;
    }
    for(WebRtc_UWord16 i = 0; i < _mixedParticipantsAmount; i++)
    {
        std::cout << i + 1 << ". Participant " << _mixedParticipants[i].participant << ": level = " << _mixedParticipants[i].level << std::endl;
    }
}

void
StatusReceiver::PrintMixedAudioLevel()
{
    std::cout << "Mixed audio level = " << _mixedAudioLevel << std::endl;
}

WebRtc_Word32 MixerWrapper::_mixerWrapperIdCounter = 0;

MixerWrapper::MixerWrapper()
    :
    _processThread(NULL),
    _threadId(0),
    _firstProcessCall(true),
    _previousTime(),
    _periodicityInTicks(TickTime::MillisecondsToTicks(FileReader::kProcessPeriodicityInMs)),
    _synchronizationEvent(EventWrapper::Create()),
    _freeItemIds(),
    _itemIdCounter(0),
    _mixerParticipants(),
    _mixerWrappererId(_mixerWrapperIdCounter++),
    _instanceOutputPath(),
    _trace(NULL),
    _statusReceiver(_mixerWrappererId),
    _generalAudioWriter()
{
    sprintf(_instanceOutputPath,"instance%d/",(int)_mixerWrappererId);
    MKDIR(_instanceOutputPath);
    _mixer = AudioConferenceMixer::CreateAudioConferenceMixer(
                                                    _mixerWrappererId);
    if(_mixer != NULL)
    {
        bool success = true;

        success = _mixer->RegisterMixedStreamCallback(*this) == 0;
        assert(success);
        success = _mixer->RegisterMixedStreamCallback(*this) == -1;
        assert(success);
        success = _mixer->UnRegisterMixedStreamCallback() == 0;
        assert(success);
        success = _mixer->UnRegisterMixedStreamCallback() == -1;
        assert(success);
        success = _mixer->RegisterMixedStreamCallback(*this) == 0;
        assert(success);

        success = _mixer->RegisterMixerStatusCallback(_statusReceiver,2) == 0;
        assert(success);
        success = _mixer->RegisterMixerStatusCallback(_statusReceiver,1) == -1;
        assert(success);
        success = _mixer->UnRegisterMixerStatusCallback() == 0;
        assert(success);
        success = _mixer->UnRegisterMixerStatusCallback() == -1;
        assert(success);
        success = _mixer->RegisterMixerStatusCallback(_statusReceiver,1) == 0;
        assert(success);
    }
    else
    {
        assert(false);
        std::cout << "Failed to create mixer instance";
    }
}

MixerWrapper*
MixerWrapper::CreateMixerWrapper()
{
    MixerWrapper* mixerWrapper = new MixerWrapper();
    if(!mixerWrapper->InitializeFileWriter())
    {
        delete mixerWrapper;
        return NULL;
    }
    return mixerWrapper;
}

MixerWrapper::~MixerWrapper()
{
    StopMixing();
    ClearAllItemIds();
    _synchronizationEvent->StopTimer();
    delete _synchronizationEvent;
    delete _mixer;
}

bool
MixerWrapper::CreateParticipant(
    MixerParticipant::ParticipantType participantType)
{
    WebRtc_Word32 startPosition = 0;
    GenerateRandomPosition(startPosition);
    return CreateParticipant(participantType,startPosition);
}

bool
MixerWrapper::CreateParticipant(
    MixerParticipant::ParticipantType participantType,
    const WebRtc_Word32 startPosition)
{
    WebRtc_UWord32 id;
    if(!GetFreeItemIds(id))
    {
        return false;
    }

    MixerParticipant* participant = MixerParticipant::CreateParticipant(id,participantType,startPosition,_instanceOutputPath);
    if(!participant)
    {
        return false;
    }
    if(_mixerParticipants.Insert(id,static_cast<void*>(participant)) != 0)
    {
        delete participant;
        return false;
    }
    if(!StartMixingParticipant(id))
    {
        DeleteParticipant(id);
        return false;
    }
    return true;
}

bool
MixerWrapper::DeleteParticipant(
    const WebRtc_UWord32 id)
{
    bool success = StopMixingParticipant(id);
    if(!success)
    {
        assert(false);
        return false;
    }
    MapItem* item = _mixerParticipants.Find(id);
    if(item == NULL)
    {
        return false;
    }
    MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
    delete participant;
    _mixerParticipants.Erase(item);
    AddFreeItemIds(id);
    return true;
}

bool
MixerWrapper::StartMixing(
    const WebRtc_UWord32 mixedParticipants)
{
    if(_processThread)
    {
        return false;
    }
    if(_mixer->SetAmountOfMixedParticipants(mixedParticipants) != 0)
    {
        assert(false);
    }
    WebRtc_UWord32 mixedParticipantsTest = 0;
    _mixer->AmountOfMixedParticipants(mixedParticipantsTest);
    assert(mixedParticipantsTest == mixedParticipants);

    if(!_synchronizationEvent->StartTimer(true,10))
    {
        assert(false);
        return false;
    }
    _processThread = ThreadWrapper::CreateThread(Process, this, kLowPriority);
    if(!_processThread->Start(_threadId))
    {
        delete _processThread;
        _processThread = NULL;
        assert(false);
        return false;
    }

    return true;
}

bool
MixerWrapper::StopMixing()
{
    while(_processThread &&
          !_processThread->Stop())
    {}
    _synchronizationEvent->StopTimer();

    delete _processThread;
    _processThread = NULL;
    return true;
}

void
MixerWrapper::NewMixedAudio(
    const WebRtc_Word32 id,
    const AudioFrame& generalAudioFrame,
    const AudioFrame** uniqueAudioFrames,
    const WebRtc_UWord32 size)
{
    if(id < 0)
    {
        assert(false);
    }
    // Store the general audio
    _generalAudioWriter.WriteToFile(generalAudioFrame);

    // Send the unique audio frames to its corresponding participants
    ListWrapper uniqueAudioFrameList;
    for(WebRtc_UWord32 i = 0; i < size; i++)
    {
        WebRtc_UWord32 id = (uniqueAudioFrames[i])->_id;
        MapItem* resultItem = _mixerParticipants.Find(id);
        if(resultItem == NULL)
        {
            assert(false);
            continue;
        }
        MixerParticipant* participant = static_cast<MixerParticipant*>(resultItem->GetItem());
        participant->MixedAudioFrame(*(uniqueAudioFrames[i]));
        uniqueAudioFrameList.PushBack(resultItem->GetItem());
    }

    // Send the general audio frames to the remaining participants
    MapItem* item = _mixerParticipants.First();
    while(item)
    {
        bool isUnique = false;
        ListItem* compareItem = uniqueAudioFrameList.First();
        while(compareItem)
        {
            if(compareItem->GetItem() == item->GetItem())
            {
                isUnique = true;
                break;
            }
            compareItem = uniqueAudioFrameList.Next(compareItem);
        }
        if(!isUnique)
        {
            MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
            participant->MixedAudioFrame(generalAudioFrame);
        }
        item = _mixerParticipants.Next(item);
    }
}

bool
MixerWrapper::GetParticipantList(
    ListWrapper& participants)
{
    MapItem* item = _mixerParticipants.First();
    while(item)
    {
        participants.PushBack(item->GetId());
        item = _mixerParticipants.Next(item);
    }
    return true;
}

void
MixerWrapper::PrintStatus()
{
    std::cout << "instance " << _mixerWrappererId << std::endl;
    std::cout << std::endl;
    _statusReceiver.PrintMixedParticipants();
    std::cout << std::endl;
    _statusReceiver.PrintVadPositiveParticipants();
    std::cout << std::endl;
    _statusReceiver.PrintMixedAudioLevel();
    std::cout << "---------------------------------------" << std::endl;
}

bool
MixerWrapper::InitializeFileWriter()
{
    const WebRtc_Word32 stringsize = 128;
    char fileName[stringsize] = "";
    strncpy(fileName,_instanceOutputPath,stringsize);
    fileName[stringsize-1] = '\0';

    strncat(fileName,"generalOutputFile.pcm",(stringsize - strlen(fileName)));
    fileName[stringsize-1] = '\0';
    return _generalAudioWriter.SetFileName(fileName);
}

bool
MixerWrapper::Process(
    void* instance)
{
    MixerWrapper* mixerWrapper = static_cast<MixerWrapper*>(instance);
    return mixerWrapper->Process();
}

bool
MixerWrapper::Process()
{
    switch(_synchronizationEvent->Wait(1000))
    {
    case kEventSignaled:
         // Normal operation, ~10 ms has passed
        break;
    case kEventError:
        // Error occured end the thread and throw an assertion
        assert(false);
        return false;
    case kEventTimeout:
        // One second has passed without a timeout something is wrong
        // end the thread and throw an assertion
        assert(false);
        return false;
    }
    WebRtc_Word32 processOfset = 0;
    const TickTime currentTime = TickTime::Now();
    if(_firstProcessCall)
    {
        _previousTime = TickTime::Now();
        _firstProcessCall = false;
    }
    else
    {
        TickInterval deltaTime = (currentTime - _previousTime);
        _previousTime += _periodicityInTicks;
        processOfset = (WebRtc_Word32) deltaTime.Milliseconds();
        processOfset -= FileReader::kProcessPeriodicityInMs;
    }

    _mixer->Process();
    WebRtc_Word32 timeUntilNextProcess = _mixer->TimeUntilNextProcess();
    if(processOfset > FileReader::kProcessPeriodicityInMs)
    {
        std::cout << "Performance Warning: Process running " << processOfset << " too slow" << std::endl;
        _previousTime = currentTime;
        if(timeUntilNextProcess > 0)
        {
            std::cout << "Performance Warning: test performance and module performance missmatch" << std::endl;
        }
    }
    else if(processOfset < -FileReader::kProcessPeriodicityInMs)
    {
        std::cout << "Performance Warning: Process running " << -processOfset << " too fast" << std::endl;
        _previousTime = currentTime;
        if(timeUntilNextProcess < FileReader::kProcessPeriodicityInMs)
        {
            std::cout << "Performance Warning: test performance and module performance missmatch" << std::endl;
        }
    }
    return true;
}


bool
MixerWrapper::StartMixingParticipant(
    const WebRtc_UWord32 id)
{
    MapItem* item = _mixerParticipants.Find(id);
    if(item == NULL)
    {
        return false;
    }
    MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
    MixerParticipant::ParticipantType participantType = MixerParticipant::REGULAR;
    participant->GetParticipantType(participantType);
    if(participantType == MixerParticipant::MIXED_ANONYMOUS)
    {
        bool anonymouslyMixed = false;
        bool success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == 0;
        assert(success);
        success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0;
        assert(success);
        assert(anonymouslyMixed);
        success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == -1;
        assert(success);
        success = _mixer->SetAnonymousMixabilityStatus(*participant,false) == 0;
        assert(success);
        success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0;
        assert(success);
        assert(!anonymouslyMixed);
        success = _mixer->SetAnonymousMixabilityStatus(*participant,false) == -1;
        assert(success);
        success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == 0;
        assert(success);
        success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0;
        assert(success);
        assert(anonymouslyMixed);
        return success;
    }
    WebRtc_UWord32 previousAmountOfMixableParticipants = 0;
    bool success = _mixer->AmountOfMixables(previousAmountOfMixableParticipants) == 0;
    assert(success);

    success = _mixer->SetMixabilityStatus(*participant,true) == 0;
    assert(success);
    success = _mixer->SetMixabilityStatus(*participant,true) == -1;
    assert(success);
    success = _mixer->SetMixabilityStatus(*participant,false) == 0;
    assert(success);
    success = _mixer->SetMixabilityStatus(*participant,false) == -1;
    assert(success);
    success = _mixer->SetMixabilityStatus(*participant,true) == 0;
    assert(success);
    if(!success)
    {
        return false;
    }

    WebRtc_UWord32 currentAmountOfMixableParticipants = 0;
    success = _mixer->AmountOfMixables(currentAmountOfMixableParticipants) == 0;
    assert(currentAmountOfMixableParticipants == previousAmountOfMixableParticipants + 1);

    bool mixable = true;
    success = _mixer->MixabilityStatus(*participant,mixable) == 0;
    assert(success);
    assert(mixable);
    if(participantType == MixerParticipant::REGULAR)
    {
        return true;
    }
    bool IsVIP = false;
    success = _mixer->SetVIPStatus(*participant,true) == 0;
    assert(success);
    success = _mixer->VIPStatus(*participant,IsVIP) == 0;
    assert(success);
    assert(IsVIP);
    success = _mixer->SetVIPStatus(*participant,true) == -1;
    assert(success);
    success = _mixer->SetVIPStatus(*participant,false) == 0;
    assert(success);
    success = _mixer->VIPStatus(*participant,IsVIP) == 0;
    assert(success);
    assert(!IsVIP);
    success = _mixer->SetVIPStatus(*participant,false) == -1;
    assert(success);
    success = _mixer->SetVIPStatus(*participant,true) == 0;
    assert(success);
    success = _mixer->VIPStatus(*participant,IsVIP) == 0;
    assert(success);
    assert(IsVIP);
    assert(success);
    return success;
}

bool
MixerWrapper::StopMixingParticipant(
    const WebRtc_UWord32 id)
{
    MapItem* item = _mixerParticipants.Find(id);
    if(item == NULL)
    {
        return false;
    }
    MixerParticipant* participant = static_cast<MixerParticipant*>(item->GetItem());
    bool success = false;
    WebRtc_UWord32 previousAmountOfMixableParticipants = 0;
    success = _mixer->AmountOfMixables(previousAmountOfMixableParticipants) == 0;
    assert(success);
    success = _mixer->SetMixabilityStatus(*participant,false) == 0;
    assert(success);
    WebRtc_UWord32 currentAmountOfMixableParticipants = 0;
    success = _mixer->AmountOfMixables(currentAmountOfMixableParticipants) == 0;
    assert(success);
    assert(success ? currentAmountOfMixableParticipants == previousAmountOfMixableParticipants -1 :
                     currentAmountOfMixableParticipants == previousAmountOfMixableParticipants);
    return success;
}

bool
MixerWrapper::GetFreeItemIds(
    WebRtc_UWord32& itemId)
{
    if(!_freeItemIds.Empty())
    {
        ListItem* item = _freeItemIds.First();
        WebRtc_UWord32* id = static_cast<WebRtc_UWord32*>(item->GetItem());
        itemId = *id;
        delete id;
        return true;
    }
    if(_itemIdCounter == (WebRtc_UWord32) -1)
    {
        return false;
    }
    itemId = _itemIdCounter++;
    return true;
}

void
MixerWrapper::AddFreeItemIds(
    const WebRtc_UWord32 itemId)
{
    WebRtc_UWord32* id = new WebRtc_UWord32;
    *id = itemId;
    _freeItemIds.PushBack(static_cast<void*>(id));
}

void
MixerWrapper::ClearAllItemIds()
{
    ListItem* item = _freeItemIds.First();
    while(item != NULL)
    {
        WebRtc_UWord32* id = static_cast<WebRtc_UWord32*>(item->GetItem());
        delete id;
        _freeItemIds.Erase(item);
        item = _freeItemIds.First();
    }
}

bool
LoopedFileRead(
    WebRtc_Word16* buffer,
    WebRtc_UWord32 bufferSizeInSamples,
    WebRtc_UWord32 samplesToRead,
    FILE* file)
{
    if(bufferSizeInSamples < samplesToRead)
    {
        return false;
    }
    WebRtc_UWord32 gottenSamples = (WebRtc_UWord32)fread(buffer,sizeof(WebRtc_Word16),samplesToRead,file);
    if(gottenSamples != samplesToRead)
    {
        WebRtc_UWord32 missingSamples = samplesToRead - gottenSamples;
        fseek(file,0,0);
        gottenSamples += (WebRtc_UWord32)fread(&buffer[gottenSamples],sizeof(WebRtc_Word16),missingSamples,file);
    }
    if(gottenSamples != samplesToRead)
    {
        return false;
    }
    return true;
}

void
GenerateRandomPosition(
    WebRtc_Word32& startPosition)
{
    startPosition = (rand() % (60*16000/160)) * 160;
}
