#include "FEPSim.h"

/**
 * Constructor. Sets data values for RPC Client and socket server.
 * 
 * @param ATMSHost The IP of ATMS Server
 * @param FEP RPC program number
 * @param FEP RPC program revision number
 * @param Socket Server listen port
 */
FEPSim::FEPSim(char * ATMShost, int FEP_PROG, int FEP_REV, int SOCK_PORT) {
    this->ATMSHost = ATMShost;
    this->FEP_PROG = FEP_PROG;
    this->FEP_REV = FEP_REV;
    this->SOCK_PORT = SOCK_PORT;
    this->FEPLogFileName = "FEPSimLog.txt";    
    // clear log file
    FEPLogFile.open(FEPLogFileName, ios::trunc);
    FEPLogFile.close();
}

/**
 * Destructor: closes the log file if open
 */
FEPSim::~FEPSim() {
    FEPLogFile.close();
}

/**
 * Handler for the ATMS RPC Response (to the client RPC Call)
 * @param response pointer to fep_reply struct
 */
void FEPSim::handleCallResponse(void *response) {
    // Failed RPC Call 
    if (response == NULL) { 
        clnt_perror(clnt, "RPC call failed");
    }
    // Successful RPC Call
    else { 
        FEPLogFile << "Successful RPC call to ATMS..." << endl;
    }
}

/**
 * Sends an fep_reply for each station on the FEPLine. 
 * Gets highway status from recieved socket msg.
 * 
 * @param buffer The recieved highway status msg.
 */
void FEPSim::sendReplys(char * buffer) {
    HighwaysParser highwaysParser = HighwaysParser(buffer);
    vector<FEP_LINE*> lines = highwaysParser.lines;
    vector<STATION*> ldsMap = highwaysParser.stations;

    cout << "Starting sendReplys for " << lines.size() << " lines." << endl;
    // Send one reply for every FEPLine
    for (int i = 0; i < lines.size(); i++) {
        fep_reply fepReply;
        FEP_LINE * currLine = lines.at(i);
        // populate reply
        fepReply.reply = SHORTPOLL;
        fepReply.schedule = currLine->schedule;
        fepReply.lineinfo = currLine->lineInfo;
        fepReply.kind = (enum polltype) 0;
        fepReply.flag = (enum replykind) 0;

        fepReply.schedule_sequence = currLine->schedleSeq;
        fepReply.global_sequence = currLine->globalSeq;

        // using current unix time, may need to look at later
        fepReply.schedule_time = time(NULL);

        fepReply.user_info1 = currLine->lineNum;
        fepReply.user_info2 = currLine->lineNum;
        fepReply.system_key = currLine->systemKey;

        fepReply.answers.size = 1;
        fepReply.answers.fep_answer_list_u.shortp.count = 1;
	
        // construct a shortanswer for each station in line
        for (int j = 0; j < currLine->lds.size(); j++) {
            fep_shortanswer fsa;
            STATION * currStation = ldsMap.at(currLine->ldsIndex.at(j));

            // msg: oa, od, currStation.dataPack, od, ff
            fsa.msg.message_len = currStation->length + 2;
            fsa.msg.message[0] = 0x0d;
            fsa.msg.message[1] = 0x0a;
            for (int k = 0; k < currStation->length; k++) {
		//printf("Adding: %d %02X\n", k, currStation->dataPack[k]);	
                fsa.msg.message[2 + k] = currStation->dataPack[k];
            }
            int aa = currStation->length;
            fsa.msg.message[2 + aa] = 0x0d;
            fsa.msg.message[3 + aa] = 0xff;
            
            /*for(int l = 0; l < fsa.msg.message_len + 2; l++)
            {
                printf("%02X", (unsigned char) fsa.msg.message[l]);
            }
            printf("\n");*/
            
            // info
            fsa.info.poll_error_count = 0;
            fsa.info.poll_time = time(NULL);
            fsa.info.poll_user_info1 = currStation->drop; // drop number
            fsa.info.poll_user_info2 = 1; //always 1
            fsa.info.retries = 0;
            fsa.info.status = (enum replystatus) 1;

            fepReply.answers.fep_answer_list_u.shortp.answers[0] = fsa;
            // send out data
            
            FEPLogFile << "Sending fepReply for line #" << currLine->lineNum 
                    << " station number #" 
                    << ldsMap.at(currLine->ldsIndex.at(j)) << endl;
            
            // Transfer the station data to ATMS and listen for response
            handleCallResponse(fep_reply_xfer_32(&fepReply, clnt));
        }
        // pause to let ATMS server catch up
        //unsigned int sleepseconds = 220000;
        //usleep(sleepseconds);
    }
    cout << "Finished." << endl;
}

/**
 * Creates an RPC Client, and on successful creation, sends fep_replys to ATMS.
 * @param The recieved highway status msg.
 */
void FEPSim::manageClientConnection(char * buffer) 
{
    // Attempt to create RPC client
    if (createClient()) 
    {
        // Prepare and send the highway status as FEP replies
        sendReplys(buffer);
        FEPLogFile << "Destroying client..." << endl;
        clnt_destroy(clnt);
        int startTime = time(NULL);
        FEPLogFile << "time is: " << startTime << endl;
    }
}

/**
  * Creates an RPC Client to the ATMS Server. If unsuccessful, returns false
  * @return bool success
  */
bool FEPSim::createClient() {
    /* Create RPC Client to communicate with ATMS */
    bool success = true;

    clnt = clnt_create(this->ATMSHost, FEP_PROG, FEP_REV, "tcp");
    /* Check if client creation failed */
    if (clnt == (CLIENT *) NULL) {
        cerr << "Can't create client to " << this->ATMSHost << endl;
        cerr << "Verify you are connected to ATL network." << endl;
        success = false;
    } else {
        FEPLogFile << "Client created" << endl;
    }
    return success;
}

/**
 * Creates a socket server and awaits the highway status message from the
 * ATMS Driver. Upon receipt of the highway status message, creates an RPC
 * client and sends the fep_replys to the ATMS.
 */
void FEPSim::runSockServer() {
    int sockfd, newsockfd, portno, clilen;
    char buffer[BUFF_SIZE];
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    portno = this->SOCK_PORT;

    /* First call to socket() function */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0) {
        perror("ERROR opening socket");
        exit(1);
    }

    /* Initialize socket structure */
    bzero((char *) &serv_addr, sizeof (serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    /* Now bind the host address using bind() call.*/
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
        perror("ERROR on binding");
        exit(1);
    }

    /* Now start listening for the clients, here process will
     * go in sleep mode and will wait for the incoming connection
     */

    listen(sockfd, 5);
    clilen = sizeof (cli_addr);

    /* Accept actual connections from the client */
    while (1) {
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, (socklen_t *) & clilen);

        if (newsockfd < 0) {
            perror("ERROR on accept");
            exit(1);
        }

        /* If connection is established then start communicating */
        // zero out buffer
        bzero(buffer, BUFF_SIZE);
        
        // read XML from socket
        int totBytes = 0;
        while ((n = recv(newsockfd, &buffer[totBytes], sizeof (buffer), 0)) > 0) {
            totBytes += n;
        }

        if (n < 0) {
            perror("ERROR reading from socket");
            exit(1);
        }
        // open log file
        FEPLogFile.open(FEPLogFileName, ios::app);
        // send data to atms
        manageClientConnection(buffer);
        // close log file
        FEPLogFile.close();
        close(newsockfd);
    }
}
