#include "stdafx.h"
#include "cbr.h"
#include <ipl98/cpp/image.h>
#include <cstdlib>
// ostream needed for endl
#include <ostream>
#include <cmath>
// iostream needed for cout
#include <iostream>
#include <fstream>


using namespace std;


struct Cbr::a_case oldCases[1000];
int casecount=0;
int first=0;
int reusethreshold=80;
ofstream logg ("logg.txt", ios::app); //writing to file in append mode

void Cbr::Retrieve(struct a_case thenewcase){
    logg << "------------------------------------------------" << "\n";
    //logg << "Starting Retrieve Stage" << "\n";
    // must search case database for relevant cases
    Cbr::ReadCasesFromDB();
    logg << "Done reading cases from DB." << "\n";
    int one = 0;
    int two = 0;
    int three = 0;
    if(casecount==0){
        logg << "There are no cases in the case database." << "\n";
        Cbr::Retain(0,thenewcase,0,0,0);
    }
    else{
    Cbr::CompareCasesRetrieve(thenewcase, &one, &two, &three);
    Cbr::Reuse(thenewcase, one, two, three);
    }
    
    Cbr::UpdateDataBase();

    logg.close();
    
}

void Cbr::ReadCasesFromDB(){
        
    ifstream ci ("example.txt", ios::in); //reading from file 
    string s[1000];
    
    if (ci.is_open())
    {
        int i = 0;
        int internal = 0;
        char ss[100];
        //must get one line at a time
        while(!ci.getline(ss, 100).eof()){
            cout << ss << endl;
            //must check if the line identifies a new case
            
            
            if(strtol(ss,0,10)==999){
                casecount++;
                internal=13;
            }

            if(internal == 0){
                oldCases[casecount].case_nr=strtol(ss,0,10);
                internal++;
            }
            else if(internal == 1){
                oldCases[casecount].nr_of_objects=strtol(ss,0,10);
                internal++;
            }
            else if(internal == 2){
                oldCases[casecount].largest_object=strtol(ss,0,10);
                internal++;
            }
            else if(internal == 3){
                char * tt;
                oldCases[casecount].avg_object=strtod(ss,&tt);
                internal++;
            }
            else if(internal == 4){
                char * tt;
                oldCases[casecount].avg_intensity=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 5){
                char * tt;
                oldCases[casecount].change_intensity=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 6){
                char * tt;
                oldCases[casecount].weight_nr_of_objects=strtod(ss,&tt);
                internal++;
            }
            else if(internal == 7){
                char * tt;
                oldCases[casecount].weight_largest_object=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 8){
                char * tt;
                oldCases[casecount].weight_avg_object=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 9){
                char * tt;
                oldCases[casecount].weight_avg_intensity=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 10){
                char * tt;
                oldCases[casecount].weight_change_intensity=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 11){
                char * tt;
                oldCases[casecount].alarm_situation=strtod(ss, &tt);
                internal++;
            }
            else if(internal == 12){
                oldCases[casecount].textual=ss;
                cout << "reading from the file: " << ss << endl;
                internal++;
            }
            else if(internal == 13){
                internal = 0;   
            }

        }

    ci.close();
    }
  
    else logg << "Unable to open file" << "\n";
}

void Cbr::CompareCasesRetrieve(struct a_case thenewcase, int* best, int* second, int* third){
    logg << "Comparing cases in the retrieve stage." << "\n";
    double bestvalue=10000000000000000000;
    double secondbestvalue=10000000000000000000;
    double thirdbestvalue=10000000000000000000;
    
        //must do a comparison with all the cases in the database and find the best match
        for(unsigned int i=1;i<casecount+1;i++){
            //must sum up difference between all features!!
            double weightCo = 1/(oldCases[i].weight_largest_object+oldCases[i].weight_nr_of_objects+oldCases[i].weight_avg_object+oldCases[i].weight_avg_intensity+oldCases[i].weight_change_intensity);
            double firstF = (pow(thenewcase.nr_of_objects-oldCases[i].nr_of_objects,2))*oldCases[i].weight_nr_of_objects;
            double secondF = (pow(thenewcase.largest_object-oldCases[i].largest_object,2))*oldCases[i].weight_largest_object;
            double thirdF = (pow(thenewcase.avg_object-oldCases[i].avg_object,2))*oldCases[i].weight_avg_object;
            double fourthF = (pow(thenewcase.avg_intensity-oldCases[i].avg_intensity,2))*oldCases[i].weight_avg_intensity;
            double fifthF = (pow(thenewcase.change_intensity-oldCases[i].change_intensity,2))*oldCases[i].weight_change_intensity;
            
            
            //add more features when nesc

            double solution = weightCo*((firstF*0.5)+(secondF*0.5)+(thirdF*0.5)+(fourthF*10)+(fifthF*10));
    
            if(solution<bestvalue){
                thirdbestvalue=secondbestvalue;
                secondbestvalue=bestvalue;
                bestvalue=solution;
                (*third)=(*second);
                (*second)=(*best);
                (*best)=oldCases[i].case_nr;

            }
            else if(solution<secondbestvalue){
                thirdbestvalue=secondbestvalue;
                secondbestvalue=solution;
                (*third)=(*second);
                (*second)=oldCases[i].case_nr;
            }
            else if(solution<thirdbestvalue){
                thirdbestvalue=solution;
                (*third)=oldCases[i].case_nr;
            }

        }
        cout << "the selected best case is case nr: " << *best << " with textual description: " << oldCases[*best].textual << endl;
        cout << "the selected secondbest case is case nr: " << *second << " with textual description: " << oldCases[*second].textual << endl;
        cout << "the selected third best case is case nr: " << *third << " with textual description: " << oldCases[*third].textual << endl;
        logg << "The best case is case " << *best << ": " << oldCases[*best].textual << "." << "\n";
        logg << "The second best case is case " << *second << ": " << oldCases[*second].textual << "." << "\n";
        logg << "The third best case is case " << *third << ": " << oldCases[*third].textual << "." << "\n";
    
}

void Cbr::RaiseAlarm(int alarm){
    if(alarm==1){
        logg << "ALARM IS RAISED!" << "\n";
    }else{
        logg << "ALARM IS NOT RAISED!" << "\n";
    }
}

void Cbr::AddNewCase(struct a_case thenewcase){
    oldCases[casecount+1].case_nr=casecount+1;
    oldCases[casecount+1].nr_of_objects=thenewcase.nr_of_objects;
    oldCases[casecount+1].largest_object=thenewcase.largest_object;
    oldCases[casecount+1].avg_object=thenewcase.avg_object;
    oldCases[casecount+1].avg_intensity=thenewcase.avg_intensity;
    oldCases[casecount+1].change_intensity=thenewcase.change_intensity;
    oldCases[casecount+1].weight_nr_of_objects=thenewcase.weight_nr_of_objects;
    oldCases[casecount+1].weight_largest_object=thenewcase.weight_largest_object;
    oldCases[casecount+1].weight_avg_object=thenewcase.weight_avg_object;
    oldCases[casecount+1].weight_avg_intensity=thenewcase.weight_avg_intensity;
    oldCases[casecount+1].weight_change_intensity=thenewcase.weight_change_intensity;
    oldCases[casecount+1].alarm_situation=thenewcase.alarm_situation;
    oldCases[casecount+1].textual=thenewcase.textual;
    
    casecount++;


}

void Cbr::UpdateDataBase(){

    logg << "Updating the database." << "\n";

    ofstream ofs ("example.txt", ios::out); //writing to file in rewrite mode

    for(unsigned int y=1;y<casecount+1;y++){
    ofs << 999 << "\n";
    ofs << y << "\n";
    ofs << oldCases[y].nr_of_objects << "\n";
    ofs << oldCases[y].largest_object << "\n";
    ofs << oldCases[y].avg_object << "\n";
    ofs << oldCases[y].avg_intensity << "\n";
    ofs << oldCases[y].change_intensity << "\n";
    ofs << oldCases[y].weight_nr_of_objects << "\n";
    ofs << oldCases[y].weight_largest_object << "\n";
    ofs << oldCases[y].weight_avg_object << "\n";
    ofs << oldCases[y].weight_avg_intensity << "\n";
    ofs << oldCases[y].weight_change_intensity << "\n";
    ofs << oldCases[y].alarm_situation << "\n";
    ofs << oldCases[y].textual << "\n";
    }
    ofs << "end.\n";
    ofs.close();
}

double Cbr::CompareCasesReuse(struct a_case firstcase, struct a_case secondcase){

    double casediff=0;
    //must do a comparison with all the cases in the database and find the best match
    //must sum up difference between all features!!
    double firstF = (pow(firstcase.nr_of_objects-secondcase.nr_of_objects,2));      
    double secondF = (pow(firstcase.largest_object-secondcase.largest_object,2));
    double thirdF = (pow(firstcase.avg_object-secondcase.avg_object,2));
    double fourthF = (pow(firstcase.avg_intensity-secondcase.avg_intensity,2));
    double fifthF = (pow(firstcase.change_intensity-secondcase.change_intensity,2));
            
    //add more features when nesc
    casediff = sqrt(firstF+secondF+thirdF+fourthF+fifthF);
    return casediff;
}

void Cbr::Reuse(struct a_case thenewcase, int oldcaseindex, int second, int third){
    //logg << "starting reuse stage" << "\n";
    //must test case resemble value
    if(CompareCasesReuse(thenewcase, oldCases[oldcaseindex])<reusethreshold){//the cases are sufficiently similar
        logg << "The cases are similar enough to pass reuse." << "\n";
        if(oldCases[oldcaseindex].alarm_situation==1){
            logg << "The retrieved case is approved as solution and represents an alarm." << "\n";
            Cbr::RaiseAlarm(1);
        }
        else{
            logg << "The retrieved case is approved as solution and does not represent an alarm." << "\n";
            Cbr::RaiseAlarm(0);
        }
        Cbr::Retain(2, thenewcase, oldcaseindex, second, third);

    }else{
        logg << "The cases are not similar enough to pass reuse." << "\n";
        //develop new case!!!!
        int test=1;
        int buffer, one, two, three;
        int found=0;
        one = oldcaseindex;
        two = second;
        three = third;
        if(casecount>2){
            while(found==0){
                test = Cbr::Revise(thenewcase, one, two, three, test, &found);
                buffer=one;
                one=two;
                two=three;
                three=buffer;
            }

            if(test==2){ // the first case is approved
                Cbr::Retain(2, thenewcase, oldcaseindex, second, third);//found a case
            }
            else if(test==3){ // the second case is approved
                Cbr::Retain(3, thenewcase, oldcaseindex, second, third);//found a case
            }
            else if(test==4 && found==1){ // the third case is approved
                Cbr::Retain(4, thenewcase, oldcaseindex, second, third);//found a case
            }
            else if(test==4 && found==2 ){ //no cases approved create new case 
                Cbr::Retain(1, thenewcase, oldcaseindex, second, third);//never found a case
            }
        }
        else{ //if there are less then 3 cases in the database, just check the best one!!!!
            test = Cbr::Revise(thenewcase, one, two, three, test, &found);
            if(found==1){ // the first case is approved
                Cbr::Retain(2, thenewcase, oldcaseindex, second, third);//found a case
            }
            else{
                Cbr::Retain(1, thenewcase, oldcaseindex, second, third);//never found a case
            }

        }
        

        
    }




}


int Cbr::Revise(struct a_case thenewcase, int oldcaseindex, int second, int third, int args, int* found){

    //must detect textual description and ask user if the selected case can be approved anyway
    cout << "the textual description of the retrieved case was: " << oldCases[oldcaseindex].textual << "." << endl;
    cout << "---------------------------------------------" << endl;
    cout << " on the basis of the texual description of the retrieved case, please enter yes or no " << endl;
    string approved;
    cin >> approved;
    cin.ignore();
    cout << " does this image represent an alarm situation? please enter yes or no " << endl;
    string alarm;
    cin >> alarm;
    cin.ignore();
    logg << "Case " << oldCases[oldcaseindex].case_nr << " approved by user: " << approved << "." << "\n"; 
    cout << "user said: " << approved << " and is this an alarm situation? " << alarm << endl;

        
    if(approved=="yes"){
        *found=1;
        if(alarm=="yes" && oldCases[oldcaseindex].alarm_situation == 1){
            Cbr::RaiseAlarm(1);
            thenewcase.alarm_situation=1;
            
            //should raise alarm
        }
        else if(alarm=="no" && oldCases[oldcaseindex].alarm_situation == 0){
            Cbr::RaiseAlarm(0);
            thenewcase.alarm_situation=0;
            
        }
        else{
            cout << "user approved wrong solution" << endl;
            //this should never happen. User approved wrong solution
        }
        
        
    }else if(approved=="no"){
        //need to try next case
        if(args==3){
            *found=2;
        }
            if(alarm=="yes"){
                //implies that one should add a new case to the database
                //should raise alarm
                thenewcase.alarm_situation=1;
                
            }
            else if(alarm=="no"){
                //implies that one should add a new case to the database
                thenewcase.alarm_situation=0;
            }
            else{
                cout << " something fishy is going on" << endl;
            }
        
    }else{
        cout << "something wrong" << endl;

    }
    args = args + 1;

    return args;

}

void Cbr::UpdateWeight(int args, struct a_case thenewcase, int oldcaseindex){
    logg << "Updating weights with oldcase: " << oldcaseindex << " and args " << args << "." << "\n";
    logg << "Case text: " << oldCases[oldcaseindex].textual << "\n";
    double newarray[5];
    double oldarray[5];
    double weightarray[5];
    newarray[0]=thenewcase.nr_of_objects;
    newarray[1]=thenewcase.largest_object;
    newarray[2]=thenewcase.avg_object;
    newarray[3]=thenewcase.avg_intensity;
    newarray[4]=thenewcase.change_intensity;
    oldarray[0]=oldCases[oldcaseindex].nr_of_objects;
    oldarray[1]=oldCases[oldcaseindex].largest_object;
    oldarray[2]=oldCases[oldcaseindex].avg_object;
    oldarray[3]=oldCases[oldcaseindex].avg_intensity;
    oldarray[4]=oldCases[oldcaseindex].change_intensity;
    double weightacc=0;
    weightacc=weightacc+oldCases[oldcaseindex].weight_nr_of_objects+oldCases[oldcaseindex].weight_largest_object+oldCases[oldcaseindex].weight_avg_object+oldCases[oldcaseindex].weight_avg_intensity+oldCases[oldcaseindex].weight_change_intensity;
    weightarray[0]=oldCases[oldcaseindex].weight_nr_of_objects;
    weightarray[1]=oldCases[oldcaseindex].weight_largest_object;
    weightarray[2]=oldCases[oldcaseindex].weight_avg_object;
    weightarray[3]=oldCases[oldcaseindex].weight_avg_intensity;
    weightarray[4]=oldCases[oldcaseindex].weight_change_intensity;
    
    double threshold = 2.5;
    int numberoffeatures = 5;
    double highper = 1.37;
    double lowper = 1.112;

    for(unsigned int i=0;i<numberoffeatures;i++){
        if(i==2){
            threshold=1.3;
        }
        if(i==3){
            threshold=1.1;
        }
        
        double firstnr=0;
        
        if(newarray[i]>oldarray[i]){
            firstnr=(newarray[i]/oldarray[i]);
        }else{
            firstnr=(oldarray[i]/newarray[i]);
        }
        
        
        if(firstnr<0){firstnr=(-firstnr);}
        
        if(firstnr<threshold){
            
            for(unsigned int j=0;j<numberoffeatures;j++){

                if(i==j){
                    if(args==2){
                        weightarray[j]=weightarray[j]*highper;
                    }else{
                        weightarray[j]=weightarray[j]/highper;
                    }
                }else{
                    if(args==2){
                        weightarray[j]=weightarray[j]/lowper;
                    }else{
                        weightarray[j]=weightarray[j]*lowper;
                    }
                }
                

            }
            j=0;

        }else{

            for(unsigned int j=0;j<numberoffeatures;j++){

                if(i==j){
                    if(args==2){
                        weightarray[j]=weightarray[j]/highper;
                    }else{
                        weightarray[j]=weightarray[j]*highper;
                    }
                }else{
                    if(args==2){
                        weightarray[j]=weightarray[j]*lowper;
                    }else{
                        weightarray[j]=weightarray[j]/lowper;
                    }
                }

            }
            j=0;


        }

    }
    i=0;
    weightacc=0;
    for(unsigned int hh=0;hh<numberoffeatures;hh++){
        weightacc=weightacc+weightarray[hh];
        

    }
    

    //weights are now set in the weightarray!! Must update oldCases
    oldCases[oldcaseindex].weight_nr_of_objects=(weightarray[0]/(weightacc/5));
    oldCases[oldcaseindex].weight_largest_object=(weightarray[1]/(weightacc/5));
    oldCases[oldcaseindex].weight_avg_object=(weightarray[2]/(weightacc/5));
    oldCases[oldcaseindex].weight_avg_intensity=(weightarray[3]/(weightacc/5));
    oldCases[oldcaseindex].weight_change_intensity=(weightarray[4]/(weightacc/5));

}


void Cbr::Retain(int args, struct a_case thenewcase, int oldcaseindex, int second, int third){

    //if a new case is to be created the weights must be set and the weights of the retrieved case must be updated:
    if(args==1){
        cout << "a new case is to be created. please enter a textual description of the case here:" << endl;        
        char result[100];
        cin.getline(result,100);
        cin.ignore();
        string ress = result;
        logg << "A new case is to be created because no matching cases were found in the database." << "\n";
        cout << "user entered:" << result << endl;
        if(thenewcase.alarm_situation==1){
            Cbr::RaiseAlarm(1);
        }else{
            Cbr::RaiseAlarm(0);
        }
        if(casecount>3){

            Cbr::UpdateWeight(1, thenewcase, oldcaseindex);
            Cbr::UpdateWeight(1, thenewcase, second);
            Cbr::UpdateWeight(1, thenewcase, third);
        }
        else{
            logg << "The weights of the retrieved case was not updated because there is so few cases in the database." << "\n";
        }
        thenewcase.weight_largest_object=1;
        thenewcase.weight_nr_of_objects=1;
        thenewcase.weight_avg_object=1;
        thenewcase.weight_avg_intensity=1;
        thenewcase.weight_change_intensity=1;
        thenewcase.textual=ress;
        Cbr::AddNewCase(thenewcase);

    }
    else if(args==2){
        Cbr::UpdateWeight(2, thenewcase, oldcaseindex);
    }
    else if(args==3){
        Cbr::UpdateWeight(2, thenewcase, second);
        Cbr::UpdateWeight(1, thenewcase, oldcaseindex);
    }
    else if(args==4){
        Cbr::UpdateWeight(2, thenewcase, third);
        Cbr::UpdateWeight(1, thenewcase, oldcaseindex);
        Cbr::UpdateWeight(1, thenewcase, second);
    }

    else if(args==0){//this is the very first case.. just add it to the database
        cout << "a very first new case is to be created. please enter a textual description of the case here:" << endl;
        char text[100];
        cin.getline(text, 100);
        cin.ignore();
        string textt=text;
        cout << " does this image represent an alarm situation? please enter yes or no " << endl;
        char alarm[100];
        cin >> alarm;
        string alarms=alarm;
        cin.ignore();
        if(alarms=="yes"){
            thenewcase.alarm_situation=1;
            Cbr::RaiseAlarm(1);
        }else{
            thenewcase.alarm_situation=0;
            Cbr::RaiseAlarm(0);
        }
        thenewcase.weight_largest_object=1;
        thenewcase.weight_nr_of_objects=1;
        thenewcase.weight_avg_object=1;
        thenewcase.weight_avg_intensity=1;
        thenewcase.weight_change_intensity=1;
        thenewcase.textual=textt;
        Cbr::AddNewCase(thenewcase);
    }
}