//
//  SequenceGenerator.mm
//  riftathon
//
//  Created by Robert Tubb on 21/10/2014.
//
//

#include "SequenceController.h"
AnimStep::AnimStep(){
    type = PREVIEW_NEUTRAL_COUNT;
    presetIndex = -1;
    seqNumber = -1;
    runNumber = 0;
    whichInSequence = -1;
    
    tempo = MIN_TEMPO;
    
    isLastOfSeq = false;
    isLastOfRun = false;
    isLastOfAll = false;
    
    showsGuides = false;
    showsIcons = true;
    showsResultsAtEnd = false;
    difficulty = 0;
    setTimeFromTempo();
    totalLengthOfSeq = 0;
}

void AnimStep::saveResult(TrainingTestResult aresult){
    result = aresult;
}

Step::Step(){
    type = COUNT_IN;
    
    showsTargetIcon = false;
    showsControlSettings = false;
    showsControlGuides = false;
    showsMatchResults = false;
    hidesSliders = true;
    isPreview = false;
    
    allowsCandidateControl = false;
    playsTarget = false;
    playsCandidate = false;
    
    showsCountDown = true;
    
    isLastOfSeq = false;
    isLastOfRun = false;
    isLastOfAll = false;
    presetIndex = -1;
    seqNumber = -1;
    runNumber = -1;
    
    tempo = MIN_TEMPO;
}
void Step::setAsBlankCounter(){
    type = COUNT_IN;
    showsTargetIcon = false;
    showsControlSettings = false;
    showsControlGuides = false;
    showsMatchResults = false;
    hidesSliders = true;
    isPreview = false;
    
    allowsCandidateControl = false;
    playsTarget = false;
    playsCandidate = false;
    showsCountDown = true;
    
}
void Step::setAsPreviewPreparer(){
    type = PREVIEW_PREPARER;
    showsTargetIcon = true;
    showsControlSettings = false;
    showsControlGuides = true;
    
    showsMatchResults = false;
    hidesSliders = true;
    isPreview = true;
    
    allowsCandidateControl = true;
    playsTarget = true;
    playsCandidate = false;
    showsCountDown = true;
}

void Step::setAsPreviewPlayer(){
    type = PREVIEW_DISPLAYER;
    showsTargetIcon = true;
    showsControlSettings = false;
    showsControlGuides = true;
    showsMatchResults = false;
    hidesSliders = false;
    
    isPreview = true;
    
    allowsCandidateControl = true;
    playsTarget = true;
    playsCandidate = false;
    
    showsCountDown = false;
}

void Step::setAsMatchingPreparer(){
    // just a count down
    type = MATCHING_PREPARER;
    showsTargetIcon = true;
    showsControlSettings = false;
    showsControlGuides = false;
    showsMatchResults = false;
    hidesSliders = true;
    
    isPreview = false;
    
    allowsCandidateControl = true;
    playsTarget = true;
    playsCandidate = false;
    
    showsCountDown = true;
}
void Step::setAsMatchingInteraction(){
    type = MATCHING_INTERACTION;
    showsTargetIcon = true;
    showsControlSettings = false;
    showsControlGuides = true;
    showsMatchResults = false;
    hidesSliders = false;
    isPreview = false;
    
    allowsCandidateControl = true;
    playsTarget = false;
    playsCandidate = false; // ??
    
    showsCountDown = false;
}
void Step::setAsMatchingFeedback(){
    type = MATCHING_INTERACTION;
    showsTargetIcon = false;
    showsControlSettings = false;
    showsControlGuides = false;
    showsMatchResults = true;
    hidesSliders = true;
    isPreview = false;
    
    allowsCandidateControl = false;
    playsTarget = false;
    playsCandidate = true; // ??
    
    showsCountDown = false;
}

//=================================================================
//=================================================================
//=================================================================

SequenceController::SequenceController(){
    int tih[] = {
        2500,
        1944,
        1590,
        1346,
        1166,
        1029,
        921,
        833,
        760,
        700,
        648,
        603,
        564,
        530,
        500
    };
    for(int i = 0; i < NUM_TEMPO_STEPS; i++){
        timeIntervalsHard.push_back(tih[i]);
    }

    
    currentRunNumber = 0; // actually goes from 1 0 is pre-start
    
};
//-------------------------------------------------------------------
void SequenceController::init(bool asoundOnlyMode, controlPanelType whichInterfaceAreWeUsing){
    soundOnlyMode = asoundOnlyMode;
    panelType = whichInterfaceAreWeUsing;
    tempoInc = float(MAX_TEMPO - MIN_TEMPO) / float(NUM_TEMPO_STEPS);
    timeInc = (MAX_TIME - MIN_TIME) / NUM_TEMPO_STEPS;
    
    if (true){
        generateSteps();
    }else{
        generateTestSequence();
    }
    
    setToStart();
    
};
//-------------------------------------------------------------------
AnimStep SequenceController::getNextStep(){
    
    currentStep++;
    if ((*currentStep).isLastOfRun){
        // uh
    }
    if ((*currentStep).isLastOfSeq){
        
    }
    return (*currentStep);
    
};
//-------------------------------------------------------------------
void SequenceController::setToStart(){
    currentStep = steps.begin();
    currentRunNumber = (*currentStep).runNumber;
}
//-------------------------------------------------------------------
void SequenceController::skipRun(){
    // loop thru steps until next run reached
    if ( (*currentStep).isLastOfAll){
        return;
    }
    currentStep++; // IGNORE CURRENT STEP (LAST OF OLD RUN)
    
    bool nextRunReached = false;
    
    while (!nextRunReached){
        currentStep++;
        if ( (*currentStep).isLastOfAll){
            return;
        }
        if ((*currentStep).runNumber != currentRunNumber && (*currentStep).runNumber != 0){
            nextRunReached = true;
            currentRunNumber = (*currentStep).runNumber;
        }
    }
}
//-------------------------------------------------------------------
void SequenceController::stepForward(){
    currentStep++;
};
//-------------------------------------------------------------------
float SequenceController::getStartIntervalTime(){
    float t =  timeIntervalsHard[0];
    if (panelType == ALL_SLIDERS){
        t = SLIDER_SLOW_FACTOR*t;
    }
    return t;
}
//-------------------------------------------------------------------
void SequenceController::generateSteps(){
    srand (time(NULL));
    
    int run = 1;
    
    
    
    for(int numInSequence = MIN_TARGETS_IN_SEQUENCE; numInSequence <= MAX_TARGETS_IN_SEQUENCE; numInSequence++){
        
        for(int rep = 0; rep < NUM_REPETITIONS_AT_LEVEL; rep++){
            
            generateCountIn(1,run);
            
            if (soundOnlyMode){
                generateASoundOnlyRun(run, numInSequence);
            }else {
                generateRun(run, numInSequence);
            }
            steps.back().isLastOfRun = true;
            run++;
            cout << "-generate run finished-" << endl;
        }
    }
    totNumRuns = run-1;
    steps.back().isLastOfAll = true;
};

//-------------------------------------------------------------------
void SequenceController::generateTestSequence(){
    // just make something simple to check sanity
    generateCountIn(2,1);
    
    vector<int> seq = nonRandomSequence(2);
    
    // first we have a preparation count in
    AnimStep nextStep;
    nextStep.presetIndex = -1; // minus one means "blank"
    nextStep.runNumber = 1;
    nextStep.seqNumber = 1;
    nextStep.whichInSequence = 0;
    //nextStep.tempo = curTempo;
    nextStep.tempoLevel = 0;
    //        nextStep.setTimeFromTempo();
    nextStep.timeAllowedMs = timeIntervalsHard[0];
    if (panelType == ALL_SLIDERS){
        nextStep.timeAllowedMs = SLIDER_SLOW_FACTOR*nextStep.timeAllowedMs;
    }
    nextStep.setTempoFromTime();
    nextStep.showsGuides = true;
    nextStep.difficulty = 0;
    nextStep.type = AnimStep::PREVIEW_NEUTRAL_COUNT;
    nextStep.showsResultsAtEnd = false;
    

    steps.push_back(nextStep);
    
    makePreview(seq, 0, nextStep);

    makeGuidedSequence(seq, 0, nextStep);

    makeMatchingSequence(seq, 0, nextStep);
    
    totNumRuns = 1;
    steps.back().isLastOfAll = true;
    
}
//-------------------------------------------------------------------


void SequenceController::generateCountIn(int countInLength, int runNumber){
    AnimStep countStep;
    for (int i = 0; i < countInLength; i++){
        countStep.whichInSequence = countInLength - i + 1;
        countStep.type = AnimStep::PREVIEW_NEUTRAL_COUNT;
        countStep.showsResultsAtEnd = false;
        countStep.runNumber = runNumber;
        countStep.timeAllowedMs = getStartIntervalTime();
        countStep.setTempoFromTime();
        steps.push_back(countStep);
        
    }
};

//-------------------------------------------------------------------


vector<int> SequenceController::randomSequence(int numInSequence){
    vector<int> stepPresetIndices;
    // get some random ints
    for(int n=0; n < numInSequence; n++){
        int nextPreset = getRandomButNot(NUM_PRESETS,stepPresetIndices);
        stepPresetIndices.push_back(nextPreset);
        cout << nextPreset << ",";
    }
    
    return stepPresetIndices;
}

vector<int> SequenceController::nonRandomSequence(int numInSequence){
    vector<int> stepPresetIndices;
    // 1,2,3,4 you know the score
    for(int n=0; n < numInSequence; n++){
        int nextPreset = n;
        stepPresetIndices.push_back(nextPreset);
        cout << nextPreset << ",";
    }
    
    return stepPresetIndices;
}
//-------------------------------------------------------------------

void SequenceController::generateRun(int run, int numInSequence){
    float curTempo = MIN_TEMPO;
    int seqNo = 0;
    AnimStep nextStep;
    for(int tempoLevel = 0; tempoLevel < NUM_TEMPO_STEPS; tempoLevel++){
        
        
        
        // first we have a preparation count in
        nextStep.presetIndex = -1; // minus one means "blank"
        nextStep.runNumber = run;
        nextStep.seqNumber = seqNo;
        nextStep.whichInSequence = 0;
        //nextStep.tempo = curTempo;
        nextStep.tempoLevel = tempoLevel;
//        nextStep.setTimeFromTempo();
        nextStep.timeAllowedMs = timeIntervalsHard[tempoLevel];
        if (panelType == ALL_SLIDERS){
            nextStep.timeAllowedMs = SLIDER_SLOW_FACTOR*nextStep.timeAllowedMs;
        }
        nextStep.setTempoFromTime();
        nextStep.showsGuides = true;
        nextStep.difficulty = 0;
        nextStep.type = AnimStep::PREVIEW_NEUTRAL_COUNT;
        nextStep.showsResultsAtEnd = false;
        
        //nextStep = makeFirstCountIn();
        steps.push_back(nextStep);

            // two bar coun tin
        steps.push_back(nextStep);

        
        
        // generate a sequence of random preset indices
        vector<int> stepPresetIndices = randomSequence(numInSequence);
        nextStep.thisSequence = stepPresetIndices;
        nextStep.totalLengthOfSeq = numInSequence;
        makePreview(stepPresetIndices, run, nextStep);
        
        // make GUIDED sequence (1 and 2)
        makeGuidedSequence(stepPresetIndices, run, nextStep);
        makeGuidedSequence(stepPresetIndices, run, nextStep);
        
        // make matching sequence without icon seq help
        makeMatchingSequence(stepPresetIndices, run, nextStep);
        makeMatchingSequence(stepPresetIndices, run, nextStep);
        
        steps.back().isLastOfSeq = true;
        if(numInSequence == 1){
            curTempo += tempoInc; // make no 1 easier!!!??? will mess up time points
        }else{
            curTempo += tempoInc;
        }
        seqNo++;
        cout << endl;
        
    }

    // ANIM nextStep.setAsBlankCounter();
    nextStep.type = AnimStep::MATCHING_NEUTRAL_COUNT;
    steps.push_back(nextStep);
    
}
void SequenceController::makePreview(vector<int> stepPresetIndices, int run, AnimStep& nextStep){
    
    // MAKE PREVIEW
    int n = 1;
    for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
        // put loader
        
        nextStep.presetIndex = *si;
        nextStep.runNumber = run;
        nextStep.whichInSequence = n;
        if (soundOnlyMode){
            nextStep.showsGuides = false;
            nextStep.showsIcons = false ;
        }else{
            nextStep.showsGuides = true;
            nextStep.showsIcons = true;
        }
        nextStep.showsResultsAtEnd = false;
        nextStep.type = AnimStep::PREVIEW_MOVE;
        steps.push_back(nextStep);
        
        n++;
    }
    
    // move back to neutral
    nextStep.presetIndex = -1;
    nextStep.type = AnimStep::PREVIEW_LAST;
    nextStep.whichInSequence = 0;
    nextStep.difficulty = 0;
    nextStep.showsResultsAtEnd = true;
    steps.push_back(nextStep);
}
void SequenceController::makeGuidedSequence(vector<int> stepPresetIndices, int run, AnimStep& nextStep){
    int n = 1;
    for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
        // put loader
        nextStep.difficulty = 1;
        nextStep.presetIndex = *si;
        nextStep.runNumber = run;
        nextStep.whichInSequence = n;
        nextStep.showsGuides = true;
        nextStep.showsIcons = true;
        nextStep.showsResultsAtEnd = true;
        nextStep.type = AnimStep::MATCHING_MOVE;
        steps.push_back(nextStep);
        
        if (SPACER_BARS){
            nextStep.type = AnimStep::MATCHING_LAST;
            steps.push_back(nextStep);
        }
        n++;
    }
    // move back to neutral
    nextStep.presetIndex = -1;
    nextStep.type = AnimStep::MATCHING_LAST;
    nextStep.showsGuides = true;
    nextStep.showsIcons = true;
    nextStep.whichInSequence = END_NEUTRAL_MATCH;
    nextStep.difficulty = 0;
    nextStep.showsResultsAtEnd = true;
    steps.push_back(nextStep);
}
void SequenceController::makeMatchingSequence(vector<int> stepPresetIndices, int run, AnimStep& nextStep){
    int n = 1;
    for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
        // put loader
        if(soundOnlyMode){
            nextStep.difficulty = 3;
        }else{
            nextStep.difficulty = 2;
        }
        nextStep.presetIndex = *si;
        nextStep.runNumber = run;
        nextStep.whichInSequence = n;
        nextStep.showsGuides = false;
        nextStep.showsIcons = false;
        nextStep.showsResultsAtEnd = true;
        nextStep.type = AnimStep::MATCHING_MOVE;
        steps.push_back(nextStep);
        
        if (SPACER_BARS){
            nextStep.type = AnimStep::MATCHING_LAST;
            steps.push_back(nextStep);
        }
        n++;
    }
    // move back to neutral
    nextStep.presetIndex = -1;
    nextStep.type = AnimStep::MATCHING_LAST;
    nextStep.whichInSequence = END_NEUTRAL_MATCH;
    nextStep.difficulty = 0;
    nextStep.showsGuides = false; // fact that preset is -1 indicates end of each seq
    nextStep.showsIcons = false;
    nextStep.showsResultsAtEnd = true; // to record neutral hit
    steps.push_back(nextStep);
}
//-------------------------------------------------------------------
void SequenceController::generateASoundOnlyRun(int run, int numInSequence){
    
    // here the preview is sound only
    float curTempo = MIN_TEMPO;
    int seqNo = 0;
    AnimStep nextStep;
    nextStep.totalLengthOfSeq = numInSequence;
    
    for(int tempoLevel = 0; tempoLevel < NUM_TEMPO_STEPS; tempoLevel++){
        // first we have a preparation count in
        nextStep.presetIndex = -1; // minus one means "blank"
        nextStep.runNumber = run;
        nextStep.seqNumber = seqNo;
        nextStep.whichInSequence = 0;
        
        nextStep.difficulty = 0;
        nextStep.tempo = curTempo; // TODO
        nextStep.showsGuides = true; // guide shown for neutral point
        nextStep.type = AnimStep::PREVIEW_NEUTRAL_COUNT;
        nextStep.showsResultsAtEnd = false;
        steps.push_back(nextStep);
        
        // generate a sequence of random preset indices
        vector<int> stepPresetIndices = randomSequence(numInSequence);
        nextStep.thisSequence = stepPresetIndices;
        
        makePreview(stepPresetIndices, run, nextStep);
        makePreview(stepPresetIndices, run, nextStep);
        
        // make matching sequence without icon seq help
        makeMatchingSequence(stepPresetIndices, run, nextStep);
        makeMatchingSequence(stepPresetIndices, run, nextStep);
        
        
        steps.back().isLastOfSeq = true;
        curTempo += tempoInc;
        seqNo++;
    }
    // shows last result ?
    nextStep.type = AnimStep::MATCHING_NEUTRAL_COUNT;
    steps.push_back(nextStep);
}
//-------------------------------------------------------------------

void SequenceController::generateARun(int run, int numInSequence){
    float curTempo = MIN_TEMPO;
    int seqNo = 0;
    AnimStep nextStep;
    for(int tempoLevel = 0; tempoLevel < NUM_TEMPO_STEPS; tempoLevel++){
        
        
        
        // first we have a preparation count in
        nextStep.presetIndex = -1; // minus one means "blank"
        nextStep.runNumber = run;
        nextStep.seqNumber = seqNo;
        nextStep.whichInSequence = 0;
        nextStep.tempo = curTempo;
        nextStep.tempoLevel = tempoLevel;
        nextStep.setTimeFromTempo();
        nextStep.showsGuides = false;
        nextStep.type = AnimStep::PREVIEW_NEUTRAL_COUNT;
        nextStep.showsResultsAtEnd = false;
        // two neutrals between the new sequences
        steps.push_back(nextStep);
        nextStep.showsGuides = true;
        steps.push_back(nextStep);
        
        // generate a sequence of random preset indices
        vector<int> stepPresetIndices = randomSequence(numInSequence);
        nextStep.thisSequence = stepPresetIndices;
        
        // make preview sequence
        int n = 1;
        for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
            // put loader
            
            nextStep.presetIndex = *si;
            nextStep.runNumber = run;
            nextStep.seqNumber = seqNo;
            nextStep.whichInSequence = n;
            
            nextStep.showsGuides = true;
            nextStep.showsIcons = true;
            nextStep.showsResultsAtEnd = false;
            nextStep.type = AnimStep::PREVIEW_MOVE;
            steps.push_back(nextStep);
            
            if (SPACER_BARS){
                nextStep.type = AnimStep::PREVIEW_LAST;
                steps.push_back(nextStep);
            }
            n++;
        }
        
        // move back to neutral
        nextStep.presetIndex = -1;
        nextStep.type = AnimStep::PREVIEW_MOVE;
        nextStep.difficulty = 0;
        nextStep.whichInSequence = 0;
        nextStep.showsResultsAtEnd = true;
        steps.push_back(nextStep);
        
        // make GUIDED sequence
        n = 1;
        for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
            // put loader
            nextStep.difficulty = 1;
            nextStep.presetIndex = *si;
            nextStep.seqNumber = seqNo;
            nextStep.whichInSequence = n;
            
            nextStep.showsGuides = true;
            nextStep.showsIcons = true;
            nextStep.showsResultsAtEnd = true;
            nextStep.type = AnimStep::MATCHING_MOVE;
            steps.push_back(nextStep);
            
            if (SPACER_BARS){
                nextStep.type = AnimStep::MATCHING_LAST;
                steps.push_back(nextStep);
            }
            n++;
        }
        // move back to neutral
        nextStep.presetIndex = -1;
        nextStep.whichInSequence = 0;
        nextStep.type = AnimStep::MATCHING_LAST;
        nextStep.showsResultsAtEnd = false;
        steps.push_back(nextStep);
        
        
        // make matching sequence with icon seq help
        n = 1;
        for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
            // put loader
            nextStep.difficulty = 2;
            nextStep.presetIndex = *si;
            nextStep.whichInSequence = n;
            
            nextStep.showsGuides = false;
            nextStep.showsIcons = true;
            nextStep.type = AnimStep::MATCHING_MOVE;
            nextStep.showsResultsAtEnd = true;
            steps.push_back(nextStep);
            
            if (SPACER_BARS){
                nextStep.type = AnimStep::MATCHING_LAST;
                steps.push_back(nextStep);
                
            }
            n++;
            
        }
        
        // move back to neutral
        nextStep.presetIndex = -1;
        nextStep.whichInSequence = 0;
        nextStep.type = AnimStep::MATCHING_LAST;
        nextStep.showsResultsAtEnd = false;
        steps.push_back(nextStep);
        
        // make matching sequence WITHOUT icon seq help
        n = 1;
        for(auto si = stepPresetIndices.begin(); si < stepPresetIndices.end(); si++){
            // put loader
            nextStep.difficulty = 3;
            nextStep.presetIndex = *si;
            nextStep.whichInSequence = n;
            
            nextStep.showsGuides = false;
            nextStep.showsIcons = false;
            
            nextStep.type = AnimStep::MATCHING_MOVE;
            nextStep.showsResultsAtEnd = true;
            
            steps.push_back(nextStep);
            
            if (SPACER_BARS){
                nextStep.type = AnimStep::MATCHING_LAST;
                steps.push_back(nextStep);
                
            }
            n++;
            
        }
        
        nextStep.type = AnimStep::MATCHING_LAST;
        nextStep.presetIndex = -1;
        nextStep.whichInSequence = 0;
        nextStep.showsGuides = false; // so you know when to go to neutral
        nextStep.showsResultsAtEnd = false;
        steps.push_back(nextStep);
        
        steps.back().isLastOfSeq = true;
        curTempo += tempoInc;
        seqNo++;
        cout << endl;
        
    }
    // ANIM nextStep.setAsBlankCounter();
    nextStep.type = AnimStep::MATCHING_NEUTRAL_COUNT;
    steps.push_back(nextStep);
    
}
//-----------------------------------------------------------------------
void SequenceController::saveResultForCurrentStep(TrainingTestResult result){
    (*currentStep).saveResult(result);
    
}
//-----------------------------------------------------------------------
TrainingTestResult SequenceController::getResultForPreviousStep(){
    if (currentStep == steps.begin()){
        
        cout << "ERROR - " << endl;
        
    }
    return (*currentStep--).result;
    
    
}
//-----------------------------------------------------------------------


int SequenceController::getRandomButNot(int max, vector<int> notThese){
    
    bool there = true;
    int randomInt = rand() % max;
    
    if (notThese.size()){
        while(there){
            randomInt = rand() % max;
            vector<int>::iterator result = std::find(notThese.begin(), notThese.end(), randomInt);
            there =  (result != notThese.end());
        }
    }
    return randomInt;
    
};