/**
* Copyright 1981-2012 ECMWF.
*
* This software is licensed under the terms of the Apache Licence 
* Version 2.0 which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
*
* In applying this licence, ECMWF does not waive the privileges and immunities 
* granted to it by virtue of its status as an intergovernmental organisation 
* nor does it submit to any jurisdiction.
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "sharedmemory.h"
#include "fortdefs.h"

int sharedMemoryCharacteristics( int, int , float , int * , size_t * , key_t * );
void createSharedMemoryCoefficients( fortint , fortint , fortreal , fortint , key_t , size_t );

int debugSet = 0;
static char * debugLevel;
 
fortint SMREAD(
  fortreal ** fortranArrayPointer,  /* to be attached to shared memory */
  fortint * Type,                   /* SP2LL, SP2QG, .. */
  fortint * Truncation,             /* spectral truncation */
  fortreal * Grid)                  /* grid spacing or gaussian number */
/*
// Callable from Fortran.
//
*/
/*
C
C---->
C**** SMREAD
C
C     Purpose
C     -------
C
C     This routine gets interpolation coefficients in a shared memory array.
C
C
C     Interface
C     ---------
C
C     ISIZE = SMREAD(KARRAY,KTYPE,KTRUNC,GRID)
C
C
C     Input parameters
C     ----------------
C
C     KTYPE  - Type of spectral to grid transformation (SP2LL, SP2QG, ..)
C     KTRUNC - Spectral truncation (eg 511 for T511)
C     GRID   - longitude grid spacing (for lat/long) or gaussian number
C
C
C     Output parameters
C     -----------------
C
C     KARRAY - Pointer to a shared memory array of REALs
C
C     Function returns the size (in values) of the shared memory array
C
C
C     Common block usage
C     ------------------
C
C     None.
C
C
C     Method
C     ------
C
C     Looks for coefficients in shared memory.
C     Waits on semaphore for coefficients to become available.
C
C
C     Externals
C     ---------
C
C     sharedMemoryCharacteristics    - to get key and size for shared memory
C     createSharedMemoryCoefficients - creates coefficients if necessary
C
C
C     Reference
C     ---------
C
C     None.
C
C
C     Comments
C     --------
C
C     Environment variable SHARED_DEBUG can be set (1, 2, ..) to give
C     diagnostic printout.
C
C
C     Author
C     ------
C
C     J.D.Chambers      ECMWF      January 2002
C
C
C     Modifications
C     -------------
C
C     None.
C
C----<
C     
*/
{
int type = (int) *Type;
int truncation = (int) *Truncation;
float grid = (float) *Grid;
key_t memoryKey;
size_t size;
int shmflg, shmid;
char * shmaddr;
int status, i;
fortint numberOfValues = 0;
char mode[] = "00666";
int value, semaphoreId, numlat, returnCode;
union semun {
  int             val;
  struct semid_ds *buf;
  ushort          *array;
} semvalues;
fortint Numlat;
struct sembuf bufs[1];

/*
// See if DEBUG switched on.
*/
    if( ! debugSet ) { 
      debugLevel = getenv("SHARED_DEBUG");
      if( debugLevel == NULL )
        debugSet = DEBUGOFF;              /* off */
      else {
        int loop;
        for( loop = 0; loop < strlen(debugLevel) ; loop++ ) {
          if( ! isdigit(debugLevel[loop]) ) {
            printf("Invalid number string in SHARED_DEBUG: %s\n", debugLevel);
            printf("SHARED_DEBUG must comprise only digits [0-9].\n");
            debugSet = DEBUGOFF;
          }
        }
        debugSet = DEBUGOFF + atoi( debugLevel );
      }
      if( DEBUG ) printf("SHARED_DEBUG: debug switched on\n");
    }
/*
// Get key and size for shared memory (whole globe)
*/
    if( DEBUG ) {
      printf("SMREAD: type = %d\n",type);
      printf("SMREAD: truncation = %d\n",truncation);
      printf("SMREAD: grid = %f\n",grid);
    }
    status = sharedMemoryCharacteristics( type,
                                          truncation,
                                          grid,
                                          &numlat,
                                          &size,
                                          &memoryKey);
    if( status ) {
      if( DEBUG ) printf("SMREAD: sharedMemoryCharacteristics call failed\n");
      return (fortint) 0;
    }
    Numlat = numlat;
/*
// Wait for the shared memory array to become available
*/
    if( DEBUG ) printf("SMREAD: call createSharedMemoryCoefficients with numlat = %d\n", numlat);
    createSharedMemoryCoefficients(*Type,*Truncation,*Grid,Numlat,memoryKey,size);

    bufs[0].sem_num = 0;
    bufs[0].sem_op  = -READY_TO_READ;
    bufs[0].sem_flg = 0;

    semaphoreId = (int) memoryKey;
    if( DEBUG )
      printf("SMREAD: wait for shared memory semaphore id %0x\n", semaphoreId);
    status = semop(semaphoreId,bufs,1);
/*
// Locate shared segment
*/
    if( DEBUG ) printf("SMREAD: locate shared memory\n");
    shmflg = 0666;
    shmid = shmget( memoryKey, (size_t)size, shmflg);
    if ( shmid < 0 ) {
       perror("shmget error");
       return (fortint) 0;
    }
/*
// Attach the shared segment to the array pointer
*/
    if( DEBUG ) printf("SMREAD: attach shared memory to array\n");
    shmaddr = (char *) NULL;
    shmflg = 0;
    *fortranArrayPointer = (fortreal *) shmat( shmid, shmaddr, shmflg);
    if ( *fortranArrayPointer == (fortreal *) -1 ) {
       perror("shmat error");
       return (fortint) 0;
    }
/*
// Set semaphore value to make memory readable for other readers
*/
    if( DEBUG ) printf("SMREAD: set semaphore to make memory readable\n");
    semvalues.val = READY_TO_READ;
    value = semctl((int)semaphoreId, (int)0, (int)SETVAL,semvalues);
/*
// Return the size (in values) of the shared segment
*/
    numberOfValues = (fortint) size/sizeof(fortreal);
    if( DEBUG )
      printf("SMREAD: number of values in shared memory = %d\n", numberOfValues);

    return numberOfValues;
}
