/*!
    \param inputPackets - the vector of input packets
    \param[out] outputPackets - the vector of output packets
    \param[out] outputPacketsReverse - the vector of output packets to send to the BackEnds (not used)
    \param filterState - pointer to the filter state (not used)
    \param params - the current configuration settings for the filtern instance (not used)
    \param topology - the MRNet topology

    Initialize graphlib and the STAT Graph Routines. Create the log file.
*/
void filterInit(vector<PacketPtr> &inputPackets,
                vector<PacketPtr> &outputPackets,
                vector<PacketPtr> &outputPacketsReverse,
                void **filterState,
                PacketPtr &params,
                const TopologyLocalInfo &topology)
{
    char *logDir;
    char fileName[BUFSIZE], hostName[BUFSIZE];
    int intRet, mrnetOutputLevel;
    unsigned int i;
    graphlib_error_t graphlibError;

    graphlibError = graphlib_Init();
    if (GRL_IS_FATALERROR(graphlibError))
    {
        cpPrintMsg(STAT_GRAPHLIB_ERROR, __FILE__, __LINE__, "Failed to initialize graphlib\n");
        return;
    }
    statInitializeReorderFunctions();
    statInitializeBitVectorFunctions();
    statInitializeCountRepFunctions();
    statInitializeMergeFunctions();

    if (inputPackets[0]->get_Tag() == PROT_SEND_BROADCAST_STREAM)
    {
        for (i = 0; i < inputPackets.size(); i++)
        {
            if (inputPackets[i]->unpack("%uc %s %d", &gLogging, &logDir, &mrnetOutputLevel) == -1)
                cpPrintMsg(STAT_MRNET_ERROR, __FILE__, __LINE__, "failed to unpack packet\n");
            if (topology.get_Network()->is_LocalNodeInternal())
            {
                if (gLogging & STAT_LOG_CP)
                {
                    /* Create the log directory */
                    intRet = mkdir(logDir, S_IRUSR | S_IWUSR | S_IXUSR);
                    if (intRet == -1 && errno != EEXIST)
                        cpPrintMsg(STAT_FILE_ERROR, __FILE__, __LINE__, "%s: mkdir failed to create log directory %s\n", strerror(errno), logDir);
                    intRet = gethostname(hostName, BUFSIZE);
                    if (intRet != 0)
                        cpPrintMsg(STAT_WARNING, __FILE__, __LINE__, "Warning, Failed to get hostName\n");
                    snprintf(fileName, BUFSIZE, "%s/%s.STATfilter.%d.log", logDir, hostName, topology.get_Rank());
                    gStatOutFp = fopen(fileName, "w");
                    if (gStatOutFp == NULL)
                        cpPrintMsg(STAT_FILE_ERROR, __FILE__, __LINE__, "%s: fopen failed to open FE log file %s\n", strerror(errno), fileName);
#ifdef MRNET40
                    if (gLogging & STAT_LOG_MRN)
                        mrn_printf_init(gStatOutFp);
#endif
                    set_OutputLevel(mrnetOutputLevel);
                }
            }
            free(logDir);
        }
    }
    for (i = 0; i < inputPackets.size(); i++)
        outputPackets.push_back(inputPackets[i]);
}
void SightStreamAggregator(std::vector< PacketPtr > &packets_in,
        std::vector< PacketPtr > &packets_out,
        std::vector< PacketPtr > & /* packets_out_reverse */,
        void **state_data,
        PacketPtr & /* params */,
        const TopologyLocalInfo &inf) {

#ifdef DEBUG_ON
    fprintf(stdout, "[MRNet FILTER METHOD just started.. PID : %d ]\n", getpid());
    fflush(stdout);
#endif
    Network *net = const_cast< Network * >( inf.get_Network() );
    PacketPtr first_packet = packets_in[0];
    int stream_id = first_packet->get_StreamId();
    int tag_id = first_packet->get_Tag();
    Stream *stream = net->get_Stream(stream_id);
    set< Rank > peers;
    stream->get_ChildRanks(peers);
    //handle special BE case
    if (peers.size() == 0 && first_packet->get_InletNodeRank() == -1) {
        Rank r = -1;
        peers.insert(-1);
        //special BE optimization
        std::vector< PacketPtr >::iterator in;
        for( in = packets_in.begin() ; in != packets_in.end(); in++) {
            packets_out.push_back(*in);
        }
        return;

#ifdef DEBUG_ON
        fprintf(stdout, "[MRNet FILTER - case BE node.. PID : %d ]\n", getpid());
#endif
    }
    glst_t *state = initAndGetGlobal(state_data, stream, peers, net, stream_id, tag_id);
    state->prod->loadBuffer(packets_in, inf);

#ifdef DEBUG_ON
    printf("[MRNet FILTER method completed!...pid : %d ] \n", getpid());
#endif
}
/*!
    \param inputPackets - the vector of input packets
    \param[out] outputPackets - the vector of output packets
    \param[out] outputPacketsReverse - the vector of output packets to send to the BackEnds (not used)
    \param filterState - pointer to the filter state (not used)
    \param params - the current configuration settings for the filtern instance (not used)
    \param topology - the MRNet topology
*/
void fileRequestDownStream(vector<PacketPtr> &inputPackets,
                           vector<PacketPtr> &outputPackets,
                           vector<PacketPtr> &outputPacketsReverse,
                           void **filterState,
                           PacketPtr &params,
                           const TopologyLocalInfo &topology)
{
    char *fileContents, *fileName;
    unsigned long fileContentsLength;
    unsigned int i;
    PacketPtr currentPacket, newPacket;
    set<string>::iterator visitedFileSetIter;
    map<string, pair<char*, unsigned long> >::iterator fileNameToContentsMapIter;

    cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "File request down stream filter begin.\n");

    if (inputPackets[0]->get_Tag() == PROT_FILE_REQ)
    {
        cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "PROT_FILE_REQ\n");
        pthread_mutex_init(&gFileNameToContentsMapMutex, NULL);
        pthread_mutex_init(&gVisitedFileSetMutex, NULL);
        newPacket = inputPackets[0];
        outputPackets.push_back(newPacket);
    }
    else
    {
        cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "!PROT_FILE_REQ\n");

        for (i = 0; i < inputPackets.size(); i++)
        {
            currentPacket = inputPackets[i];
#ifdef MRNET40
            currentPacket->unpack("%Ac %s", &fileContents, &fileContentsLength, &fileName);
#else
            currentPacket->unpack("%ac %s", &fileContents, &fileContentsLength, &fileName);
#endif
            if (topology.get_Network()->is_LocalNodeInternal())
            {
                cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "caching contents of %s at CP length %lu\n", fileName, fileContentsLength);
                pthread_mutex_lock(&gFileNameToContentsMapMutex);
                fileNameToContentsMapIter = gFileNameToContentsMap.find(fileName);
                if (fileNameToContentsMapIter == gFileNameToContentsMap.end())
                    gFileNameToContentsMap[fileName] = make_pair(fileContents, fileContentsLength);
                pthread_mutex_unlock(&gFileNameToContentsMapMutex);
            }
            else
                free(fileContents);

            pthread_mutex_lock(&gVisitedFileSetMutex);
            visitedFileSetIter = gVisitedFileSet.find(fileName);
            if (visitedFileSetIter != gVisitedFileSet.end())
            {
                gVisitedFileSet.erase(visitedFileSetIter);
                pthread_mutex_unlock(&gVisitedFileSetMutex);
                cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "passing along contents of %s, length %lu at CP\n", fileName, fileContentsLength);
                outputPackets.push_back(inputPackets[i]);
            }
            else
            {
                cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "ignoring file %s at CP\n", fileName);
                pthread_mutex_unlock(&gVisitedFileSetMutex);
            }
            if (fileName != NULL)
            {
                free(fileName);
                fileName = NULL;
            }
        }
    }
    cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "File request down stream filter end.\n");
}
/*!
    \param inputPackets - the vector of input packets
    \param[out] outputPackets - the vector of output packets
    \param[out] outputPacketsReverse - the vector of output packets to send to the BackEnds (not used)
    \param filterState - pointer to the filter state (not used)
    \param params - the current configuration settings for the filtern instance (not used)
    \param topology - the MRNet topology
*/
void fileRequestUpStream(vector<PacketPtr> &inputPackets,
                         vector<PacketPtr> &outputPackets,
                         vector<PacketPtr> &outputPacketsReverse,
                         void **filterState,
                         PacketPtr &params,
                         const TopologyLocalInfo &topology)
{
    char *fileName = NULL;
    unsigned int i;
    PacketPtr currentPacket, newPacket;
    map<string, pair<char *, unsigned long> >::iterator gFileNameToContentsMapIter;

    cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "File request up stream filter begin.\n");

    if (inputPackets[0]->get_Tag() == PROT_FILE_REQ_RESP)
    {
        newPacket = inputPackets[0];
        outputPackets.push_back(newPacket);
    }
    else
    {
        for (i = 0; i < inputPackets.size(); i++)
        {
            currentPacket = inputPackets[i];
            currentPacket->unpack("%s", &fileName);
            pthread_mutex_lock(&gFileNameToContentsMapMutex);
            gFileNameToContentsMapIter = gFileNameToContentsMap.find(fileName);
            if (gFileNameToContentsMapIter != gFileNameToContentsMap.end())
            {
                pthread_mutex_unlock(&gFileNameToContentsMapMutex);
                cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "Sending cached result for %s in reverse.\n", fileName);
                PacketPtr newPacketReverse(new Packet(inputPackets[0]->get_StreamId(),
#ifdef MRNET40
                                           inputPackets[0]->get_Tag(), "%Ac %s", gFileNameToContentsMapIter->second.first,
#else
                                           inputPackets[0]->get_Tag(), "%ac %s", gFileNameToContentsMapIter->second.first,
#endif
                                           gFileNameToContentsMapIter->second.second, fileName));
                outputPacketsReverse.push_back(newPacketReverse);
            }
            else
            {
                pthread_mutex_unlock(&gFileNameToContentsMapMutex);
                pthread_mutex_lock(&gVisitedFileSetMutex);
                if (gVisitedFileSet.find(fileName) == gVisitedFileSet.end())
                {
                    gVisitedFileSet.insert(fileName);
                    pthread_mutex_unlock(&gVisitedFileSetMutex);
                    cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "Noting request for %s.\n", fileName);
                    outputPackets.push_back(inputPackets[i]);
                }
                else
                {
                    pthread_mutex_unlock(&gVisitedFileSetMutex);
                    cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "%d: Duplicate request for %s.\n", topology.get_Network()->get_LocalRank(), fileName);
                }
            }
            if (fileName != NULL)
            {
                free(fileName);
                fileName = NULL;
            }
        } /* for (i = 0; i < inputPackets.size(); i++) */
    } /* else for if (inputPackets[0]->get_Tag() == PROT_FILE_REQ_RESP) */

    cpPrintMsg(STAT_LOG_MESSAGE, __FILE__, __LINE__, "File request up stream filter end.\n");
}