#include "FEPSim.h"

/**
 * Constructor
 * 
 * @param host The host rpc server ip address
 * @param networkFile the xml network file
 */
FEPSim::FEPSim(char * ATMShost) {
    this->ATMSHost = ATMShost;
}

/**
 * Destructor
 */
FEPSim::~FEPSim() {
    cout << "Destroying client..." << endl;
    clnt_destroy(clnt);
}

/**
 * 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 {
        cout << "Successful RPC call to ATMS..." << endl;
    }
}

void FEPSim::sendReplys(char * xml)
{
    NetworkReader networkReader = NetworkReader(xml);
    vector<FEP_LINE*> lines = networkReader.getLines();
    vector<STATION*> ldsMap = networkReader.getStations();
    
    // Send one reply for every FEPLine
    for (int i = 0; i < lines.size(); i++) {
        fep_reply fepReply;
        cout << "Sending fepReply for line #" << lines.at(i)->lineNum << endl;
        // populate reply
        fepReply.reply = SHORTPOLL;
        fepReply.schedule = lines.at(i)->schedule;
        fepReply.lineinfo = lines.at(i)->lineInfo;
        fepReply.kind = (enum polltype) 0;
        fepReply.flag = (enum replykind) 0;

        /***********************************
         * We might need to handle this on the java side if its an
         * issue
        lines.at(i).schedleSeq += 1;
        lines.at(i).globalSeq += 51;
         */

        fepReply.schedule_sequence = lines.at(i)->schedleSeq;
        fepReply.global_sequence = lines.at(i)->globalSeq;

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

        fepReply.user_info1 = lines.at(i)->lineNum;
        fepReply.user_info2 = lines.at(i)->lineNum;
        fepReply.system_key = lines.at(i)->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 < lines.at(i)->lds.size(); j++) {
            fep_shortanswer fsa;
            int index = lines.at(i)->ldsIndex.at(j);
            cout << "LDS index: " << index << endl;
            // msg: oa, od, ldsMap.at(index).dataPack, od, ff
            fsa.msg.message_len = ldsMap.at(index)->length + 2;
            fsa.msg.message[0] = 0x0d;
            fsa.msg.message[1] = 0x0a;
            for (int k = 0; k < ldsMap.at(index)->length; k++)
                fsa.msg.message[2 + k] = ldsMap.at(index)->dataPack[k];
            int aa = ldsMap.at(index)->length;
            fsa.msg.message[2 + aa] = 0x0d;
            fsa.msg.message[3 + aa] = 0xff;

            // info
            fsa.info.poll_error_count = 0;
            fsa.info.poll_user_info1 = ldsMap.at(index)->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
            printf("Transferring line=%d, lds_drop_no=%d...\n", lines.at(i)->lineNum, ldsMap.at(index)->drop);
            // Make RPC Call and handle response
            printf("Handling ATMS response...\n");
            handleCallResponse(fep_reply_xfer_32(&fepReply, clnt));
        }
    }
}

/**
 * Sends an fep_reply for every line in the FEP.
 */
void FEPSim::updateATMS(char * xml) {
    if(createClient(this->ATMSHost))
    {
        sendReplys(xml);
    }
}

/**
 * Creates the RPC Client. If not successful, exit with status 1.
 * @param host rpc server ip
 */
bool FEPSim::createClient(char * host) {
    /* Create RPC Client to communicate with ATMS */
    bool success = true;
    cout << "Creating RPC Client" << endl;
    clnt = clnt_create(host, /*100090,*/ 103121, 32, "tcp");
    /* Check if client creation failed */
    if (clnt == (CLIENT *) NULL) {
        cerr << "Can't create client to " << host << endl;
        success = false;
    }
    else {
        cout << "Client created" << endl;
    }
    return success;
}

/**
 * Main driver for ATMSCommunicator. Creates an RPC Client from command line args.
 * @param argc
 * @param argv
 * @return 
 */
int main(int argc, char *argv[]) {
    if(argc != 3)
    {
        cerr << "Usage: FEPSim <ATMS_Host> <FEP_ATMSDriver_PortNo" << endl;
        exit(1);
    }
    char *FEPSimHost = argv[1];    
    FEPSim fep = FEPSim(FEPSimHost);
    
    int sockfd, newsockfd, portno, clilen;
    char buffer[BUFF_SIZE];
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    portno = atoi(argv[2]);

    /* 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);
        }
        
        // send data to atms
        fep.updateATMS(buffer);
    }

    return 0;
}
