/**
* 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 <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

extern int debugSet;

#include "sharedmemory.h"
#include "fortdefs.h"

void createSharedMemoryCoefficients(
     fortint Type,       /* SP2LL, SP2QG, .. */
     fortint Truncation, /* spectral truncation */
     fortreal Grid,      /* grid spacing or gaussian number */
     fortint Numlat,     /* number of latitudes in the shared memory array */
     key_t memoryKey,    /* shared memory key */
     int size)           /* size of the shared memory */
{
int type = (int) Type;
struct sembuf bufs[1];
union semun {
  int             val;
  struct semid_ds *buf;
  ushort          *array;
} semvalues;
fortreal * array;
int shmflg, shmid;
char * shmaddr;
int status, i, oldmask;
char mode[] = "00666";
fortint gaussianNumber, one = 1, first = 1, returnCode;
int semaphoreId, value;
fortreal northPole = 90.0;

/*
// If the semaphore for the shared memory already exists,
// wait for it to become ready to read, and return
*/
    if( DEBUG ) printf("createSharedMemoryCoefficients: see if shared memory semaphore exists\n");
    semaphoreId = semget(memoryKey,1,0);

    if( semaphoreId != -1 ) {
      bufs[0].sem_num = 0;
      bufs[0].sem_op  = -READY_TO_READ;
      bufs[0].sem_flg = 0;
      if( DEBUG ) printf("createSharedMemoryCoefficients: wait for shared memory semaphore\n");
      status = semop(semaphoreId,bufs,(size_t)1);

      semvalues.val = READY_TO_READ;
      value = semctl((int)semaphoreId, (int)0, (int)SETVAL,semvalues);
      if( DEBUG ) printf("createSharedMemoryCoefficients: set shared memory semaphore readable\n");
      return;
    }
/*
// Create semaphore for the shared memory array
*/
    if( DEBUG ) {
      printf("createSharedMemoryCoefficients: have to create shared memory segment\n");
      printf("createSharedMemoryCoefficients: so create shared memory semaphore\n");
    }
    oldmask = umask(000);
    semaphoreId = semget(memoryKey,1,IPC_CREAT | 0666);
    if( semaphoreId == -1 ) {
      perror("creator: failed to create smaphore");
      exit(1);
    }
    (void) umask(oldmask);
/*
// Create shared segment id
*/
    if( DEBUG ) printf("createSharedMemoryCoefficients: get shared memory segment\n");
    (void) umask(0);
    shmflg = IPC_CREAT | 0666;
    shmid = shmget(memoryKey, (size_t)size, shmflg);
    if ( shmid < 0 ) {
       perror("shmget error");
       exit(1);
    }
/*
// Attach the shared segment
*/
    if( DEBUG ) printf("createSharedMemoryCoefficients: attach shared memory segment\n");
    shmaddr = (char *) NULL;
    shmflg = 0;
    array = (fortreal *) shmat( shmid, shmaddr, shmflg);
    if ( array == (fortreal *) -1 ) {
       perror("shmat error");
       exit(1);
    }
/*
// Put some data into the shared segment
*/
    if( DEBUG ) printf("createSharedMemoryCoefficients: put data into the shared segment\n");
    returnCode = 0;

    JDEBUG();

    switch( type ) {

      case SP2LL:
        if( DEBUG ) printf("createSharedMemoryCoefficients: call NMAKLL for SP2LL\n");
        NMAKLL( &Truncation, &Grid, &northPole, &Numlat, array, &returnCode);
        if( returnCode ) {
          printf("Problem with NMAKLL, return code = %d\n", returnCode);
          exit(1);
        }
        break;

      case SP2RG:
      case SP2QG:
/*
*/
       {
        char htype[2];
        fortreal * plat;
        fortint * kpts;

        gaussianNumber = (fortint) Grid;

        plat = (fortreal *) malloc( gaussianNumber*2*sizeof(fortreal));
        if( plat == NULL ) {
          perror("createSharedMemoryCoefficients: plat malloc problem");
          exit(1);
        }

        kpts = (fortint *) malloc( gaussianNumber*2*sizeof(fortint));
        if( kpts == NULL ) {
          perror("createSharedMemoryCoefficients: kpts malloc problem");
          exit(1);
        }

        if( type == SP2RG ) {
          if( DEBUG ) printf("createSharedMemoryCoefficients: handle SP2RG\n");
          strcpy(htype,"F");
        }
        else {
          if( DEBUG ) printf("createSharedMemoryCoefficients: handle SP2QG\n");
          strcpy(htype,"R");
        }

        if( DEBUG ) printf("createSharedMemoryCoefficients: call JGETGG\n");
        JGETGG(&gaussianNumber, &htype, plat, kpts, &returnCode, one);
        if( returnCode ) {
          printf("Problem with JGETGG, return code = %d\n", returnCode);
          exit(1);
        }
        if( DEBUG ) printf("createSharedMemoryCoefficients: call NMAKGG\n");
        NMAKGG(&Truncation, &first, plat, &Numlat, array, &returnCode);
        if( returnCode ) {
          printf("Problem with NMAKGG, return code = %d\n", returnCode);
          exit(1);
        }

        free(plat);
        free(kpts);
        break;
       }
/*
*/
      default:
        printf("creator: Type %d not yet handled\n", type);
        exit(1);
    }
/*
// Set the semaphore to make the memory readable for cooperating processes
*/
    if( DEBUG ) printf("createSharedMemoryCoefficients: set semaphore to make memory readable\n");
    semvalues.val = READY_TO_READ;
    value = semctl((int)semaphoreId, (int)0, (int)SETVAL,semvalues);
/*
// Detach the shared segment
*/
    if( DEBUG ) printf("createSharedMemoryCoefficients: detach the shared segment\n");
    status = shmdt(array);
    if ( status < 0 ) {
       perror("shmdt error");
       exit(1);
    }

    return;
}
