#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <rpc/rpc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include "extnDefn.h"
#include "LockList.h"
#include "LockReqList.h"
#include "../commonIncludes/msgq.h"

//#define PRINT

IntArr              pidArr;
extern int          clntFlag ;
LockRequest         lockRequest;
extern LockRequest  lockPerm;
extern LockList*    localLockList;
LockReqList*        requestedList = NULL;
String              localStr;
extern int          msgQueueId;  
extern long         GetProgNum();
extern void         ExecuteFile();
extern MagicNumber* Calculate_cur_magicnum();

static struct timeval TIMEOUT = { 50, 0 };
int     retVal;
u_long  progNum;
u_long*  progNum1;

//Function prototypes
int PostFile( String filename, CLIENT* cl);
void copy_lockList_Into_lockRequestList() ;
void call_set_Lock_Allocated_RPC(
                LocalLockArr *Available_ptr , 
                int* pos ) ;

void createAvailableList( 
            int* pos , 
            LocalLockArr *Available_ptr , 
            int *lock_ptr) ;
LockArr* getLocalLockInfoArray() ;

LockArr* getRemoteLockInfoArray();
void call_AcquireLocks_RPC( int* pos) ;

LocalLockArr* acquireLocks(
                int* arg , 
                int* lockId , 
                int* numOfLocksWanted , 
                LockArr* globalLockInfoArray , 
                LockReqInf lockReqInf ,
                int *pos ) ;

LockArr* mergeLockInfoArrays(LockArr* firstArray , LockArr* secondArray) ;

/*
get_locks_available_5()
-----------------------

 Arguments:  arg, the number of locks required;

This function returns an array of locks which could be acquired.
The number of locks returned could be less than or equal to the 
argument passed, depending on the availibility..
*/

#ifdef NEW_VER_RPC

LocalLockArr* get_locks_available_5_svc( 
                    int* arg, 
                    struct svc_req *junk2)

#else

LocalLockArr* get_locks_available_5( int* arg)

#endif

{
    LockArr            *localLockInfoArray;
    LockArr            *remoteLockInfoArray;
    LockArr            *globalLockInfoArray;
    LocalLockArr    *locksAvailable;  
    LockReqInf      lockReqInf;
    LockReqList*    result;
    int             i;
    int             j;
    int             pos;
    int             numOfLocksWanted;
    int             lockId;
 
        //Get the list of locks from remote coordinators.
    remoteLockInfoArray = getRemoteLockInfoArray();

        //Get the list of local locks
    localLockInfoArray = getLocalLockInfoArray();

        //Merge the two lists to get a new list that contains
        //the list of all the free locks available in the ARC
        //system.
    globalLockInfoArray =  mergeLockInfoArrays( 
                                    remoteLockInfoArray , 
                                    localLockInfoArray );

        // If no locks are available on any of the machines 
    if( globalLockInfoArray == NULL) {
#ifdef PRINT

     PrintInLogFile( "No locks available \n");

#endif

        printf("No locks available \n");
         //Return empty list.
           locksAvailable = 
                ( LocalLockArr *) malloc( sizeof(LocalLockArr));
           locksAvailable->LocalLockArr_len = 0;
           locksAvailable->LocalLockArr_val = 0;
           return locksAvailable;
    }

       if( locksAvailable != NULL) {
           free( locksAvailable);
           locksAvailable = NULL;
    }

    //If the total number of locks available are less that the number
    //requested.
    if( *arg >  globalLockInfoArray->LockArr_len) {
           *arg = globalLockInfoArray->LockArr_len;
    }

    numOfLocksWanted = *arg;

    // choose locks from remoteLockInfoTable and localLockList
    // list in requestedList and lockRequest
    // find where requested list is set ????
    if( requestedList  == NULL) {
           requestedList = (LockReqList *) malloc (sizeof(LockReqList));
           SetLockReqInfNull(requestedList);
    }

    lockId = 0;

    //Acquire the locks
    ////////////////////////////////////////////////////////////////    
    locksAvailable = acquireLocks(
                        arg , 
                        &lockId , 
                        &numOfLocksWanted , 
                        globalLockInfoArray , 
                        lockReqInf,
                        &pos);
    ////////////////////////////////////////////////////////////////    
#ifdef PRINT
        PrintInLogFile( "final list of locks available :::\n");
#endif

    PrintLocalLockArr( locksAvailable);

    if(locksAvailable == NULL) {
        locksAvailable = ( LocalLockArr *) malloc( sizeof(LocalLockArr));
        locksAvailable->LocalLockArr_len = 0;
        locksAvailable->LocalLockArr_val = 0;
    }
    return( locksAvailable);
}

void copy_lockList_Into_lockRequestList(
                int numOfLocksToCopy,
                LockReqInf lockReqInf ,
                LockArr *globalLockInfoArray ,
                int* lockId) {

        int i,j;

        //Copy numOfLocksToCopy locks from globalLockInfoArray 
        //to the lockRequest
        for( i = 0; i < numOfLocksToCopy; i++) {
            lockRequest.LockList.LockArr_val[i].NodeId = lockReqInf.NodeId
                =  globalLockInfoArray->LockArr_val[i + *lockId ].NodeId;

            lockRequest.LockList.LockArr_val[i].LockId = lockReqInf.LockId
                =  globalLockInfoArray->LockArr_val[i + *lockId].LockId;

            lockRequest.LockList.LockArr_val[i].MagicNum.hpf
                =  globalLockInfoArray->LockArr_val[i + *lockId].MagicNum.hpf;

            printf("UserInteraction.c : Magic number of the remote
                    machine is %lf \n",
                    globalLockInfoArray->LockArr_val[i + *lockId].MagicNum.hpf);
            PrintInResult("UserInteraction.c : Magic number of the remote
                    machine is %lf \n",
                    globalLockInfoArray->LockArr_val[i + *lockId].MagicNum.hpf);

            //Also set the permission mode to NOACCESS
            lockRequest.Perm.IntArr_val[i] = NOACCESS;

            lockReqInf.Len  =  globLastIndex;

            if( lockReqInf.ReplyCount != NULL) {
                /*
                free(lockReqInf.ReplyCount);
                */
            }
            lockReqInf.ReplyCount =  (int *) malloc(globLastIndex *
                                            sizeof( int));

            //Set the permissions for all the live nodes
            //( as seen from the node-array) as NOACCESS.
            for(j = 0; j < globLastIndex; j++) {

            	//Ignore self and also those machines that are down or have 
            	//failed.
                if(( globNodeArr.NodeArr_val[j].NodeId >= 0)
                     && (globNodeArr.NodeArr_val[j].NodeId != globMyId)) {
                    lockReqInf.ReplyCount[j]  = NOACCESS;
                }
                else {
                    lockReqInf.ReplyCount[j]  = ACCESS;
                }
            }
            PutInLockReqList(requestedList, lockReqInf, 100);
        }
}

void call_AcquireLocks_RPC(int* pos) {
    int i , j;
    LockReqList* result;
    CLIENT* cl;

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

            //Ignore self and also those machines that are down or have 
            //failed.
        if((globNodeArr.NodeArr_val[i].NodeId >= 0)
               && (globNodeArr.NodeArr_val[i].NodeId != globMyId)) {

            if ((cl = clnt_create(
                        globNodeArr.NodeArr_val[i].IPaddress.String_val,
                        COORDINATOR, GENERATOR,"tcp")) == NULL) {

                  clnt_pcreateerror(
                        globNodeArr.NodeArr_val[i].Name.String_val );
                  clntFlag = 0;
            }

            if( !clntFlag) {
                clntFlag = 1;
                for( j = 0; j < lockRequest.Perm.IntArr_len; j++) {

                    result = GetLockReqLink(requestedList, &pos,
                        lockRequest.LockList.LockArr_val[j].NodeId,
                        lockRequest.LockList.LockArr_val[j].LockId,
                        lockRequest.SeqNum);

                    result->Data.ReplyCount[i] =  ACCESS;
                }
                continue;
            } // end if
 
#ifdef PRINT
    PrintInLogFile("%d %d %d %d ", lockRequest.NodeId,
                lockRequest.LockList.LockArr_val[0].LockId,
                lockRequest.Perm.IntArr_val[0],
                lockRequest.Perm.IntArr_val[1]);
#endif

            if (clnt_call(cl, ACQUIRE_LOCKS, xdr_LockRequest,
                 (char *)&lockRequest, xdr_LockRequest, 
                 (char *)&lockPerm, TIMEOUT) != RPC_SUCCESS) {

                clnt_perror(cl, globNodeArr.NodeArr_val[i].Name.String_val);
            }

#ifdef PRINT
     PrintInLogFile("%d %d %d %d ", lockPerm.NodeId,
                     lockPerm.LockList.LockArr_val[0].LockId,
                     lockPerm.Perm.IntArr_val[0],
                     lockPerm.Perm.IntArr_val[1]);
#endif

             /* store results */

            for( j = 0; j < lockPerm.Perm.IntArr_len; j++) {

                result = GetLockReqLink(requestedList, &pos,
                    lockPerm.LockList.LockArr_val[j].NodeId, 
                    lockPerm.LockList.LockArr_val[j].LockId, 
                    lockPerm.SeqNum);

                result->Data.ReplyCount[lockPerm.NodeId] 
                    =  lockPerm.Perm.IntArr_val[j];
            }

                clnt_destroy(cl);
        }
    } // end of for loop
}

void call_set_Lock_Allocated_RPC(
                LocalLockArr *Available , 
                int* pos ) {

    int i;
    void*           dum;
    LockList* tempLockList;
    CLIENT *cl;

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

            //If it is the local lock, then set its status as ALLOCATED
            //locally.
            if( Available->LocalLockArr_val[i].LockInf.NodeId == globMyId) {

                if ((tempLockList = GetLockLink( localLockList, &pos, 
                    Available->LocalLockArr_val[i].LockInf.LockId))
                    == NULL) {
                    error(" set_lock_free: lock non exixting");
                }
                else {
                    (tempLockList->Data).Status = ALLOCATED; 
                }
            }

            //For the remote locks, call the RPC service to set the
            //status of locks as ALLOCATED.
            else {
                if ((cl = clnt_create(
                            globNodeArr.NodeArr_val[
                            Available->LocalLockArr_val[i].LockInf.  NodeId].
                            IPaddress.String_val,
                            COORDINATOR, 
                            ADMINISTRATION,
                            "tcp")) == NULL) {

                    clnt_pcreateerror(globNodeArr.NodeArr_val[
                            Available->LocalLockArr_val[i].LockInf.NodeId].
                            Name.String_val );

                    clntFlag = 0;
                }

                if(!clntFlag) {
                    clntFlag = 1;
                    continue;
                }

                if (clnt_call(cl, SET_LOCK_ALLOCATED, xdr_Lock,
                     (char *)&Available->LocalLockArr_val[i].LockInf, 
                     xdr_void, dum, TIMEOUT) != RPC_SUCCESS) {

                    clnt_perror(cl, globNodeArr.NodeArr_val[
                            Available->LocalLockArr_val[i].LockInf.NodeId].
                            Name.String_val);
                }
            }
        }
}

void createAvailableList( 
                int* pos ,
                LocalLockArr *Available ,
                int *locks) {

    int i , j;
    LockReqList*    result;

        for( i = 0; i < lockRequest.LockList.LockArr_len; i++) {

            result = GetLockReqLink(requestedList, &pos,
                lockRequest.LockList.LockArr_val[i].NodeId, 
                lockRequest.LockList.LockArr_val[i].LockId, 
                lockRequest.SeqNum);

            for( j = 0; j < globLastIndex; j++) {

            	//Ignore self and also those machines that are down or have 
            	//failed.
                if((globNodeArr.NodeArr_val[j].NodeId >= 0)
                     && (globNodeArr.NodeArr_val[j].NodeId != globMyId)) {
                        
                        //Check for the access permissions of each lock
                    if(result->Data.ReplyCount[j] == NOACCESS) {
                        break;
                    }
                }
            }

            //If the access permissions are ACCESS for the lock with 
            //respect to all the the nodes,
            if( j == globLastIndex) 

                CopyLock( &(Available->LocalLockArr_val[*locks].LockInf),
                    &( lockRequest.LockList.LockArr_val[i]));

                PutInString( &(Available->LocalLockArr_val[*locks].IPAddress), 
                    globNodeArr.NodeArr_val[lockRequest.LockList.
                        LockArr_val[i].NodeId].IPaddress.String_val);

                //printf(" ON machine %s ..............\n",
                        //Available->LocalLockArr_val[*locks].IPAddress.
                        //String_val);
                //printf(" Magic number of machine %lf ..\n",
                        //Available->LocalLockArr_val[*locks].
                        //LockInf.MagicNum.hpf);
                PrintInResult(" ON machine %s ..............\n",
                        Available->LocalLockArr_val[*locks].IPAddress.String_val);
                PrintInResult(" Magic number of machine %lf ..\n",
                        Available->LocalLockArr_val[*locks].LockInf.MagicNum.hpf);

                Available->LocalLockArr_val[*locks].Status = FREE;
                Available->LocalLockArr_val[*locks].RibId = -1 ;
                Available->LocalLockArr_val[*locks].SeqNum = 
                        lockRequest.SeqNum ;

#ifdef PRINT
    PrintInLogFile("Coming here %d \n", 
                    Available->LocalLockArr_val[*locks].LockInf.LockId);
#endif

                *locks++;
            }
        }

LockArr* getLocalLockInfoArray() {
    return GetLockInf();
}

LockArr* mergeLockInfoArrays(LockArr* firstArray , LockArr* secondArray) {
        return appendLockArr(firstArray , secondArray);

}



LockArr* getRemoteLockInfoArray( ) {
    int i;
    CLIENT* cl;
    LockArr remoteCoordinatorLockArr;  
    LockArr*    remoteLockInfoArray;

    if(remoteCoordinatorLockArr.LockArr_val != NULL) {
        free( remoteCoordinatorLockArr.LockArr_val);
        remoteCoordinatorLockArr.LockArr_val = NULL;
    }

    //Initialize the lock-array.
    remoteLockInfoArray = ( LockArr *) malloc( sizeof(LockArr));
    remoteLockInfoArray->LockArr_len = 0;
    remoteLockInfoArray->LockArr_val = 0;

        /****** for each machine in the list asks for lock info */
    for(i = 0; i < globLastIndex; i++) {
            //Ignore self and also those machines that are down or have 
            //failed.
        if( (globNodeArr.NodeArr_val[i].NodeId >= 0)
                 && (globNodeArr.NodeArr_val[i].NodeId != globMyId)) {
                //Try to create the client handle.
            if ((cl = clnt_create(
                            globNodeArr.NodeArr_val[i].IPaddress.String_val,
                            COORDINATOR, GENERATOR,"tcp")) == NULL) {
                    // Connection failed.
                clnt_pcreateerror(globNodeArr.NodeArr_val[i].Name.String_val );
                clntFlag = 0;
            }
             
            if( !clntFlag) {
                //If the connection to the remote machine could not be made    
                clntFlag = 1;
                continue;
            }
            
            //Initialize the remoteCoordinatorLockArr
            remoteCoordinatorLockArr.LockArr_len = 0;
            if(remoteCoordinatorLockArr.LockArr_val != NULL) {
                free( remoteCoordinatorLockArr.LockArr_val);
                remoteCoordinatorLockArr.LockArr_val = NULL;
            }

            //With the client handle made, invoke the remote RPC function.
            if (clnt_call(cl, REQUEST_LOCK_INFO, xdr_int, &globMyId,
                       xdr_LockArr, 
                       &remoteCoordinatorLockArr, 
                       TIMEOUT) != RPC_SUCCESS) {
                clnt_perror(cl, globNodeArr.NodeArr_val[i].Name.String_val);
             }

            //If the return value from the above RPC function call is not 
            //NULL, //then it contains the array of locks that the remote
            //machine has returned.
            if( remoteCoordinatorLockArr.LockArr_len != 0) {
                    //Add the lock array obtained to the globalLockInfoArray.
                remoteLockInfoArray = mergeLockInfoArrays( 
                                        remoteLockInfoArray, 
                                        &remoteCoordinatorLockArr); 
                PrintLockArr( remoteLockInfoArray);
            }

            else {
                remoteLockInfoArray = appendLockArr(remoteLockInfoArray, NULL); 
            }
            //Destroy the client handle.
            clnt_destroy(cl);
        }
        
    } //////////// *** end of for loop
#ifdef PRINT

        PrintInLogFile("---------------\n");
        PrintLockArr( remoteLockInfoArray);
        PrintInLogFile("---------------\n");

#endif
        return remoteLockInfoArray;
}

LocalLockArr* acquireLocks(
                int* arg , 
                int* lockId , 
                int* numOfLocksWanted , 
                LockArr* globalLockInfoArray , 
                LockReqInf lockReqInf ,
                int *pos ) {
    int numOfLocks = 0; 
    LocalLockArr    *locksAvailable;  
    LockReqList*    result;
    int             locks;
    LocalLockArr    Available;
        // args is the number of locks actually got
    while( (numOfLocks < *arg) &&
            ( *lockId < globalLockInfoArray->LockArr_len)) {

        if((*lockId + *arg - numOfLocks) < globalLockInfoArray->LockArr_len) {
            numOfLocksWanted = *arg - numOfLocks;
        }
        else {
            numOfLocksWanted = globalLockInfoArray->LockArr_len - *lockId;
        }

#ifdef PRINT
        PrintInLogFile( "numOfLocksWanted = %d *lockId = %d\n", 
                        numOfLocksWanted, *lockId);
#endif

        lockRequest.LockList.LockArr_len =  numOfLocksWanted;

        if( lockRequest.LockList.LockArr_val != NULL) {
            /*
            free(lockRequest.LockList.LockArr_val);
            */
        }

        lockRequest.LockList.LockArr_val = (Lock *) malloc
                    (lockRequest.LockList.LockArr_len * sizeof( Lock));

        lockRequest.Perm.IntArr_len = numOfLocksWanted;
        if( lockRequest.Perm.IntArr_val != NULL) {
            /*
            free(lockRequest.Perm.IntArr_val);
            */
        }

        lockRequest.Perm.IntArr_val = (int*) malloc
                        ( lockRequest.Perm.IntArr_len * sizeof( int ));

        lockRequest.NodeId = globMyId;
        lockRequest.SeqNum = lockReqInf.SeqNum = globHighestSeq + 1;

        //////////////////////////////////////////////////////////////////
        copy_lockList_Into_lockRequestList(
                            numOfLocksWanted , 
                            lockReqInf , 
                            globalLockInfoArray , 
                            lockId);
        //////////////////////////////////////////////////////////////////

        PrintLockReqList(requestedList);

        *lockId+= numOfLocksWanted;

        //////////////////////////////////////////////////////////////////
        call_AcquireLocks_RPC(&pos);
        //////////////////////////////////////////////////////////////////

        /* find out the locks which are available */
    
        Available.LocalLockArr_len  = lockRequest.LockList.LockArr_len;

        if( Available.LocalLockArr_val != NULL) {
            /*
            free( Available.LocalLockArr_val);
            */
            Available.LocalLockArr_val = NULL;
        }

        Available.LocalLockArr_val  = (LocalLock *) malloc 
            (Available.LocalLockArr_len * sizeof(LocalLock));

        locks = 0;

        //////////////////////////////////////////////////////////////////
        createAvailableList(&pos , &Available , &locks);
        //////////////////////////////////////////////////////////////////

#ifdef PRINT

    PrintInLogFile("Coming here %d \n", locks);

#endif
        PrintLocalLockArr( &Available);
        Available.LocalLockArr_len  = locks;

        ///////////////////////////////////////////////////////////
        call_set_Lock_Allocated_RPC( &Available , &pos );
        ///////////////////////////////////////////////////////////

        locksAvailable = appendLocalLockArr(locksAvailable,
                &Available); 
        numOfLocks+= Available.LocalLockArr_len;
    }
        ///****** end of while loop
        return locksAvailable;
}

