#include "arcDefn.h"
#include <errno.h>
#include <signal.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/timeb.h>

int             numOfRetries = DEFAULT_RETRIES;
int             retryTimeout = DEFAULT_TIMEOUT;
int             localExeOption = DEFAULT_LOCAL_EXEC;

RibData         Buffer[MAXRIBS]; 
ResultCheck     Cdata[MAXRIBS];
int             RibId= 0;
LocalLockArr    *lockList = NULL;
LocalLockArr    *lockList1 = NULL;
int             curLockId = -1;
int             msgQueueId;
int             pid;
String          str;
time_t          presentTime;

int             progNum;
int*            progNum1;

MagicNumber     magicNum;


static struct timeval TIMEOUT = {600, 0};

void PostArgs();
void GetResults();

/** Set parameters for the program ***/
void SetParam( int timeout, int retry, int localexec) {
    retryTimeout = timeout;
    numOfRetries = retry;
    localExeOption = localexec;
}

/*** Set Params for a RIB ***/
void SetRibParam( int ribId) {
    Buffer[ribId].Timeout = retryTimeout;
    Buffer[ribId].Retries = numOfRetries;
    Buffer[ribId].LocalExec = localExeOption;
}

/*Called by the user program to obtain the locks needed for the computation  */
int GetLocks( int num) {
    //printf("Before acquire locks \n");
    lockList1 = AcquireLocks(num);
    lockList = appendLocalLockArr( lockList, lockList1);
    // AcquireLocks is in rpclib.c
    //printf("I am in getlocks \n");
    if(lockList == (LocalLockArr*) NULL )  {
		return 0;
	}

    return lockList->LocalLockArr_len;
}

void SetLockFree( lockId) {
    lockList->LocalLockArr_val[lockId].Status = FREE;
}

int GetCurLockId() {
    return curLockId;
}

void IncrCurLockId() {
   int       i = 0;

   while( ( i < lockList->LocalLockArr_len) && 
            (lockList->LocalLockArr_val[i].Status == ALLOCATED)) {
        i++;
   }
   
   if( i < lockList->LocalLockArr_len) {
        curLockId = i; 
   }
   else {
        curLockId = -1; 
   }
   return;
}

MagicNumberArr * GetCurMagicNums( int num) {
    int                 i ;
    int                 j ;
    MagicNumberArr      *arr;
    MagicNumber         *magicNum;

    arr = (MagicNumberArr*) malloc( sizeof(MagicNumberArr));

    arr->MagicNumberArr_len = num;
    arr->MagicNumberArr_val = (MagicNumber *) malloc 
                                ( num * sizeof(MagicNumber));

    /* num is the number of locks for which we need to get
       magic numbers */
      
    //printf("I am in GetCurMagicNums \n");
    j = 0;
    i=0;

    /* Check the entire locklist */

    if( lockList != NULL) {

        printf("In method GetCurMagicNums , locklist is not null \n");

        /* Loop till the end of the locklist is reached or the
           required number of locks are obtained */
        while(( i < lockList->LocalLockArr_len) && ( j < num)) {

                 /* If the status of the lock is free, increment j */
            if( lockList->LocalLockArr_val[i].Status == FREE) {

                CopyMagicNum( &(arr->MagicNumberArr_val[j]),
                    &(lockList->LocalLockArr_val[i].LockInf.MagicNum));
                //printf("Magic number value of free lock %lf\n",
                        //lockList->LocalLockArr_val[i].LockInf.MagicNum);
                j++;
            }
            i++;
        }
    }

    /* Get the magic number by calling calculate_curr_magic_number which 
       computes the load of the machine ...........*/

    magicNum = CalculateMagicNum();
    //printf("Magic number of local machine %lf \n", magicNum);

    for( i = j; i < num; i++) {
        CopyMagicNum( &(arr->MagicNumberArr_val[i]),magicNum); 
    }

    return arr;
}

IntArr* getRibId( int nodeId) {
    int     i;
    int     j = 0;
    IntArr  *arr;

    arr = (IntArr *) malloc( sizeof(IntArr));

    if( lockList == NULL) {
        arr->IntArr_len = 0;
        arr->IntArr_val = NULL;
    }

    arr->IntArr_val = (int *) malloc( lockList->LocalLockArr_len * 
                                            sizeof(int));

    /* Returns the RIB id associated with this node from the 
       lock allocated to this node */

    for( i = 0; i < lockList->LocalLockArr_len; i++) {

        if( (lockList->LocalLockArr_val[i].LockInf.NodeId == nodeId)
                && ( lockList->LocalLockArr_val[i].Status == ALLOCATED)) {

            arr->IntArr_val[j] = lockList->LocalLockArr_val[i].RibId;
            j++;
        }
    }

    arr->IntArr_len = j;

    return arr;
}

int PostedAlready( int nodeId, int funcType) {
    int i;
    
    /* To Find out whether u have deposited the RIB function already*/

    for( i = 0; i < RibId ; i++) {

        /* If the function type is matching and NodeId allocated for
           lock is matching with the nodeId passed */
         
        if( (Buffer[i].FuncType == funcType) &&
          (lockList->LocalLockArr_val[Buffer[i].LockId].LockInf.NodeId == nodeId)) {

            if( Buffer[i].Status != Terminated) {
                return 1; /* If the program is still running */
            }
        }
    }
    return 0;
}

/* Register the source program and record the name of the 
 * machine on which the ARC program is being run 
 */
void ArcStartUp() {
    char server[50];

    if( (msgQueueId = msgget( MKEY1, 0)) < 0) {
        printf( "client can't msgget queue1 \n");
    }

    pid = getpid();

    RegisterProgram(pid);
    //printf("Program is %d \n ",pid);
    GetHostNameAndIPadd( NULL, server);
    //printf("Running on %s \n",server);
    str.String_len = strlen(server);
    str.String_val = (char *) malloc( str.String_len * sizeof(char));
    strcpy( str.String_val, server);
}

void ArcCleanUp() {
    int ribId;
    RibInf  ribInf; 
    CLIENT* cl;
    char    server[50];
    int     clntFlag = 1;
    static struct timeval  timeout = {0,0};

    GetHostNameAndIPadd(NULL, server); 
    UnRegisterProgram(pid);
}

u_long PostRibServer( ribId, pid) {
    CLIENT*     cl;
    char        server[50];
    RibInf      ribInf;
    int         size;

    if( Buffer[ribId].LockId >= 0) {
		/* Check if lock corresponds to the RibId of the
               current node */
        /* If RIB has already been allocated to the lock then 
           set ribInf.NodeId = currentNodeId */
        //printf(".........LockId is .......%d  \n",Buffer[ribId].LockId);

        ribInf.NodeId = lockList->LocalLockArr_val[Buffer[ribId].LockId].LockInf.NodeId;

        Cdata[ribId].RibId=ribId;
        Cdata[ribId].NodeId=ribInf.NodeId;

        //printf("RibId %d on Node Id %d with NodeName %s \n",ribId,ribInf.NodeId, lockList->LocalLockArr_val[Buffer[ribId].LockId].IPAddress.String_val);

    }
    else {
        ribInf.NodeId = -1;
    }

    ribInf.FuncName.String_len = strlen(Buffer[ribId].FuncName);
    ribInf.FuncName.String_val = (char *) malloc (ribInf.FuncName.String_len *
                        sizeof(char));
    strcpy( ribInf.FuncName.String_val, Buffer[ribId].FuncName);
    //printf("Function name of RIB %s\n", ribInf.FuncName.String_val);

    ribInf.Directives.String_len = strlen(Buffer[ribId].Directives);
    ribInf.Directives.String_val = (char *) malloc (ribInf.Directives.String_len *
                        sizeof(char));
    strcpy( ribInf.Directives.String_val, Buffer[ribId].Directives);
    //printf("RIB Directives %s\n", ribInf.Directives.String_val);
    ribInf.Pid = pid;
    ribInf.Files.StrArr_len = 0;
    ribInf.Files.StrArr_val = NULL;

    GetHostNameAndIPadd(NULL, server ); 
    if(( ribInf.NodeId >= 0 ) && (!PostedAlready(ribInf.NodeId, 
            Buffer[ribId].FuncType))) {

        if ((cl = clnt_create(server, COORDINATOR, USER_INTERACTION, "tcp"))
                == NULL) {
            printf("Cannot create connection with server for posting RIBs \n");
            clnt_pcreateerror(server);
        }

        /* Post the rib_inf on to the server if it is not 
           already posted */

        if (clnt_call(cl, POST_RIB_INF, xdr_RibInf, &ribInf,
                xdr_u_long, &progNum, TIMEOUT) != RPC_SUCCESS) {
            printf("Failing to post rib info ...\n");
            clnt_perror( cl, server);
        }

        Cdata[ribId].ProgNum= progNum; 
        //printf("Pid................%d...\n",pid);
        //printf("ProgNum..to be posted ...is the one.......%d...\n",progNum);
    }

    else {
        if( ribInf.NodeId < 0) {
            if ((cl = clnt_create(server, COORDINATOR, SERVER, "tcp"))
                == NULL) {
                clnt_pcreateerror(server);
            }   
        }

        else {
            if ((cl = clnt_create(lockList->LocalLockArr_val[Buffer[ribId].LockId].IPAddress.String_val, 
                    COORDINATOR, SERVER, "tcp")) == NULL) {
                clnt_pcreateerror(server);
            }
        }

        if (clnt_call(cl, PROCESS_RIB, xdr_RibInf, &ribInf,
                xdr_u_long, &progNum, TIMEOUT) != RPC_SUCCESS) {
            printf("Failing to process RIB ...\n");
            clnt_perror( cl, server);
        }
    }
    clnt_destroy(cl);

    return( progNum);
}

void UnLock() {
    CLIENT* cl;
    int i;

    if ((cl = clnt_create(str.String_val, 
           COORDINATOR, USER_INTERACTION, "tcp")) == NULL) {
        clnt_pcreateerror(str.String_val);
    }   

    if (clnt_call(cl, UNLOCK, xdr_LocalLockArr, lockList,
                xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
        clnt_pcreateerror( str.String_val);
    }
    clnt_destroy(cl);
}

int messageHandler(void *pos, int size) {
    int     i;
    int     j;
    int     len;
    int     rib=-1;
    Mesg        msg;
    ResultMesg  resmsg;
    ErrorMesg   errmsg;
    FailureMesg failmsg;
    IntArr*     arr;
    extern      errno;

/*    printf( "In message Handler \n"); */

    pos = (void *) malloc (size);
    if( msgrcv ( msgQueueId, (char *)&msg.mtype, MAXMESGDATA, 0, 0) > 0) {
        if( msg.msubtype == RESULT) {
             if(MAXMESGDATA > 0) {
                  resmsg = *(( ResultMesg *)&msg);
                  //printf(".... Rib received is %d....\n",resmsg.ribId);
                  //printf(".....program num ...%d  \n",resmsg.prognum);
                  //printf(".....program num  %d stored for ribid ...%d  \n",resmsg.prognum,resmsg.ribId);
                  if(resmsg.ribId <= RibId) {
                       GetResults( resmsg.ribId, resmsg.prognum);    
                       Cdata[resmsg.ribId].RibId = resmsg.ribId;
                       SetLockFree(Buffer[resmsg.ribId].LockId);  
                       rib=resmsg.ribId;

                       if(Buffer[resmsg.ribId].Status == Available) {
                            memcpy(pos,Buffer[resmsg.ribId].Results,size);
                            //printf("Results retrieved for Rib %d ..... \n",resmsg.ribId);
                       }
                  }
             }
        }

        else {
        if( msg.msubtype == ERROR) {
            errmsg = *(( ErrorMesg *)&msg);
            error("Error: %s\n", errmsg.error);
            exit(PROGRAM_ERROR);
        }
        else {
        if( msg.msubtype == NODEFAILURE) {
            printf("Node failed  .......\n");
            failmsg = *((FailureMesg *)&msg);
            arr = getRibId( failmsg.nodeId);

            for(i = 0; i < arr->IntArr_len; i++) {
                time(&presentTime);
                Buffer[arr->IntArr_val[i]].RetriesMade++;

                /* Check if value of timeout has reached or not */

                if(((presentTime - Buffer[arr->IntArr_val[i]].StartTime) 
                        < Buffer[arr->IntArr_val[i]].Timeout)  &&
                        (Buffer[arr->IntArr_val[i]].RetriesMade 
                         <= Buffer[arr->IntArr_val[i]].Retries)) {
                    Buffer[arr->IntArr_val[i]].RetriesMade++;
                    /* Try to get a free lock */
                    Buffer[arr->IntArr_val[i]].LockId = GetCurLockId();

                    /* If u get a free lock then set its status to allocated */

                    if( Buffer[arr->IntArr_val[i]].LockId >= 0) {
                        lockList->LocalLockArr_val[Buffer[arr->IntArr_val[i]].LockId].Status = ALLOCATED;
                        lockList->LocalLockArr_val[Buffer[arr->IntArr_val[i]].LockId].RibId = arr->IntArr_val[i];
                    }

                    /* Get the program id by posting the RIB onto the machine from which
                       u had got locks */

                    Buffer[arr->IntArr_val[i]].ProgNum = 
                    PostRibServer(arr->IntArr_val[i], getpid());

                    sleep(3);

                    PostArgs( arr->IntArr_val[i]);
                }
            }
        }
        }
        }
    }
    else {
        if(errno == ENOMSG)
             return rib;
        else 
             printf("error \n");
    }
    return rib;
}


        /* Here the code for SYNC FUNC CALL will come */ 
void WaitOnSync(int ribId, void* pos, int size) {

    int rib=-1;

        time(&presentTime);
        while(((presentTime - Buffer[ribId].StartTime) < Buffer[ribId].Timeout) 
                && (Buffer[ribId].RetriesMade <= Buffer[ribId].Retries)) {
            if ( Buffer[ribId].Status == Available) {
                memcpy (pos, Buffer[ribId].Results, size); 

                if(  Buffer[ribId].LockId > -1) {
                    SetLockFree(  Buffer[ribId].LockId); 
                }
                break;
            }
            rib = messageHandler(&pos,size);
        }

        if ( Buffer[ribId].Status == NotAvailable) {
            if(Buffer[ribId].LocalExec) {
                Buffer[ribId].LockId = -1; 
                 Buffer[ribId].ProgNum = PostRibServer(ribId, getpid());
                 sleep(1);
                 PostArgs( ribId);

                 while( Buffer[ribId].Status != Available) {
                     rib=messageHandler(&pos,size);

                     if( Buffer[ribId].Status == Available) {
                         memcpy (pos, Buffer[ribId].Results, size); 
                     if    (  Buffer[ribId].LockId > -1) {
                             SetLockFree(  Buffer[ribId].LockId); 
                         }
                     }
                 }
            }
            else {
                printf( "error: could not complete rib execution \n");
                exit(1);
            }
        } 
}

void WaitOnSyncAnd( int num, ...) {
    int     i;
    int*    ribId;
    void**  pos;
    int*    size;
    int*    status;
    int     flag = 0;
    va_list ap;
    int     Timeout = DEFAULT_TIMEOUT;
    int     rib;

    va_start(ap, num);

    ribId = (int* ) malloc( num * sizeof(int));
    pos   = (void**) malloc ( num * sizeof( void*));
    size = (int *) malloc ( num * sizeof(int));
    status = (int *) malloc ( num * sizeof(int));

    for( i = 0; i < num; i++) {
        ribId[i] = va_arg(ap, int);
        pos[i]  = va_arg(ap, void*);
        size[i] = va_arg(ap, int);
        status[i] = 0;

        if( Timeout < (Buffer[ribId[i]].Timeout + Buffer[ribId[i]].StartTime)) {
            Timeout = Buffer[ribId[i]].Timeout + Buffer[ribId[i]].StartTime;
        }
    }

    va_end(ap);
    time(&presentTime);
    while(presentTime < Timeout)
    {
        for( i = 0; i < num; i++)
        {
            if (( Buffer[ribId[i]].Status == Available) &&( !status[i]))
            {
                status[i] = 1;
                memcpy (pos[i], Buffer[ribId[i]].Results, size[i]); 
                if(  Buffer[ribId[i]].LockId > -1) 
                {
                    SetLockFree(  Buffer[ribId[i]].LockId); 
                }
            }
            if (( Buffer[ribId[i]].Status != Available) 
               &&(Buffer[ribId[i]].RetriesMade <= Buffer[ribId[i]].Retries))
            {
                flag = 1;
            }
        }

        if( flag)
        {
            rib=messageHandler(&pos,size);
        }
    }

    flag = 1;
    for( i = 0; i < num; i++)
    {
        if (( Buffer[ribId[i]].Status != NotAvailable)
            && (!Buffer[ribId[i]].LocalExec)) 
        {
            flag = 0;
            break;
        }
    }

    if(!flag)
    {
        printf( "error: could not complete rib execution \n");
        exit(1);
    }


    for( i = 0; i < num; i++)
    {
        if ( Buffer[ribId[i]].Status != NotAvailable)
        {
            Buffer[ribId[i]].LockId = -1; 
             Buffer[ribId[i]].ProgNum = PostRibServer(ribId[i], getpid());
             sleep(1);
             PostArgs( ribId[i]);
        }
    }
    
    for( i = 0; i < num; i++)
    {
        if ( Buffer[ribId[i]].Status != NotAvailable)
        {
             while( Buffer[ribId[i]].Status != Available)
             {
                 rib=messageHandler(&pos,size); 
                if ( Buffer[ribId[i]].Status == Available) 
                {
                    memcpy (pos[i], Buffer[ribId[i]].Results, size[i]); 
                     if    (  Buffer[ribId[i]].LockId > -1) 
                     {
                         SetLockFree(  Buffer[ribId[i]].LockId); 
                     }
                 }
             }
        }
    }
}

MagicNumber* GetNewMagicNum(char* server) {
      CLIENT* cl;

      if((cl = clnt_create(server, COORDINATOR, GENERATOR,"tcp"))
                                 == NULL) {
              clnt_pcreateerror(server);
              return CLIENT_CONNECTION_FAIL;
      }


      if(clnt_call(cl, CALCULATE_CUR_MAGICNUM_VER2,xdr_void,NULL,
               xdr_MagicNumber, &magicNum, TIMEOUT) != RPC_SUCCESS) {
            clnt_perror(cl,server);
            return(RPC_TIMED_OUT);
      }

      clnt_destroy(cl);
      return &magicNum;
}

