/************************************************/
/*                                              */
/* File        : dmap.c                         */
/* Description : detector map library           */
/*                                              */
/* Author: Sfiligoi Igor                        */
/*                                              */
/* Created      : 07.04.1997                    */
/* Last modified: 18.08.1997                    */
/*                                              */
/************************************************/

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

#include "dmap.h"
#include "dmap_write.h"

/* Empty definitions */
#define DMAP_DET_VOID		0	/* not present */
#define DMAP_BTYPE_VOID		0	/* not present */

/* Calorimeter ADC and TDC */
#define DMAP_CAL_CHANS_PER_BOARD	30

/* Chamber TDC */
#define DMAP_CHAMBER_CHANS_PER_BOARD	96
#define DMAP_WIRE_VOID			0xffff	/* not present */

/* QCAL ADC and TDC */
#define DMAP_QCAL_CHANS_PER_BOARD	30

/* Board */
typedef union
      {
	DMAP_CAL_EL     *cal;		/* array */
	DMAP_CHAMBER_EL *chamber;	/* array */
	DMAP_QCAL_EL    *qcal;		/* array */
      } DMAP_BOARD_SUB_EL;

typedef struct
      {
	unsigned char     btype;	/* See DMAP_BTYPE_.. constants */
	DMAP_BOARD_SUB_EL data;
      } DMAP_BOARD_EL;

/* Crate */
#define DMAP_BOARDS_PER_CRATE       16

typedef DMAP_BOARD_EL DMAP_CRATE_EL[DMAP_BOARDS_PER_CRATE];

/* Chain */
typedef struct
      {
	int            nrcrates;	/* nr of crates in the chain, >=1 */
	DMAP_CRATE_EL *crate;		/* array, element 0 is the crate 1 */
      } DMAP_CHAIN_EL;

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

/* System */
#define DMAP_NR_CHAINS		GEOVME_MAX_NUM_CHAINS

DMAP_CHAIN_EL dmap[DMAP_NR_CHAINS];	/* element 0 is the chain 1 */

char dmap_initialized = 0;

/**************************************************************************/
/******************************************/
/*     Initialization functions           */
/******************************************/

/* allocate the necessary structures */
/* use the GeoVme */
int dmap_init(void)
{
  int chain_nr;

  if (dmap_initialized==1)
    { /* non fatal error */
      fprintf(stderr,"WARNING in dmap_init: Dmap already initialized.\n");
    }

  for (chain_nr=1; chain_nr<=DMAP_NR_CHAINS; chain_nr++)
    {
      int chain_idx = chain_nr-1;
      int nrcrates;
      int crate_nr;

      /* find out the number of crates */
      for (nrcrates=1;
	   GeoVme_btype(chain_nr,nrcrates,21)==GEOVME_ROCK_BTYPE; /* include only crates with a rock */
	   nrcrates++);


      dmap[chain_idx].nrcrates = nrcrates;
      if (nrcrates>1)
	{
	  dmap[chain_idx].crate = (DMAP_CRATE_EL *) malloc((nrcrates-1)*sizeof(DMAP_CRATE_EL));

	  if (dmap[chain_idx].crate==NULL)
	    {
	      ErrorSetF(DMAP_ERROR_UNKNOWN,"dmap_init","Malloc returned NULL.");
	      return DMAP_ERROR_UNKNOWN;
	    }
	}
      else
	dmap[chain_idx].crate=NULL;

      for (crate_nr=1; crate_nr<nrcrates; crate_nr++)
	{
	  int crate_idx = crate_nr-1;
	  int board_nr;

	  for (board_nr=0; board_nr<DMAP_BOARDS_PER_CRATE; board_nr++)
	    {
	      int btype;
	      
	      /* find out it the board is present and its type */
	      btype = GeoVme_btype(chain_nr,crate_nr,board_nr+5);

	      switch (btype)
		{
		case GEOVME_CADC_BTYPE:
		case GEOVME_CTDC_BTYPE:
		  {
		    int chan_nr;

		    if (btype==GEOVME_CADC_BTYPE)
		      dmap[chain_idx].crate[crate_idx][board_nr].btype = DMAP_BTYPE_CALADC;
		    else
		      dmap[chain_idx].crate[crate_idx][board_nr].btype = DMAP_BTYPE_CALTDC;

		    dmap[chain_idx].crate[crate_idx][board_nr].data.cal = (DMAP_CAL_EL *) malloc(DMAP_CAL_CHANS_PER_BOARD*sizeof(DMAP_CAL_EL));
		    if (dmap[chain_idx].crate[crate_idx][board_nr].data.cal==NULL)
		      {
			ErrorSetF(DMAP_ERROR_UNKNOWN,"dmap_init","Malloc returned NULL.");
			return DMAP_ERROR_UNKNOWN;
		      }

		    for (chan_nr=0; chan_nr<DMAP_CAL_CHANS_PER_BOARD; chan_nr++)
		      {
			dmap[chain_idx].crate[crate_idx][board_nr].data.cal[chan_nr].detector = DMAP_DET_VOID;
			/* other fields are undefined */
		      }
		  }
		  break;
#define GEOVME_CHAMBER_BTYPE     2
		case GEOVME_CHAMBER_BTYPE:
		  {
		    int chan_nr;

		    dmap[chain_idx].crate[crate_idx][board_nr].btype = DMAP_BTYPE_CHATDC;

		    dmap[chain_idx].crate[crate_idx][board_nr].data.chamber = (DMAP_CHAMBER_EL *) malloc(DMAP_CHAMBER_CHANS_PER_BOARD*sizeof(DMAP_CHAMBER_EL));
		    if (dmap[chain_idx].crate[crate_idx][board_nr].data.chamber==NULL)
		      {
			ErrorSetF(DMAP_ERROR_UNKNOWN,"dmap_init","Malloc returned NULL.");
			return DMAP_ERROR_UNKNOWN;
		      }

		    for (chan_nr=0; chan_nr<DMAP_CHAMBER_CHANS_PER_BOARD; chan_nr++)
		      {
			dmap[chain_idx].crate[crate_idx][board_nr].data.chamber[chan_nr].wire = DMAP_WIRE_VOID;
			/* other fields are undefined */
		      }
		  }
		  break;
#define GEOVME_QADC_BTYPE       3
#define GEOVME_QTDC_BTYPE       4
		case GEOVME_QADC_BTYPE:
		case GEOVME_QTDC_BTYPE:
		  {
		    int chan_nr;

		    if (btype==GEOVME_QADC_BTYPE)
		      dmap[chain_idx].crate[crate_idx][board_nr].btype = DMAP_BTYPE_QCALADC;
		    else
		      dmap[chain_idx].crate[crate_idx][board_nr].btype = DMAP_BTYPE_QCALTDC;

		    dmap[chain_idx].crate[crate_idx][board_nr].data.qcal = (DMAP_QCAL_EL *) malloc(DMAP_QCAL_CHANS_PER_BOARD*sizeof(DMAP_QCAL_EL));
		    if (dmap[chain_idx].crate[crate_idx][board_nr].data.qcal==NULL)
		      {
			ErrorSetF(DMAP_ERROR_UNKNOWN,"dmap_init","Malloc returned NULL.");
			return DMAP_ERROR_UNKNOWN;
		      }

		    for (chan_nr=0; chan_nr<DMAP_QCAL_CHANS_PER_BOARD; chan_nr++)
		      {
			dmap[chain_idx].crate[crate_idx][board_nr].data.qcal[chan_nr].detector = DMAP_DET_VOID;
			/* other fields are undefined */
		      }
		  }
		  break;
		default:
		  {
		    if (btype==GEOVME_ROCK_BTYPE)
		      { /* Nonfatal error */
			fprintf(stderr,"WARNING in dmap_init: GeoVme reported a ROCK in %i %i %i!\n",chain_nr, crate_nr, board_nr);
		      }
		    dmap[chain_idx].crate[crate_idx][board_nr].btype = DMAP_BTYPE_VOID;
		  }
		  break;
		}
	    }
	}
    }

 dmap_initialized = 1;
 return DMAP_ERROR_OK;
}

/* return 0 if it is not a comment, 1 else */
int dmap_load_iscomment(char *buffer)
{
 int i;

 for (i=0; buffer[i]==' ' ;i++);

 if ((buffer[i]==0)||  /* blank line */
     (buffer[i]=='%')) /* comment */
   return 1;
 else
   return 0;
}

/* load the map in memory */
int dmap_load(char *filename)
{
 FILE *fmap;
 int res;
 char buffer[1024];
 int line_nr;

 res = dmap_init();

 if (res!=DMAP_ERROR_OK)
   {
     ErrorSetF(res,"dmap_load","dmap_init error: %s",ErrorGetMessage());
     return res;
   }

 fmap = fopen(filename,"r");

 if (fmap==NULL)
   {
     ErrorSetF(DMAP_ERROR_UNKNOWN,"dma_load","Error opening file %s.",filename);
     return DMAP_ERROR_UNKNOWN;
   }

 line_nr = 0;
 while (fscanf(fmap,"%[^\n]\n",buffer)!=EOF)
   {
     int chain_nr,crate_nr,board_nr,chan_nr,detector;
     char ibuffer[1024];
     char detector_str[128];

     line_nr++;

     if (dmap_load_iscomment(buffer))
       continue; /* skip comments */

     if (sscanf(buffer,"%i %i %i %i %[^ ] %[^\n]",&chain_nr,&crate_nr,&board_nr,&chan_nr,detector_str,ibuffer)!=6)
       {
	 fclose(fmap);
	 ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Error in the first half of line %i.",line_nr);
	 return DMAP_ERROR_LOAD_LINE;
       }

     if (strcmp(detector_str,"ECAPA")==0)
       detector = DMAP_DET_ECAPA;
     else if (strcmp(detector_str,"ECAPB")==0)
       detector = DMAP_DET_ECAPB;
     else if (strcmp(detector_str,"BARR")==0)
       detector = DMAP_DET_BARREL;
     else if (strcmp(detector_str,"CHAMB")==0)
       detector = DMAP_DET_CHAMBER;
     else if (strcmp(detector_str,"QCALA")==0)
       detector = DMAP_DET_QCALA;
     else if (strcmp(detector_str,"QCALB")==0)
       detector = DMAP_DET_QCALB;
     else
       {
	 fclose(fmap);
	 ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Invalid detector in line %i. (Should be ECAPA, BARR, ECAPB, CHAMB, QCALA or QCALB)",line_nr);
	 return DMAP_ERROR_LOAD_LINE;
       }

     if ((chain_nr<1)||(chain_nr>DMAP_NR_CHAINS))
       {
	 fclose(fmap);
	 ErrorSetF(DMAP_ERROR_CHECK_LINE,"dmap_load","Chain nr out of range in line %i. (%i)",line_nr,chain_nr);
	 return DMAP_ERROR_CHECK_LINE;
       }

     if ((crate_nr<1)||(crate_nr>=dmap[chain_nr-1].nrcrates))
       {
	 fclose(fmap);
	 ErrorSetF(DMAP_ERROR_CHECK_LINE,"dmap_load","Crate nr out of range in line %i. (%i)",line_nr,crate_nr);
	 return DMAP_ERROR_CHECK_LINE;
       }

     if ((board_nr<0)||(board_nr>=DMAP_BOARDS_PER_CRATE))
       {
	 fclose(fmap);
	 ErrorSetF(DMAP_ERROR_CHECK_LINE,"dmap_load","Board nr out of range in line %i. (%i)",line_nr,board_nr);
	 return DMAP_ERROR_CHECK_LINE;
       }

     if (detector==DMAP_DET_CHAMBER)
       {
	 int layer,wire;

	 if ((chan_nr<0)||(chan_nr>=DMAP_CHAMBER_CHANS_PER_BOARD))
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_CHECK_LINE,"dmap_load","Chan nr out of range in line %i. (%i)",line_nr,chan_nr);
	     return DMAP_ERROR_CHECK_LINE;
	   }

	 if (dmap[chain_nr-1].crate[crate_nr-1][board_nr].btype!=DMAP_BTYPE_CHATDC)
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_CONSISTENCY,"dmap_load","Board type invalid in line %i. (GeoVme %i, Read %i)",
		       line_nr,
		       dmap[chain_nr-1].crate[crate_nr-1][board_nr].btype,
		       DMAP_BTYPE_CHATDC);
	     return DMAP_ERROR_CONSISTENCY;
	   }

	 if (sscanf(ibuffer,"%i %i",&layer,&wire)!=2)
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Error in the second half of line %i (chamber).",line_nr);
	     return DMAP_ERROR_LOAD_LINE;
	   }

	 {
	   DMAP_CHAMBER_EL *el;

	   el = &(dmap[chain_nr-1].crate[crate_nr-1][board_nr].data.chamber[chan_nr]);

	   if (el->wire!=DMAP_WIRE_VOID)
	     { /* was already set before */
	       fclose(fmap);
	       ErrorSetF(DMAP_ERROR_REPEAT,"dmap_load","Repeated set in line %i.",line_nr);
	       return DMAP_ERROR_REPEAT;
	     }

	   /* no errors, set the element*/
	   el->layer = layer;
	   el->wire = wire;
	 }

       } /* end chamber */
     else if ((detector==DMAP_DET_ECAPA)||
	      (detector==DMAP_DET_ECAPB)||
	      (detector==DMAP_DET_BARREL))
       {
	 int module,plane,column,side,card;
	 char card_str[128];

	 if ((chan_nr<0)||(chan_nr>=DMAP_CAL_CHANS_PER_BOARD))
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_CHECK_LINE,"dmap_load","Chan nr out of range in line %i. (%i)",line_nr,chan_nr);
	     return DMAP_ERROR_CHECK_LINE;
	   }

	 if (sscanf(ibuffer,"%i %i %i %i %s",&module,&plane,&column,&side,card_str)!=5)
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Error in the second half of line %i (calorimeter).",line_nr);
	     return DMAP_ERROR_LOAD_LINE;
	   }

	 if (strcmp(card_str,"CALADC")==0)
	   card = DMAP_BTYPE_CALADC;
	 else if (strcmp(card_str,"CALTDC")==0)
	   card = DMAP_BTYPE_CALTDC;
	 else
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Invalid calorimter card in line %i. (Should be CALADC or CALTDC)",line_nr);
	     return DMAP_ERROR_LOAD_LINE;
	   }

	 if (dmap[chain_nr-1].crate[crate_nr-1][board_nr].btype!=card)
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_CONSISTENCY,"dmap_load","Board type invalid in line %i. (GeoVme %i, Read %i)",
		       line_nr,
		       dmap[chain_nr-1].crate[crate_nr-1][board_nr].btype,
		       card);
	     return DMAP_ERROR_CONSISTENCY;
	   }

	 {
	   DMAP_CAL_EL *el;

	   el = &(dmap[chain_nr-1].crate[crate_nr-1][board_nr].data.cal[chan_nr]);
	   if (el->detector!=DMAP_DET_VOID)
	     { /* was already set before */
	       fclose(fmap);
	       ErrorSetF(DMAP_ERROR_REPEAT,"dmap_load","Repeated set in line %i.",line_nr);
	       return DMAP_ERROR_REPEAT;
	     }

	   /* no errors, set the element */
	   el->detector = detector;
	   el->module = module;
	   el->plane = plane;
	   el->column = column;
	   el->side = side;
	 }
       } /* end cal */
     else if ((detector==DMAP_DET_QCALA)||
	      (detector==DMAP_DET_QCALB))
       {
	 int module,card;
	 char card_str[128];

	 if ((chan_nr<0)||(chan_nr>=DMAP_QCAL_CHANS_PER_BOARD))
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_CHECK_LINE,"dmap_load","Chan nr out of range in line %i. (%i)",line_nr,chan_nr);
	     return DMAP_ERROR_CHECK_LINE;
	   }

	 if (sscanf(ibuffer,"%i %s",&module,card_str)!=3)
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Error in the second half of line %i (QCAL).",line_nr);
	     return DMAP_ERROR_LOAD_LINE;
	   }

	 if (strcmp(card_str,"QCALADC")==0)
	   card = DMAP_BTYPE_QCALADC;
	 else if (strcmp(card_str,"QCALTDC")==0)
	   card = DMAP_BTYPE_QCALTDC;
	 else
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_LOAD_LINE,"dmap_load","Invalid calorimter card in line %i. (Should be QCALADC or QCALTDC)",line_nr);
	     return DMAP_ERROR_LOAD_LINE;
	   }

	 if (dmap[chain_nr-1].crate[crate_nr-1][board_nr].btype!=card)
	   {
	     fclose(fmap);
	     ErrorSetF(DMAP_ERROR_CONSISTENCY,"dmap_load","Board type invalid in line %i. (GeoVme %i, Read %i)",
		       line_nr,
		       dmap[chain_nr-1].crate[crate_nr-1][board_nr].btype,
		       card);
	     return DMAP_ERROR_CONSISTENCY;
	   }

	 {
	   DMAP_QCAL_EL *el;

	   el = &(dmap[chain_nr-1].crate[crate_nr-1][board_nr].data.qcal[chan_nr]);
	   if (el->detector!=DMAP_DET_VOID)
	     { /* was already set before */
	       fclose(fmap);
	       ErrorSetF(DMAP_ERROR_REPEAT,"dmap_load","Repeated set in line %i.",line_nr);
	       return DMAP_ERROR_REPEAT;
	     }

	   /* no errors, set the element */
	   el->detector = detector;
	   el->module = module;
	 }
       } /* end qcal */
   }

 fclose(fmap);

 return DMAP_ERROR_OK;
}

/* free the allocated memory */
int dmap_dispose()
{
 int chain_nr;

 if (dmap_initialized==0)
   {
     ErrorSet(DMAP_ERROR_NOTINIT,"dmap_dispose","Dmap not initialized!");
     return DMAP_ERROR_NOTINIT;
   }

 for (chain_nr=1; chain_nr<=DMAP_NR_CHAINS; chain_nr++)
    {
      int chain_idx = chain_nr-1;
      int crate_nr;

      for (crate_nr=1; crate_nr<dmap[chain_idx].nrcrates; crate_nr++)
	{
	  int crate_idx = crate_nr-1;
	  int board_nr;

	  for (board_nr=0; board_nr<DMAP_BOARDS_PER_CRATE; board_nr++)
	    {
	      int btype = dmap[chain_idx].crate[crate_idx][board_nr].btype;

	      if (btype != DMAP_BTYPE_VOID)
		{ /* if void, nothing to do */
		  if (btype == DMAP_BTYPE_CHATDC)
		    free(dmap[chain_idx].crate[crate_idx][board_nr].data.chamber);
		  else if ((btype==DMAP_BTYPE_CALADC)||(btype==DMAP_BTYPE_CALTDC))
		    free(dmap[chain_idx].crate[crate_idx][board_nr].data.cal);
		  else
		    free(dmap[chain_idx].crate[crate_idx][board_nr].data.qcal);
		}
	    }
	}

      free(dmap[chain_idx].crate);
    }

 dmap_initialized = 0;
 return DMAP_ERROR_OK;
}

/******************************************/
/*          Decode functions              */
/******************************************/

int dmap_get(unsigned char chain,   	/* IN:  chain   */
	     unsigned char crate,   	/* IN:  crate   */
	     unsigned char slot,    	/* IN:  slot    */
	     unsigned char chan,    	/* IN:  channel */
	     DMAP_CHAN_EL *data)	/* OUT: associated data */
{
  int btype;

  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_get","Dmap not initialized!");
      return DMAP_ERROR_NOTINIT;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_get","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_RANGE;
    }
  
  if ((crate<1)||(crate>=dmap[chain-1].nrcrates))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_get","Crate nr out of range. (%i)",crate);
      return DMAP_ERROR_RANGE;
    }
  
  if (slot>=DMAP_BOARDS_PER_CRATE)
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_get","Slot nr out of range. (%i)",slot);
      return DMAP_ERROR_RANGE;
    }

  btype = dmap[chain-1].crate[crate-1][slot].btype;

  data->btype = btype;

  switch (btype)
    {
    case DMAP_BTYPE_CALTDC:
    case DMAP_BTYPE_CALADC:
      if (chan>=DMAP_CAL_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_get","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      data->data.cal = dmap[chain-1].crate[crate-1][slot].data.cal[chan];

      if (data->data.cal.detector==DMAP_DET_VOID)
	{
	  ErrorClear();
	  return DMAP_ERROR_EMPTY; /* not a real error */
	}
      break;
    case DMAP_BTYPE_CHATDC:
      if (chan>=DMAP_CHAMBER_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      data->data.chamber = dmap[chain-1].crate[crate-1][slot].data.chamber[chan];

      if (data->data.chamber.wire==DMAP_WIRE_VOID)
	{
	  ErrorClear();
	  return DMAP_ERROR_EMPTY; /* not a real error */
	}
      break;
    case DMAP_BTYPE_QCALTDC:
    case DMAP_BTYPE_QCALADC:
      if (chan>=DMAP_QCAL_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      data->data.qcal = dmap[chain-1].crate[crate-1][slot].data.qcal[chan];

      if (data->data.qcal.detector==DMAP_DET_VOID)
	{
	  ErrorClear();
	  return DMAP_ERROR_EMPTY; /* not a real error */
	}
      break;
    default:
      {
	ErrorClear();
	return DMAP_ERROR_EMPTY; /* not a real error */
      }
      break;
    }

  return DMAP_ERROR_OK;
}

/******************************************/
/*          Encode functions              */
/******************************************/

int dmap_find(DMAP_CHAN_EL  data,	/* IN:  data to find */
	      unsigned char *chain,	/* OUT: chain   */
	      unsigned char *crate,   /* OUT: crate   */
	      unsigned char *slot,    /* OUT: slot    */
	      unsigned char *chan)    /* OUT: channel */
{
  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_find","Dmap not initialized!");
      return DMAP_ERROR_NOTINIT;
    }

  if ((data.btype<1)||(data.btype>5))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_find","Invalid board type. (%i)",data.btype);
      return DMAP_ERROR_RANGE;
    }

  switch (data.btype)
    {
    case DMAP_BTYPE_CALTDC:
    case DMAP_BTYPE_CALADC:
      if ((data.data.cal.detector!=DMAP_DET_ECAPA)&&
	  (data.data.cal.detector!=DMAP_DET_BARREL)&&
	  (data.data.cal.detector!=DMAP_DET_ECAPB))
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_find","Invalid detecor type. (cal %i)",data.data.cal.detector);
	  return DMAP_ERROR_RANGE;
	}
      break;
    case DMAP_BTYPE_CHATDC:
      if (data.data.chamber.wire==DMAP_WIRE_VOID)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_find","Invalid wire nr. (chamber)");
	  return DMAP_ERROR_RANGE;
	}
      break;
    case DMAP_BTYPE_QCALTDC:
    case DMAP_BTYPE_QCALADC:
      if ((data.data.qcal.detector!=DMAP_DET_QCALA)&&
	  (data.data.qcal.detector!=DMAP_DET_QCALB))
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_find","Invalid detecor type. (qcal %i)",data.data.qcal.detector);
	  return DMAP_ERROR_RANGE;
	}
      break;
    }


  {
    int chain_nr;
    
    for (chain_nr=1; chain_nr<=DMAP_NR_CHAINS; chain_nr++)
      {
	int crate_nr;
	int chain_idx = chain_nr-1;

	for (crate_nr=1; crate_nr<dmap[chain_idx].nrcrates; crate_nr++)
	  {
	    int board_nr;
	    int crate_idx = crate_nr-1;

	    for (board_nr=0; board_nr<DMAP_BOARDS_PER_CRATE; board_nr++)
	      {
		if (dmap[chain_idx].crate[crate_idx][board_nr].btype==data.btype)
		  switch (data.btype)
		    {
		    case DMAP_BTYPE_CALTDC:
		    case DMAP_BTYPE_CALADC:
		      {
			int chan_nr;

			for (chan_nr=0; chan_nr<DMAP_CAL_CHANS_PER_BOARD; chan_nr++)
			  {
			    DMAP_CAL_EL *el;

			    el = &(dmap[chain_idx].crate[crate_idx][board_nr].data.cal[chan_nr]);

			    if ((el->detector==data.data.cal.detector)&&
				(el->module==data.data.cal.module)&&
				(el->plane==data.data.cal.plane)&&
				(el->column==data.data.cal.column)&&
				(el->side==data.data.cal.side))
			      { /* found */
				*chain = chain_nr;
				*crate = crate_nr;
				*slot = board_nr;
				*chan = chan_nr;

				return DMAP_ERROR_OK;
			      }
			  }
		      }
		      break;
		    case DMAP_BTYPE_CHATDC:
		      {
			int chan_nr;

			for (chan_nr=0; chan_nr<DMAP_CHAMBER_CHANS_PER_BOARD; chan_nr++)
			  {
			    DMAP_CHAMBER_EL *el;

			    el = &(dmap[chain_idx].crate[crate_idx][board_nr].data.chamber[chan_nr]);

			    if ((el->layer==data.data.chamber.layer)&&
				(el->wire==data.data.chamber.wire))
			      { /* found */
				*chain = chain_nr;
				*crate = crate_nr;
				*slot = board_nr;
				*chan = chan_nr;

				return DMAP_ERROR_OK;
			      }
			  }
		      }
		      break;
		    case DMAP_BTYPE_QCALTDC:
		    case DMAP_BTYPE_QCALADC:
		      {
			int chan_nr;

			for (chan_nr=0; chan_nr<DMAP_QCAL_CHANS_PER_BOARD; chan_nr++)
			  {
			    DMAP_QCAL_EL *el;

			    el = &(dmap[chain_idx].crate[crate_idx][board_nr].data.qcal[chan_nr]);

			    if ((el->detector==data.data.qcal.detector)&&
				(el->module==data.data.qcal.module))
			      { /* found */
				*chain = chain_nr;
				*crate = crate_nr;
				*slot = board_nr;
				*chan = chan_nr;

				return DMAP_ERROR_OK;
			      }
			  }
		      }
		      break;
		    }
	      }
	  }
      }
  }
  
  ErrorClear();
  return DMAP_ERROR_EMPTY;
}

/******************************************/
/*           Query functions              */
/******************************************/

/* returns the number of chains,
   or DMAP_ERROR_UNKNOWN in case of error */
int dmap_query_nr_chains(void)
{
  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_query_nr_chains","Dmap not initialized!");
      return DMAP_ERROR_UNKNOWN;
    }

  return DMAP_NR_CHAINS;
}


/* returns the number of crates in the chain,
   or DMAP_ERROR_UNKNOWN in case of error */
int dmap_query_nr_crates(unsigned char chain)
{
  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_query_nr_crates","Dmap not initialized!");
      return DMAP_ERROR_UNKNOWN;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_nr_crates","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_UNKNOWN;
    }
  
  return dmap[chain-1].nrcrates;
}

/* returns the number of slots in the crate,
   or DMAP_ERROR_UNKNOWN in case of error */
int dmap_query_nr_slots(unsigned char chain,
			unsigned char crate)
{
  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_query_nr_slots","Dmap not initialized!");
      return DMAP_ERROR_UNKNOWN;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_nr_slots","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_UNKNOWN;
    }
  
  if ((crate<1)||(crate>=dmap[chain-1].nrcrates))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_nr_slots","Crate nr out of range. (%i)",crate);
      return DMAP_ERROR_UNKNOWN;
    }
  
  return DMAP_BOARDS_PER_CRATE;
}


/* returns the number of channels in the slot, 
   0 if the board is empty, 
   or DMAP_ERROR_UNKNOWN in case of error */
int dmap_query_nr_chans(unsigned char chain,
			unsigned char crate,
			unsigned char slot)
{
  int btype;

  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_query_nr_chans","Dmap not initialized!");
      return DMAP_ERROR_UNKNOWN;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_nr_chans","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_UNKNOWN;
    }
  
  if ((crate<1)||(crate>=dmap[chain-1].nrcrates))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_nr_chans","Crate nr out of range. (%i)",crate);
      return DMAP_ERROR_UNKNOWN;
    }
  
  if (slot>=DMAP_BOARDS_PER_CRATE)
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_nr_chans","Slot nr out of range. (%i)",slot);
      return DMAP_ERROR_UNKNOWN;
    }

  btype = dmap[chain-1].crate[crate-1][slot].btype;

  switch (btype)
    {
    case DMAP_BTYPE_CALADC:
    case DMAP_BTYPE_CALTDC:
      return DMAP_CAL_CHANS_PER_BOARD;
    case DMAP_BTYPE_CHATDC:
      return DMAP_CHAMBER_CHANS_PER_BOARD;
    case DMAP_BTYPE_QCALADC:
    case DMAP_BTYPE_QCALTDC:
      return DMAP_QCAL_CHANS_PER_BOARD;
    default:
      return 0;
    }
}


/* returns the type of the board in the slot, 
   DMAP_ERROR_EMPTY if the board is empty, 
   or DMAP_ERROR_UNKNOWN in case of error */
int dmap_query_btype(unsigned char chain,
		     unsigned char crate,
		     unsigned char slot)
{
  int btype;

  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_query_btype","Dmap not initialized!");
      return DMAP_ERROR_UNKNOWN;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_btype","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_UNKNOWN;
    }
  
  if ((crate<1)||(crate>=dmap[chain-1].nrcrates))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_btype","Crate nr out of range. (%i)",crate);
      return DMAP_ERROR_UNKNOWN;
    }
  
  if (slot>=DMAP_BOARDS_PER_CRATE)
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_query_btype","Slot nr out of range. (%i)",slot);
      return DMAP_ERROR_UNKNOWN;
    }

  btype = dmap[chain-1].crate[crate-1][slot].btype;

  if (btype==DMAP_BTYPE_VOID)
    return DMAP_ERROR_EMPTY;
  else
    return btype;
}


/****************************************************************/
/*                        dmap_write                            */
/****************************************************************/

/******************************************/
/*     Initialization functions           */
/******************************************/


/* creates a new empty map in memory (based on GeoVme) */
/* Is alternative to dmap_load */
int dmap_new(void)
{
 int res;
 res =  dmap_init();

 if (res!=DMAP_ERROR_OK)
   {
     ErrorSetF(res,"dmap_new","dmap_init error: %s",ErrorGetMessage());
     return res;
   }

 return DMAP_ERROR_OK;
}

/******************************************/
/*            Set function                */
/******************************************/

int dmap_set(unsigned char chain,	/* IN:  chain   */
	     unsigned char crate,   	/* IN:  crate   */
	     unsigned char slot,    	/* IN:  slot    */
	     unsigned char chan,    	/* IN:  channel */
	     DMAP_CHAN_EL  data)	/* IN:  new data */
{
  int btype;

  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_set","Dmap not initialized!");
      return DMAP_ERROR_NOTINIT;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_RANGE;
    }
  
  if ((crate<1)||(crate>=dmap[chain-1].nrcrates))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Crate nr out of range. (%i)",crate);
      return DMAP_ERROR_RANGE;
    }
  
  if (slot>=DMAP_BOARDS_PER_CRATE)
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Slot nr out of range. (%i)",slot);
      return DMAP_ERROR_RANGE;
    }

  btype = dmap[chain-1].crate[crate-1][slot].btype;

  if (btype!=data.btype)
    {
      ErrorSetF(DMAP_ERROR_CONSISTENCY,"dmap_set","Invalid btype. (Was %i New %i)",btype,data.btype);
      return DMAP_ERROR_CONSISTENCY;
    }

  switch (btype)
    {
    case DMAP_BTYPE_CALTDC:
    case DMAP_BTYPE_CALADC:
      if (chan>=DMAP_CAL_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      if ((data.data.cal.detector!=DMAP_DET_ECAPA)&&
	  (data.data.cal.detector!=DMAP_DET_ECAPB)&&
	  (data.data.cal.detector!=DMAP_DET_BARREL))
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Detector out of range. (%i)",data.data.cal.detector);
	  return DMAP_ERROR_RANGE;
	}

      dmap[chain-1].crate[crate-1][slot].data.cal[chan] = data.data.cal;
      break;
    case DMAP_BTYPE_CHATDC:
      if (chan>=DMAP_CHAMBER_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      dmap[chain-1].crate[crate-1][slot].data.chamber[chan] = data.data.chamber;
      break;
    case DMAP_BTYPE_QCALTDC:
    case DMAP_BTYPE_QCALADC:
      if (chan>=DMAP_QCAL_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      if ((data.data.qcal.detector!=DMAP_DET_QCALA)&&
	  (data.data.qcal.detector!=DMAP_DET_QCALB))
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_set","Detector out of range. (%i)",data.data.qcal.detector);
	  return DMAP_ERROR_RANGE;
	}

      dmap[chain-1].crate[crate-1][slot].data.qcal[chan] = data.data.qcal;
      break;
    default:
      {
	ErrorSetF(DMAP_ERROR_CONSISTENCY,"dmap_set","Cannot write in an empty slot.");
	return DMAP_ERROR_CONSISTENCY;
      }
    }

  return DMAP_ERROR_OK;
}

/******************************************/
/*           Clear function               */
/******************************************/

int dmap_clear(unsigned char chain,	/* IN:  chain   */
	       unsigned char crate,   	/* IN:  crate   */
	       unsigned char slot,    	/* IN:  slot    */
	       unsigned char chan)    	/* IN:  channel */
{
  int btype;

  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_clear","Dmap not initialized!");
      return DMAP_ERROR_NOTINIT;
    }

  if ((chain<1)||(chain>DMAP_NR_CHAINS))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_clear","Chain nr out of range. (%i)",chain);
      return DMAP_ERROR_RANGE;
    }
  
  if ((crate<1)||(crate>=dmap[chain-1].nrcrates))
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_clear","Crate nr out of range. (%i)",crate);
      return DMAP_ERROR_RANGE;
    }
  
  if (slot>=DMAP_BOARDS_PER_CRATE)
    {
      ErrorSetF(DMAP_ERROR_RANGE,"dmap_clear","Slot nr out of range. (%i)",slot);
      return DMAP_ERROR_RANGE;
    }

  btype = dmap[chain-1].crate[crate-1][slot].btype;

  switch (btype)
    {
    case DMAP_BTYPE_CALTDC:
    case DMAP_BTYPE_CALADC:
      if (chan>=DMAP_CAL_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_clear","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      dmap[chain-1].crate[crate-1][slot].data.cal[chan].detector = DMAP_DET_VOID;
      break;
    case DMAP_BTYPE_CHATDC:
      if (chan>=DMAP_CHAMBER_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_clear","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      dmap[chain-1].crate[crate-1][slot].data.chamber[chan].wire = DMAP_WIRE_VOID;
      break;
    case DMAP_BTYPE_QCALTDC:
    case DMAP_BTYPE_QCALADC:
      if (chan>=DMAP_QCAL_CHANS_PER_BOARD)
	{
	  ErrorSetF(DMAP_ERROR_RANGE,"dmap_clear","Channel nr out of range. (%i)",chan);
	  return DMAP_ERROR_RANGE;
	}

      dmap[chain-1].crate[crate-1][slot].data.qcal[chan].detector = DMAP_DET_VOID;
      break;
    /*default: nothing to do */
    }

  return DMAP_ERROR_OK;
}

/******************************************/
/*            Save function               */
/******************************************/

/* write the map in a way it can be reread */
int dmap_write(char *filename,     /* i.e. "../data/dmap.dat" */
	       char *system_name)  /* i.e. "System Test" */
{
  if (dmap_initialized==0)
    {
      ErrorSet(DMAP_ERROR_NOTINIT,"dmap_write","Dmap not initialized!");
      return DMAP_ERROR_NOTINIT;
    }

  {
    int chain_nr;
    FILE *fmain;

    fmain = fopen(filename,"w");

    if (fmain==NULL)
      {
	ErrorSetF(DMAP_ERROR_UNKNOWN,"dmap_write","Error creating file %s.",filename);
	return DMAP_ERROR_UNKNOWN;
      }

    fprintf(fmain,"%%===========================================\n");
    fprintf(fmain,"%% %s\n",system_name);
    fprintf(fmain,"%%===========================================\n\n");

    for (chain_nr=1; chain_nr<=DMAP_NR_CHAINS; chain_nr++)
      {
	int crate_nr;
	int chain_idx = chain_nr-1;

	fprintf(fmain,"%%=============\n");
	fprintf(fmain,"%% Chain %2i\n",chain_nr);
	fprintf(fmain,"%%=============\n\n");

	for (crate_nr=1; crate_nr<dmap[chain_idx].nrcrates; crate_nr++)
	  {
	    int board_nr;
	    int crate_idx = crate_nr-1;

	    fprintf(fmain,"%%=====================\n");
	    fprintf(fmain,"%% Chain %2i Crate %2i\n",chain_nr,crate_nr);
	    fprintf(fmain,"%%=====================\n\n");
	    
	    for (board_nr=0; board_nr<DMAP_BOARDS_PER_CRATE; board_nr++)
	      {
		int btype = dmap[chain_idx].crate[crate_idx][board_nr].btype;

		fprintf(fmain,"%%==============================\n");
		fprintf(fmain,"%% Chain %2i Crate %2i Slot: %2i\n",chain_nr,crate_nr,board_nr);
		fprintf(fmain,"%%==============================\n\n");

		if (btype!=DMAP_BTYPE_VOID)
		  {
		    switch (btype)
		      {
		      case DMAP_BTYPE_CALTDC:
		      case DMAP_BTYPE_CALADC:
			{
			  int chan_nr;
			  char acard[16];

			  if (btype==DMAP_BTYPE_CALADC)
			    strcpy(acard,"CALADC");
			  else
			    strcpy(acard,"CALTDC");

			  fprintf(fmain,"%%===============================================================================\n");
			  fprintf(fmain,"%% Chain | Crate | Slot  | Chan  |DETECT |Module | Plane |Column | Side  | Card\n");
			  fprintf(fmain,"%%===============================================================================\n");

			  for (chan_nr=0; chan_nr<DMAP_CAL_CHANS_PER_BOARD; chan_nr++)
			    {
			      DMAP_CAL_EL *el;

			      el = &(dmap[chain_idx].crate[crate_idx][board_nr].data.cal[chan_nr]);

			      if (el->detector!=DMAP_DET_VOID)
				{
				  int amodule,aplane,acolumn,aside;
				  char adetector[16];

				  amodule = el->module;
				  aplane = el->plane;
				  acolumn = el->column;
				  aside = el->side;

				  if (el->detector==DMAP_DET_ECAPA)
				    strcpy(adetector," ECAPA ");
				  else if (el->detector==DMAP_DET_ECAPB)
				    strcpy(adetector," ECAPB ");
				  else
				    strcpy(adetector," BARR  ");

				  fprintf(fmain,"   %2i      %2i      %2i      %2i    %s   %2i      %2i      %2i      %2i    %s\n",
					  chain_nr,crate_nr,board_nr,chan_nr,adetector,amodule,aplane,acolumn,aside,acard);
				}
			    }
			}
			break;
		      case DMAP_BTYPE_CHATDC:
			{
			  int chan_nr;

			  fprintf(fmain,"%%=======================================================\n");
			  fprintf(fmain,"%% Chain | Crate | Slot  | Chan  |DETECT | Layer | Wire\n");
			  fprintf(fmain,"%%=======================================================\n");

			  for (chan_nr=0; chan_nr<DMAP_CHAMBER_CHANS_PER_BOARD; chan_nr++)
			    {
			      DMAP_CHAMBER_EL *el;

			      el = &(dmap[chain_idx].crate[crate_idx][board_nr].data.chamber[chan_nr]);

			      if (el->wire!=DMAP_WIRE_VOID)
				{
				  int alayer,awire;

				  alayer = el->layer;
				  awire = el->wire;
				  fprintf(fmain,"   %2i      %2i      %2i      %2i     CHAMB   %2i      %2i\n",
					  chain_nr,crate_nr,board_nr,chan_nr,alayer,awire);
				}
			    }
			}
			break;
		      case DMAP_BTYPE_QCALTDC:
		      case DMAP_BTYPE_QCALADC:
			{
			  int chan_nr;
			  char acard[16];

			  if (btype==DMAP_BTYPE_QCALADC)
			    strcpy(acard,"QCALADC");
			  else
			    strcpy(acard,"QCALTDC");

			  fprintf(fmain,"%%=======================================================\n");
			  fprintf(fmain,"%% Chain | Crate | Slot  | Chan  |DETECT |Module | Card\n");
			  fprintf(fmain,"%%=======================================================\n");

			  for (chan_nr=0; chan_nr<DMAP_QCAL_CHANS_PER_BOARD; chan_nr++)
			    {
			      DMAP_QCAL_EL *el;

			      el = &(dmap[chain_idx].crate[crate_idx][board_nr].data.qcal[chan_nr]);

			      if (el->detector!=DMAP_DET_VOID)
				{
				  int amodule;
				  char adetector[16];

				  amodule = el->module;

				  if (el->detector==DMAP_DET_QCALA)
				    strcpy(adetector," QCALA ");
				  else
				    strcpy(adetector," QCALB ");

				  fprintf(fmain,"   %2i      %2i      %2i      %2i    %s   %2i      %s\n",
					  chain_nr,crate_nr,board_nr,chan_nr,adetector,amodule,acard);
				}
			    }
			}
			break;
		      }


		    fprintf(fmain,"\n");
		  }
	      }
	  }
      }
    
  }

  return DMAP_ERROR_OK;
}