//
//  TrainingMessageOrganiser.mm
//  riftathon
//
//  Created by Robert Tubb on 17/10/2014.
//
//

#include "TrainingMessageOrganiser.h"

void TrainingMessageOrganiser::init( PDSynthWrapper& cs, PDSynthWrapper& ts, bool soundOnlyMode){
    
    MessageOrganiser::init(cs,ts);
    
    TickListenerFunction callback;
    callback = boost::bind(&TrainingMessageOrganiser::onNextTickAnim, this, _1);
    candidateSynth.registerForTicks(callback);
    
    numParamsToUse = TOTAL_NUM_PARAMS;

    sequenceController.init(soundOnlyMode);

    
}

void TrainingMessageOrganiser::setup(controlPanelType whichInterfaceAreWeUsing){
    
    setupDefaultMapping(whichInterfaceAreWeUsing);
    
    controlPanel->setValues(zeros<int>(TOTAL_NUM_PARAMS));
}
//----------------------------------------------------------------------------------------
void TrainingMessageOrganiser::setMiddlePanel(TextPanel* tp){
    middlePanel = tp;
}

//----------------------------------------------------------------------------------------
void TrainingMessageOrganiser::showMyPanels(){
    presetIconPanel->show();
    instructionPanel->show();
    bottomPanel->show();
    controlPanel->show();
    middlePanel->show();
    okToGetMidi = true; // enables check that hand moves on startup?
}

//-----------------------------------------------------------------------
void TrainingMessageOrganiser::displayInstructions(string text){

    instructionPanel->setText(text);
    instructionPanel->show();

}

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

void TrainingMessageOrganiser::showUserHowTheyDid(){
    // colour flash
    // distance ?
    // score
    
}


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

void TrainingMessageOrganiser::buttonPressCallback(int mappingID, int value){
    if(mappingID == VOLUME_CHANGE_ID){
        targetSynth.sendVolume(value);
        candidateSynth.sendVolume(value);
        return;
    }
    
    if (mappingID == START_TRAINING_SEQUENCE_ID){

        candidateSynth.setMetroTime(sequenceController.getStartTickTime());
        candidateSynth.startMetronome();
        bottomPanel->hide();
        
        vector<int> details;
        
        eventLogger.logEvent(START_NEW_RUN);
        return;
    }
    
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------
void TrainingMessageOrganiser::onNextTick(int tickNumber){
    static int showingCountdown = false;

    downCounter = 4 - tickNumber % 4;
    
    if (showingCountdown){
        middlePanel->setColor(ofColor::white);
        middlePanel->setText(ofToString(downCounter));
    }
    
    // only first beat in bar is active one
    if ( tickNumber % 4  != 0){
        
        return;
    }
    
}
void TrainingMessageOrganiser::lastOfRun(int which){
    candidateSynth.stopMetronome();
    stringstream s;
    
    s << "FINISHED RUN " << which << " OF " << sequenceController.getTotNumRuns() << endl;
    controlPanel->hide();
    middlePanel->setColor(ofColor::white);
    middlePanel->setText("FINISHED RUN");
    middlePanel->show();
    //playCandidateButton->setLabel("NEXT");
    bottomPanel->show();
    seqNumPanel->hide();
    eventLogger.logEvent(FINISHED_RUN);
}
void TrainingMessageOrganiser::lastOfAll(){
    candidateSynth.stopMetronome();
    cout << "FINISHED BLOCK" << endl;
    controlPanel->hide();
    middlePanel->setColor(ofColor::white);
    middlePanel->setText("FINISHED BLOCK!");
    middlePanel->show();
    bottomPanel->show();
}

void TrainingMessageOrganiser::logNewStep(AnimStep newStep){
    
    vector<int> details;
    
    details.push_back(int(newStep.type));
    details.push_back(newStep.whichInSequence);
    details.push_back(newStep.showsGuides);
    details.push_back(newStep.showsIcons);
    details.push_back(newStep.presetIndex);
    details.push_back(newStep.tempo);
    
    eventLogger.logEvent(NEW_STEP, details);
    
}

//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::onNextTickAnim(int tickNumber){
    static AnimStep oldStep;
    static vector<int> lastPosition;
    
    cout << "TICK " << tickNumber << endl;
    downCounter = 4 - tickNumber % 4;
    
    updateCountdown(downCounter);
    // only first beat in bar is active one (at the moment)
    // TODO send next target values again and again
    if ( tickNumber % 4  != 0) {
        if (tickNumber % 4  == 3){
            targetSynth.sendAllParams();
        }
        return;
    };
    
    
    Preset* previousTargetPreset;
    vector<int> curCandidateSettings = candidateSynth.getAllParamValues();
    if (oldStep.showsResultsAtEnd){
        
        // store results
        if(oldStep.presetIndex == -1){
            previousTargetPreset =  expPresetManager.getNeutralPreset();
            cout << "SHOULDNT SCORE NEUTRAL??" << endl;
        }else{
            
            previousTargetPreset =  expPresetManager.getPresetAtIndex(oldStep.presetIndex);
        }
        previousTargetPreset =  expPresetManager.getPresetAtIndex(oldStep.presetIndex);
        
        eventLogger.logEvent(FINISH_POS, curCandidateSettings);
        TrainingTestResult result =  doResults(oldStep,
                                               previousTargetPreset,
                                               lastPosition,
                                               curCandidateSettings);
        
        flashResult(result);
    }
    
    AnimStep newStep = sequenceController.getNextStep();
    
    logNewStep(newStep);
    
    if(newStep.isLastOfAll){
        lastOfAll();
        return;
    }
    if(newStep.isLastOfRun){
        lastOfRun(newStep.runNumber);
        return;
    }
    
    candidateSynth.setMetroTime(newStep.getTimeBetweenTicks());
    
    switch (newStep.type){
        case AnimStep::PREVIEW_NEUTRAL_COUNT :
            previewNeutralCount(newStep);
            break;
        case AnimStep::PREVIEW_MOVE :
            previewMove(newStep);
            break;
        case AnimStep::PREVIEW_HIT :
            previewHit( newStep);
            break;
        case AnimStep::MATCHING_NEUTRAL_COUNT :
            matchingNeutralCount( newStep);
            break;
        case AnimStep::MATCHING_MOVE :
            matchingMove( newStep);
            break;
        case AnimStep::MATCHING_HIT :
            matchingHit( newStep);
            break;
        case AnimStep::GUIDED_MOVE :
            guidedMove( newStep);
            break;
            
        case AnimStep::GUIDED_HIT :
            guidedHit( newStep);
            break;
        default:
            break;
    }
    
    oldStep = newStep;
    lastPosition = curCandidateSettings;
    
}
//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::updateCountdown(int n){
    
}
//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::previewNeutralCount(AnimStep newStep){
    okToGetMidi = false;

    middlePanel->hide();
    
    Preset * currentTargetPreset;

    // your 'hand' is hidden
    controlPanel->showValueIndicators(false);
    
    
    // guide is set to neutral values
    currentTargetPreset = expPresetManager.getNeutralPreset();
    vector<int> newTargetValues = currentTargetPreset->getValues();
    presetIconPanel->setTextAndImage(currentTargetPreset->name, currentTargetPreset->getImage(), false);
    presetIconPanel->show();
    controlPanel->setValues(newTargetValues);
    controlPanel->setAndShowHint(newTargetValues, currentTargetPreset->getImage());
    controlPanel->setActive(false);
    
    targetSynth.setAllParams(newTargetValues);
    candidateSynth.setAllParams(newTargetValues);
    
    displayInstructions("Get ready for preview");
    seqNumPanel->hide();
    oldTargetValues = newTargetValues;
}
//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::previewMove(AnimStep newStep){
    //controlPanel->setValues(oldTargetValues);
    okToGetMidi = false;
    targetSynth.trigger(); // PLAYS LAST TARGET (but it doesn't?)
    Preset * currentTargetPreset;
    if(newStep.presetIndex == -1){
        currentTargetPreset =  expPresetManager.getNeutralPreset();
    }else{
        
        currentTargetPreset =  expPresetManager.getPresetAtIndex(newStep.presetIndex);
        if (currentTargetPreset == NULL){
            displayInstructions("No stored preset!\n please restart and choose EXP");
            candidateSynth.stopMetronome();
            return;
        }
    }
    vector<int> newTargetValues = currentTargetPreset->getValues();

    targetSynth.setAllParamsWithoutSend(newTargetValues);
    
    // show icon and guides
    if (newStep.showsGuides){ // always show neutral one
        
        // show where we're going
        controlPanel->setAndShowHint(newTargetValues, currentTargetPreset->getImage());
        
        // show how to get there
        controlPanel->animateToNewValues(newTargetValues, newStep.getTimeBetweenTicks()*4);
        controlPanel->showValueIndicators(true); // for animation
        controlPanel->setActive(false);
    }else{
        // We're in PERFORM mode
        if (newStep.presetIndex == -1){
            // last step of preview , can show neutral to give idea of when things happen
            // also allow interaction so that neutral can be prepared
            controlPanel->setAndShowHint(newTargetValues, currentTargetPreset->getImage());
            // TODO set value to neutral too
            controlPanel->showValueIndicators(true);
            okToGetMidi = true;
            controlPanel->setActive(true);
        }else{
            // empty box
            controlPanel->showHint(false);
            controlPanel->showValueIndicators(false);
            controlPanel->setActive(false);
        }
    }
    
    if (newStep.showsIcons){
        presetIconPanel->setTextAndImage(currentTargetPreset->name, currentTargetPreset->getImage(), false);
        presetIconPanel->show();
    }else{
        presetIconPanel->hide();
    }
    controlPanel->show();
    
    
    

    stringstream instruct;
    instruct << "PREVIEW" << endl;;
    instruct << newStep.whichInSequence+1 << endl;
    displayInstructions(instruct.str());
    showSeqNum(newStep.whichInSequence+1);
    oldTargetValues = newTargetValues;
}
//-----------------------------------------------------------------------------
// lest setep of preview seq - plays moves back to neutral pos?
void TrainingMessageOrganiser::previewHit(AnimStep newStep){
    Preset * currentTargetPreset;
    okToGetMidi = false;
    currentTargetPreset =  expPresetManager.getPresetAtIndex(newStep.presetIndex);
    vector<int> newTargetValues = currentTargetPreset->getValues();
    
    targetSynth.setAllParams(newTargetValues);
    
    // your hand shows!?
    controlPanel->showValueIndicators(true);
    okToGetMidi = true;
    //
    presetIconPanel->setTextAndImage(currentTargetPreset->name, currentTargetPreset->getImage(), false);
    presetIconPanel->show();
    
    controlPanel->setAndShowHint(newTargetValues, currentTargetPreset->getImage());

    
    targetSynth.trigger();
    
    displayInstructions("Preview HIT");
}

//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::guidedMove(AnimStep newStep){
    targetSynth.trigger(); // plays LAST target (?)
    triggerCandidateSound();
    middlePanel->hide();
    
    Preset * currentTargetPreset;
    if(newStep.presetIndex == -1){
        currentTargetPreset =  expPresetManager.getNeutralPreset();
    }else{
        
        currentTargetPreset =  expPresetManager.getPresetAtIndex(newStep.presetIndex);
    }

    vector<int> newTargetValues = currentTargetPreset->getValues();
    eventLogger.logEvent(NEW_TARGET, newTargetValues); // massively redundant info assuming we have saved presets...
    
    targetSynth.setAllParamsWithoutSend(newTargetValues);
    
    
    controlPanel->setActive(true);
    // show icon and guide markers
    presetIconPanel->setTextAndImage(currentTargetPreset->name, currentTargetPreset->getImage(), false);
    presetIconPanel->show();
    
    controlPanel->setAndShowHint(newTargetValues, currentTargetPreset->getImage());
    
    
    // show your hand and allow movement
    controlPanel->showValueIndicators(true);
    okToGetMidi = true;
    
    stringstream instruct;
    instruct << "GUIDED MOVE" << endl;;
    instruct << newStep.whichInSequence+1 << endl;
    displayInstructions(instruct.str());
    
    showSeqNum(newStep.whichInSequence+1);

    oldTargetValues = newTargetValues;
}

//======================================================================
void TrainingMessageOrganiser::guidedHit(AnimStep newStep){
    Preset * currentTargetPreset;
    currentTargetPreset =  expPresetManager.getPresetAtIndex(newStep.presetIndex);
    vector<int> newTargetValues = currentTargetPreset->getValues();
    eventLogger.logEvent(NEW_TARGET, newTargetValues);
    sendSynthValuesAgain();
    
    // show your hand and allow movement (so you can set off to next target??)
    
    controlPanel->showValueIndicators(true);
    okToGetMidi = true;
    
    targetSynth.setAllParamsWithoutSend(newTargetValues);
    triggerCandidateSound();
    targetSynth.trigger();
    displayInstructions("GUIDED HIT");
    vector<int> startPosition = expPresetManager.getNeutralPreset()->getValues();
    
    oldTargetValues = newTargetValues;
}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::matchingNeutralCount(AnimStep newStep){
    Preset * currentTargetPreset = expPresetManager.getNeutralPreset();
    vector<int> newTargetValues = currentTargetPreset->getValues();
    // if showing guides show all the targets as transparent ghosts
    if (newStep.showsGuides){
        controlPanel->setAndShowHint(newTargetValues, currentTargetPreset->getImage());

    }else{
        controlPanel->showHint(false);
    }
    
    // show your hand and allow movement
    controlPanel->showValueIndicators(true);
    okToGetMidi = true;
    
        displayInstructions("Match Count");
    seqNumPanel->hide();
    oldTargetValues = newTargetValues;
}
//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::matchingMove(AnimStep newStep){

    
    middlePanel->hide();
    
    Preset * currentTargetPreset;
    if(newStep.presetIndex == -1){
        currentTargetPreset =  expPresetManager.getNeutralPreset();
    }else{
        
        currentTargetPreset =  expPresetManager.getPresetAtIndex(newStep.presetIndex);
    }

    vector<int> newTargetValues = currentTargetPreset->getValues();
    eventLogger.logEvent(NEW_TARGET, newTargetValues);
    targetSynth.setAllParams(newTargetValues);

    // if showing guides show the hint hand
    
    if (newStep.showsGuides){
        controlPanel->setHintNoShow(newTargetValues, currentTargetPreset->getImage());
        controlPanel->showHint(true);
    }else{
        //
        controlPanel->showHint(false);
    }
    if (newStep.showsIcons){
        presetIconPanel->setTextAndImage(currentTargetPreset->name, currentTargetPreset->getImage(), false);
        presetIconPanel->show();
    }else{
        presetIconPanel->hide();
    }
    // show your hand and allow movement
    controlPanel->showValueIndicators(true);
    okToGetMidi = true;
    
    stringstream instruct;
    instruct << "MATCHING" << endl;;
    instruct << newStep.whichInSequence+1 << endl;
    displayInstructions(instruct.str());
    showSeqNum(newStep.whichInSequence+1);
    // trigger where we're at at the moment
    triggerCandidateSound();

    oldTargetValues = newTargetValues;

}
//-----------------------------------------------------------------------------
void TrainingMessageOrganiser::matchingHit(AnimStep newStep){
    Preset * currentTargetPreset;
    currentTargetPreset =  expPresetManager.getPresetAtIndex(newStep.presetIndex);
    vector<int> newTargetValues = currentTargetPreset->getValues();
    eventLogger.logEvent(NEW_TARGET, newTargetValues);
    sendSynthValuesAgain();
    
    // show your hand and allow movement (so you can set off to next target??)
    //box6D->showIndicator(true);
    stringstream instruct;
    instruct << "MATCHING FINISHED" << endl;;
    instruct << newStep.whichInSequence+1 << endl;
    displayInstructions(instruct.str());
    showSeqNum(newStep.whichInSequence+1);
    
    vector<int> startPosition = expPresetManager.getNeutralPreset()->getValues();
    controlPanel->setHintNoShow(newTargetValues, currentTargetPreset->getImage());
    
    triggerCandidateSound();
    oldTargetValues = newTargetValues;
}
//-----------------------------------------------------------------------------

TrainingTestResult TrainingMessageOrganiser::doResults(AnimStep newStep,
                                                       Preset * targetPreset,
                                                       vector<int> startingPosition,
                                                       vector<int> currentPosition){
    TrainingTestResult result =
    trainingScoreManager.getScoreForAnswer(currentPosition,
                                           startingPosition,
                                           targetPreset->getValues(),
                                           newStep.getTimeBetweenTicks());
    
    sequenceController.saveResultForCurrentStep(result);
    instructionPanel->setColor(result.colorBand);


    displayInstructions(result.displayText);
    
    showScoreNum(trainingScoreManager.getScore());
    
    return result;
}

void TrainingMessageOrganiser::flashResult(TrainingTestResult r){
    
    // stick a colored number over target position
    controlPanel->flashResultLight(r.colorBand, 30);

    
}
