#include "AE.h"
#include "discoveryMessage_m.h"
#include "utils.h"
#include "types.h"
#include <iostream>

int AE::numInitStages() const {
    return 2;
}

void AE::registration() {
    this->queryIndex = 0;
    this->warmup = par("warmup");
    // get the id of the AE
    URI = getId(); // this is the omnet id which is given when creating the module in the NED file (sequential numbering )
    // assigning randomly the data
    data = uniform(0, 100);
    int ran_number = std::rand() % 100;
    EV << "Random Number" << ran_number << "\n";
    // maxhop set to 10
    maxHop = par("maxHops");
    resource_pool_size = par("resource_pool_size");
    this->feature_type = std::to_string(std::rand() % resource_pool_size).c_str();

    // send an AE message with op_code Registration
    sendAEMessage(REGISTRATION);
//
//    if (getIndex() % 5 == 1) {
//        sendAEMessage(CANCELLATION);
//    }
}

void AE::sendQuery() {
    // getIndex is an omnet function that select in  a vector of AE (in this case ) the index in the vector table.
    if ((std::rand() % 5) == 1) {
        // send the AE message with op_code QUERY
//        EV << "AE of URI 28 is Searching for waterValve" << "\n";
        sendAEMessage(QUERY);
    }
    if ((std::rand() % 15) == 1) {
            // send the AE message with op_code QUERY
    //        EV << "AE of URI 28 is Searching for waterValve" << "\n";
            sendAEMessage(CANCELLATION);
        }



}

void AE::initialize(int stage) {
    number_of_replies = 0;
    successRate = registerSignal("number_replies");
    switch (stage) {
    case InitStage::INIT_REGISTRATION:
        registration();
        break;
    case InitStage::INIT_QUERY:
        sendQuery();
        break;
    default: {
        EV_FATAL << "Unknown initialization phase!\n";
        break;
    }
    }
} // end of initialize

void AE::sendAEMessage(int op_code) {
    // this function we set the fields of an AEMessage with respect to op_code
    switch (op_code) {
    case CANCELLATION: {
        AEMessage *regMsg = new AEMessage("C");
        // set the message fields
        regMsg->setURI(URI);
        regMsg->setData(data);
        regMsg->setOp_code(CANCELLATION);
        regMsg->setFeature_type(feature_type.c_str());
        //send to the output gate of the AE $o as output and 0 is the number
        sendDelayed(regMsg, simTime() + this->warmup + (std::rand()%5), "cse$o", 0);
        break;
    }
    case REGISTRATION: {
        AEMessage *regMsg = new AEMessage("Rg");
        // set the message fields
        regMsg->setURI(URI);
        regMsg->setFeature_type(feature_type.c_str());
        regMsg->setData(data);
        regMsg->setOp_code(REGISTRATION);
        SimTime t;
        t.setRaw(1);
        //send to the output gate of the AE $o as output and 0 is the number
        sendDelayed(regMsg, simTime() + exponential(t), "cse$o", 0);
        break;
    } // end of REGISTRATION case
    case QUERY: {
        AEMessage *queryMsg = new AEMessage("Q");
        queryMsg->setURI(URI);
        queryMsg->setQueryID(queryIndex++);
        queryMsg->setFeature_type(std::to_string(std::rand() % resource_pool_size).c_str());
        queryMsg->setOp_code(QUERY);
        queryMsg->setMaxHop(maxHop);

        sendDelayed(queryMsg, simTime() + this->warmup, "cse$o", 0);
        break;
    } // end of QUERY case

    default:
        break;
    } // end of switch

} // end of function sendAEMessage

void AE::handleMessage(cMessage *msg) {
    static int NumOfReplies = 0;
    //AE will receive the response
    //AEMessage *responseMsg = check_and_cast<AEMessage *>(msg);
    discoveryMessage *responseMsg = check_and_cast<discoveryMessage*>(msg);
    EV << "AE receives a response" << "\n";
    int number_of_response=0;
    number_of_response++;

    if (responseMsg->getReturnCode() == ResultCode::SUCCESS) {
        number_of_successfulResponse= 0;
        number_of_successfulResponse++;

        EV << "Resource of type " << responseMsg->getFeature_type()
                  << " found in " << responseMsg->getURI_init() << "\n";

    }

    if (responseMsg->getReturnCode() == ResultCode::NOT_FOUND) {

        EV << "Resource of type " << responseMsg->getFeature_type()
                  << " not found in" << responseMsg->getURI_init() << "\n";
    }

    number_of_replies++;
    emit(successRate, number_of_replies);
    delete responseMsg;
}
