
/*
 * For a detailed description see header file.
 */

#include "LossyCom.h"

#include "VMS_Implementations/VMS_impl/vmalloc.h"

/*
 * Initializes the central exchange structure.
 * Allocates memory to fit the number of endpoints.
 * Returns NULL if an error occurs.
 */
lossyCom__exchange_t* lossyCom__initialize(uint16_t numEndpoints)
{
    lossyCom__exchange_t* exchange;
    
    exchange = VMS_WL__malloc(sizeof(lossyCom__exchange_t));
    if(exchange == NULL)
        return NULL;
    
    exchange->triggerCounter = 0;
    exchange->numEndpoints = numEndpoints;
    exchange->outboxArray = VMS_WL__malloc(sizeof(lossyCom__msg_t)*numEndpoints);
    if(exchange->outboxArray == NULL){
        VMS_WL__free(exchange);
        return NULL;
    }
    
    return exchange;    
}

/*
 * Connects the local endpoint to the central exchange structure.
 * This registers a message handler that handles all the incomming messages.
 * Also an endpointID is set this ID has to be between 0 and total number of
 * endpoints-1 and the number has to be unique.
 */
void lossyCom__initialize_endpoint(lossyCom__endpoint_t* localEndpoint,
                                   lossyCom__exchange_t* centralExchange,
                                   lossyCom__endpointID_t endpointID,
                                   lossyCom__msgHandler msgHandler,
                                   void* msgHandlerData)
{
    localEndpoint->localTriggerCopy = 0;
    localEndpoint->endpointID = endpointID;
    localEndpoint->centralExchange = centralExchange;
    localEndpoint->msgHandler = msgHandler;
    localEndpoint->msgHandlerData = msgHandlerData;
}

/*
 * This broadcasts a message to all connected receivers
 */
void inline lossyCom__broadcastMsg(lossyCom__endpoint_t* localEndpoint,
                                   lossyCom__msgBody_t msg)
{
    lossyCom__sendMsg(localEndpoint,
                      BROADCAST_ID,
                      msg);
}

/*
 * This sends a message another endpoint. Again it is not guaranteed that the 
 * message is received. But in most cases it will.
 */
void inline lossyCom__sendMsg(lossyCom__endpoint_t* localEndpoint,
                       lossyCom__endpointID_t receiverEndpointID,
                       lossyCom__msgBody_t msg)
{
    lossyCom__msg_t msgDraft;
    uint16_t triggerCopy;
    
    //build message
    msgDraft = (0 | msg);
    msgDraft |= ((lossyCom__msg_t)receiverEndpointID << ENDPOINT_ID_SHIFT);
    
    triggerCopy = localEndpoint->centralExchange->triggerCounter +1;
    msgDraft |= ((lossyCom__msg_t)triggerCopy << TRIGGER_SHIFT);
    
    //write msg to central exchange
    localEndpoint->centralExchange->outboxArray[localEndpoint->endpointID] =
            msgDraft;
    
    localEndpoint->centralExchange->triggerCounter = triggerCopy;
    //printf("send updated trigger to %d\n", triggerCopy);
}

void inline lossyCom__receiveMsg(lossyCom__endpoint_t* localEndpoint)
{
    uint16_t currentTriggerCopy;
    lossyCom__endpointID_t senderEndpointID;
    lossyCom__endpointID_t receiverID;
    lossyCom__msg_t msgCopy;
    uint16_t msgTrigger;
    lossyCom__msgBody_t msgBody;
    
    senderEndpointID = 0;
    currentTriggerCopy = localEndpoint->centralExchange->triggerCounter;
    
    //new message arrived if trigger counter is higher than the last time read
    if(currentTriggerCopy > localEndpoint->localTriggerCopy)
    {
        while(senderEndpointID < localEndpoint->centralExchange->numEndpoints)
        {
            if(senderEndpointID != localEndpoint->endpointID)
            {
                msgCopy = localEndpoint->centralExchange->outboxArray[senderEndpointID];
                msgTrigger = 0xFFFF & (msgCopy >> TRIGGER_SHIFT);
                // check if the message is new (msg trigger > archived trigger)
                // and already valid (msgTrigger <= currentTriggerCopy)
                if(msgTrigger > localEndpoint->localTriggerCopy &&
                        msgTrigger <= currentTriggerCopy)
                {
                    //printf("receive search until %d \n", currentTriggerCopy);
                    //let the message handler parse the message
                    msgBody = 0xFFFFFFFF & msgCopy;
                    receiverID = 0xFFFF & (msgCopy >> ENDPOINT_ID_SHIFT);
                    //only receive broadcast and p2p for own receiverID
                    if(receiverID == BROADCAST_ID ||
                              receiverID == localEndpoint->endpointID)
                    {
                        (*(localEndpoint->msgHandler))(senderEndpointID,
                                                msgBody,
                                                localEndpoint->msgHandlerData);
                    }
                }
            }
            senderEndpointID++;
        }
    }
    //save last TriggerCounter of last parsed Msg
    localEndpoint->localTriggerCopy = currentTriggerCopy;
}