/*
 * © Copyright 1996-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.
 */

/*****************************************************************************

  These routines implemente a C access to COS blocked files

  B.Raoult

  Thu May  9 09:36:24 BST 1991

*****************************************************************************/

#include "cos.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CRAYBLK 4096

/*****************************************************************************

  This routine checks if the current control word is a valid BCW
  It needs to be improved.

*****************************************************************************/

static int validate_bcw(cf)
COSFILE* cf;
{
    if (M(cf->cw) == 0) /* is it a BCW ? */
    {
        if (BN(cf->cw) != cf->block)
            return FALSE;
        cf->block++;
        return TRUE;
    }
    else
        return FALSE;
}

/*****************************************************************************

  This routine checks if the current control word is a valid RCW
  It needs to be improved.

*****************************************************************************/

static int validate_rcw(cf)
COSFILE* cf;
{
    if (M(cf->cw) != 0) /* is it a RCW ? */
    {
        return TRUE;
    }
    else
        return FALSE;
}

/*****************************************************************************

  Reads a control word. Checks if is it a valid block.

*****************************************************************************/

static void read_control_word(cf, blocktype)
    COSFILE* cf;
int blocktype;
{

    int validflag = FALSE;

    if (!fread(&(cf->cw), sizeof(crayword), 1, cf->f))
        cf->err = errno ? errno : -1;
    else {

        if (blocktype & BCW)
            validflag = validate_bcw(cf);
        if (blocktype & RCW)
            validflag = validflag | validate_rcw(cf);

        if (!validflag) {
            cf->err = -1;
            fprintf(stderr, "Bad control word file %s\n", cf->fname);
            /* exit(1); it is a fatal error  */
        }
    }
}

/*****************************************************************************

   opening a cos blocked file.

*****************************************************************************/

COSFILE* cos_open(const char* fname) {
    COSFILE* cf = (COSFILE*)malloc(sizeof(COSFILE));

    if (cf == NULL) {
        /* perror("Opening file"); */
        return NULL;
    }

    cf->f = fopen(fname, "r");

    cf->buffer = NULL;
    cf->cnt    = CRAYBLK;

    if (cf->f == NULL) {
        free(cf);
        perror("Opening file");
        return NULL;
    }

    cf->err   = 0;
    cf->block = 0;
    cf->fname = (char*)strdup(fname);

    read_control_word(cf, BCW);
    if (cf->err) {
        cos_close(cf);
        return NULL;
    }

    return cf;
}

/*****************************************************************************

   reading a cos blocked file. On exit buf contains the data,
   len the number of bytes read. The return code is 0 if all Ok,
   -1 if  end of file, anything else otherwise.


   Note: buf must be big enough to hold the data.

*****************************************************************************/

int cos_read(cf, buf, len)
COSFILE* cf;
char* buf;
long* len;
{
    long offset;

    *len = 0;

    do {
        offset = FWI(cf->cw);
        if (offset) {
            if (!fread(buf, offset, 1, cf->f)) {
                /* perror("Reading data"); */
                cf->err = errno ? errno : -1;
                return cf->err;
            }
            *len += offset;
            buf += offset;
        }
        read_control_word(cf, BCW | RCW);

    } while (M(cf->cw) == 0);

    if (M(cf->cw) == COSEOF)
        return EOF;
    return 0;
}

/*****************************************************************************
******************************************************************************/

int cos_write(cf, buf, len)
COSFILE* cf;
char* buf;
long len;
{

    if (len == 0)
        return 0;
    if (!cf->buffer) {
        cf->cnt    = CRAYBLK;
        cf->buffer = (char*)malloc(CRAYBLK);
        if (!cf->buffer)
            return -1;
    }


    return 0;
}


/*****************************************************************************

   Close a cos blocked file.

*****************************************************************************/

int cos_close(cf)
COSFILE* cf;
{
    fclose(cf->f);
    if (cf->buffer)
        free(cf->buffer);
    free(cf->fname);
    free(cf);
    return errno;
}


/****************************************************************************


  FORTRAN interface :

  CALL COSOPEN(ICF,'MYFILE',IERR)
  CALL COSREAD(ICF,IARRAY,ILEN,IERR)
  CALL COSCLOSE(ICF,IERR)

*****************************************************************************/

void COSOPEN(filedesc, fname, e) char* fname;
long* e;
COSFILE** filedesc;
{
    COSFILE* cf;

    cf = *filedesc = cos_open(fname);
    if (cf == NULL)
        *e = errno ? errno : 1;
    else
        *e = cf->err;
}

void COSCLOSE(filedesc, e) long* e;
COSFILE** filedesc;
{
    *e = cos_close(*filedesc);
}

void COSREAD(filedesc, buffer, len, e) char* buffer;
long* e;
long* len;
COSFILE** filedesc;
{
    *e = cos_read(*filedesc, buffer, len);
}
