/*============================================================================

  WCSLIB 5.1 - an implementation of the FITS WCS standard.
  Copyright (C) 1995-2015, Mark Calabretta

  This file is part of WCSLIB.

  WCSLIB is free software: you can redistribute it and/or modify it under the
  terms of the GNU Lesser General Public License as published by the Free
  Software Foundation, either version 3 of the License, or (at your option)
  any later version.

  WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  more details.

  You should have received a copy of the GNU Lesser General Public License
  along with WCSLIB.  If not, see http://www.gnu.org/licenses.

  Direct correspondence concerning WCSLIB to mark@calabretta.id.au

  Author: Mark Calabretta, Australia Telescope National Facility, CSIRO.
  http://www.atnf.csiro.au/people/Mark.Calabretta
  $Id: dis_f.c,v 5.1 2015/04/07 10:56:38 mcalabre Exp $
*=============================================================================
*
* In these wrappers, if
*
*   deref == 0, then dis is the address of a Fortran INTEGER array of length
*               DISLEN containing a disprm struct.
*
*   deref == 1, then dis is the address of a Fortran INTEGER(2) array holding
*               the address of a disprm struct, such as is returned by
*               disalloc_(), or linget() with LIN_DISPRE or LIN_DISSEQ.
*
*---------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <wcserr.h>
#include <wcsutil.h>
#include <dis.h>

/* Fortran name mangling. */
#include <wcsconfig_f77.h>
#define disalloc_ F77_FUNC(disalloc, DISALLOC)
#define disput_   F77_FUNC(disput,   DISPUT)
#define disptc_   F77_FUNC(disptc,   DISPTC)
#define disptd_   F77_FUNC(disptd,   DISPTD)
#define dispti_   F77_FUNC(dispti,   DISPTI)
#define disget_   F77_FUNC(disget,   DISGET)
#define disgtc_   F77_FUNC(disgtc,   DISGTC)
#define disgtd_   F77_FUNC(disgtd,   DISGTD)
#define disgti_   F77_FUNC(disgti,   DISGTI)

#define disini_   F77_FUNC(disini,   DISINI)
#define disparms_ F77_FUNC(disparms, DISPARMS)
#define discpy_   F77_FUNC(discpy,   DISCPY)
#define disfree_  F77_FUNC(disfree,  DISFREE)
#define disprt_   F77_FUNC(disprt,   DISPRT)
#define disperr_  F77_FUNC(disperr,  DISPERR)
#define disset_   F77_FUNC(disset,   DISSET)
#define disp2x_   F77_FUNC(disp2x,   DISP2X)
#define disx2p_   F77_FUNC(disx2p,   DISX2P)
#define diswarp_  F77_FUNC(diswarp,  DISWARP)

#define DIS_FLAG   100
#define DIS_NAXIS  101
#define DIS_DTYPE  102
#define DIS_AXMAP  103
#define DIS_OFFSET 104
#define DIS_SCALE  105
#define DIS_NPARM  106
#define DIS_PARMS  107
#define DIS_MAXDIS 108
#define DIS_TOTDIS 109

#define DIS_ERR    201

/*--------------------------------------------------------------------------*/

/* disp should be the address of an INTEGER(2) array.  On return it holds   */
/* the address of an allocated disprm struct.                               */

int disalloc_(int *disp)

{
  if ((*(struct disprm **)disp = calloc(1, sizeof(struct disprm))) == 0x0) {
    return DISERR_MEMORY;
  }

  return 0;
}

/*--------------------------------------------------------------------------*/

int disput_(
  const int *deref,
  int *dis,
  const int *what,
  const void *value,
  const int *j,
  const int *k)

{
  int j0, k0;
  const char *cvalp;
  const int  *ivalp;
  const double *dvalp;
  struct disprm *disp;

  /* Cast pointers. */
  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  cvalp = (const char *)value;
  ivalp = (const int *)value;
  dvalp = (const double *)value;

  /* Convert 1-relative FITS (and Fortran) axis numbers and parameter */
  /* indices to 0-relative C array indices.                           */
  j0 = *j - 1;
  k0 = *k - 1;

  switch (*what) {
  case DIS_FLAG:
    disp->flag = *ivalp;
    break;
  case DIS_NAXIS:
    disp->naxis = *ivalp;
    disp->flag = 0;
    break;
  case DIS_DTYPE:
    strncpy(disp->dtype[j0], cvalp, 16);
    wcsutil_null_fill(16, disp->dtype[j0]);
    disp->flag = 0;
    break;
  case DIS_AXMAP:
    disp->axmap[j0][k0] = *ivalp;
    disp->flag = 0;
    break;
  case DIS_OFFSET:
    disp->offset[j0][k0] = *dvalp;
    break;
  case DIS_SCALE:
    disp->scale[j0][k0] = *dvalp;
    break;
  case DIS_NPARM:
    disp->nparm[j0] = *ivalp;
    disp->flag = 0;
    break;
  case DIS_PARMS:
    disp->parms[j0][k0] = *dvalp;
    disp->flag = 0;
    break;
  case DIS_MAXDIS:
    disp->maxdis[j0] = *dvalp;
    break;
  case DIS_TOTDIS:
    disp->totdis = *dvalp;
    break;
  default:
    return 1;
  }

  return 0;
}

int disptc_(const int *deref, int *dis, const int *what, const char *value,
  const int *j, const int *k)
{
  return disput_(deref, dis, what, value, j, k);
}

int disptd_(const int *deref, int *dis, const int *what, const double *value,
  const int *j, const int *k)
{
  return disput_(deref, dis, what, value, j, k);
}

int dispti_(const int *deref, int *dis, const int *what, const int *value,
  const int *j, const int *k)
{
  return disput_(deref, dis, what, value, j, k);
}

/*--------------------------------------------------------------------------*/

int disget_(const int *deref, const int *dis, const int *what, void *value)

{
  int j, k, naxis;
  char   *cvalp;
  int    *ivalp;
  double *dvalp;
  const int    *idisp;
  const struct disprm *disp;

  /* Cast pointers. */
  if (*deref == 0) {
    disp = (const struct disprm *)dis;
  } else {
    disp = *(const struct disprm **)dis;
  }

  cvalp = (char *)value;
  ivalp = (int *)value;
  dvalp = (double *)value;

  naxis = disp->naxis;

  switch (*what) {
  case DIS_FLAG:
    *ivalp = disp->flag;
    break;
  case DIS_NAXIS:
    *ivalp = naxis;
    break;
  case DIS_DTYPE:
    for (j = 0; j < naxis; j++) {
      strncpy(cvalp, disp->dtype[j], 16);
      wcsutil_blank_fill(16, cvalp);
      cvalp += 16;
    }
    break;
  case DIS_AXMAP:
    for (j = 0; j < naxis; j++) {
      for (k = 0; k < naxis; k++) {
        *(ivalp++) = disp->axmap[j][k];
      }
    }
    break;
  case DIS_OFFSET:
    for (j = 0; j < naxis; j++) {
      for (k = 0; k < naxis; k++) {
        *(dvalp++) = disp->offset[j][k];
      }
    }
    break;
  case DIS_SCALE:
    for (j = 0; j < naxis; j++) {
      for (k = 0; k < naxis; k++) {
        *(dvalp++) = disp->scale[j][k];
      }
    }
    break;
  case DIS_NPARM:
    for (j = 0; j < naxis; j++) {
      *(ivalp++) = disp->nparm[j];
    }
    break;
  case DIS_PARMS:
    for (j = 0; j < naxis; j++) {
      for (k = 0; k < disp->nparm[j]; k++) {
        *(dvalp++) = disp->parms[j][k];
      }
    }
    break;
  case DIS_MAXDIS:
    for (j = 0; j < naxis; j++) {
      *(dvalp++) = disp->maxdis[j];
    }
    break;
  case DIS_TOTDIS:
    *dvalp = disp->totdis;
    break;
  case DIS_ERR:
    /* Copy the contents of the wcserr struct. */
    if (disp->err) {
      idisp = (int *)(disp->err);
      for (k = 0; k < ERRLEN; k++) {
        *(ivalp++) = *(idisp++);
      }
    } else {
      for (k = 0; k < ERRLEN; k++) {
        *(ivalp++) = 0;
      }
    }
    break;
  default:
    return 1;
  }

  return 0;
}

int disgtc_(const int *deref, const int *dis, const int *what, char *value)
{
  return disget_(deref, dis, what, value);
}

int disgtd_(const int *deref, const int *dis, const int *what, double *value)
{
  return disget_(deref, dis, what, value);
}

int disgti_(const int *deref, const int *dis, const int *what, int *value)
{
  return disget_(deref, dis, what, value);
}

/*--------------------------------------------------------------------------*/

int disini_(const int *deref, const int *naxis, int *dis)

{
  struct disprm *disp;

  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  return disini(1, *naxis, disp);
}

/*--------------------------------------------------------------------------*/

int disparms_(const int *deref, int *dis)

{
  struct disprm *disp;

  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  return disparms(disp);
}

/*--------------------------------------------------------------------------*/

int discpy_(const int *deref, const int *dissrc, int *disdst)

{
  const struct disprm *dissrcp;
  struct disprm *disdstp;

  if ((*deref&1) == 0) {
    dissrcp = (const struct disprm *)dissrc;
  } else {
    dissrcp = *(const struct disprm **)dissrc;
  }

  if ((*deref&2) == 0) {
    disdstp = (struct disprm *)disdst;
  } else {
    disdstp = *(struct disprm **)disdst;
  }

  return discpy(1, dissrcp, disdstp);
}

/*--------------------------------------------------------------------------*/

int disfree_(const int *deref, int *dis)

{
  struct disprm *disp;

  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  return disfree(disp);
}

/*--------------------------------------------------------------------------*/

int disprt_(const int *deref, const int *dis)

{
  const struct disprm *disp;

  if (*deref == 0) {
    disp = (const struct disprm *)dis;
  } else {
    disp = *(const struct disprm **)dis;
  }

  /* This may or may not force the Fortran I/O buffers to be flushed.  If
   * not, try CALL FLUSH(6) before calling DISPRT in the Fortran code. */
  fflush(NULL);

  return disprt(disp);
}

/*--------------------------------------------------------------------------*/

/* prefix should be null-terminated, or else of length 72 in which case
 * trailing blanks are not significant. */

int disperr_(const int *deref, int *dis, const char prefix[72])

{
  char prefix_[72];
  int  i;
  const struct disprm *disp;

  if (*deref == 0) {
    disp = (const struct disprm *)dis;
  } else {
    disp = *(const struct disprm **)dis;
  }

  strncpy(prefix_, prefix, 72);
  if (prefix_[71] == ' ') {
    for (i = 70; i >= 0; i--) {
      if (prefix_[i] != ' ') break;
      prefix_[i] = '\0';
    }
  } else {
    prefix_[71] = '\0';
  }

  /* This may or may not force the Fortran I/O buffers to be flushed. */
  /* If not, try CALL FLUSH(6) before calling DISPERR in the Fortran code. */
  fflush(NULL);

  return wcserr_prt(disp->err, prefix_);
}

/*--------------------------------------------------------------------------*/

int disset_(const int *deref, int *dis)

{
  struct disprm *disp;

  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  return disset(disp);
}

/*--------------------------------------------------------------------------*/

int disp2x_(
  const int *deref,
  int *dis,
  const double rawcrd[],
  double discrd[])

{
  struct disprm *disp;

  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  return disp2x(disp, rawcrd, discrd);
}

/*--------------------------------------------------------------------------*/

int disx2p_(
  const int *deref,
  int *dis,
  const double discrd[],
  double rawcrd[])

{
  struct disprm *disp;

  if (*deref == 0) {
    disp = (struct disprm *)dis;
  } else {
    disp = *(struct disprm **)dis;
  }

  return disx2p(disp, discrd, rawcrd);
}

/*--------------------------------------------------------------------------*/

int diswarp_(
  int *dis,
  const double pixblc[],
  const double pixtrc[],
  const double pixsamp[],
  int    *nsamp,
  double maxdis[],
  double *maxtot,
  double avgdis[],
  double *avgtot,
  double rmsdis[],
  double *rmstot)

{
  return diswarp((struct disprm *)dis, pixblc, pixtrc, pixsamp, nsamp,
                 maxdis, maxtot, avgdis, avgtot, rmsdis, rmstot);
}
