/* program forceCausality.c
 * read a sddsfile with one matrix table.
 * Determine from the lattice file whether matrix
 * elements are supposed to be zero.
 * The row labels may be in a column named
 * *Names, or *Labels. Take the first occurence
 * of these. If they don't exist, take the first
 * string column.
 */

#include "mdb.h"
#include "scan.h"
#include "match_string.h"
#include "matlib.h"
#include "SDDS.h"

#define CLO_BEAMLINE 0
#define CLO_VERBOSE 1
#define CLO_PIPE 2
#define COMMANDLINE_OPTIONS 3
char *commandline_option[COMMANDLINE_OPTIONS] = {
  "beamline",
  "verbose",
  "pipe"
  };

static char *USAGE="forceCausality [-pipe=[input][,output]] <inputfile> <outputfile>\n\
       -beamline=<SDDSlatticeFile> [-verbose]\n\
Reads the first string column and the double column names of the input file,\n\
and determines from the order of the string data in the beamline file\n\
which matrix elements of the input file will be zeroed.\n\
pipe      reads input from and/or writes output to a pipe.\n\
beamline  reads a lattice file containing at least a string column\n\
          which will be interperted as lattice elements\n";

int main(int argc, char **argv)
{
  SCANNED_ARG *s_arg;
  SDDS_DATASET inputPage, outputPage, beamlinePage;
  
  char *inputfile, *outputfile;
  char **inputColumnName, **inputStringColumnName, **inputDoubleColumnName, **inputControlName=NULL;
  char **outputDoubleColumnName;
  char **beamlineColumnName, **beamlineStringColumnName, **beamlineControlNameValue=NULL;
  char *beamlineControlName=NULL;
  int32_t inputColumns, beamlineColumns;
  long inputRows, inputDoubleColumns, inputStringColumns;
  long outputRows, outputDoubleColumns;
  long beamlineStringColumns, beamlineRows=0;
  long i, i_arg, j, columnType;
#define BUFFER_SIZE_INCREMENT 20
  int32_t type_ptr[1]={SDDS_STRING};
  MATRIX *R;
  char *beamlineFile;
  long retval;
  long foundString_match=0;
  long verbose;
  long *positionOfRowControlName, *positionOfColumnControlName;
  unsigned long pipeFlags;
  long tmpfileUsed, noWarnings;
  
  SDDS_RegisterProgramName(argv[0]);
  argc = scanargs(&s_arg,  argc,  argv);
  if(argc==1)
    bomb(NULL, USAGE);

  verbose=0;
  inputfile = outputfile = beamlineFile = NULL;
  pipeFlags = 0;
  tmpfileUsed = 0;
  noWarnings = 0;
  for (i_arg=1; i_arg<argc; i_arg++) {
    if (s_arg[i_arg].arg_type==OPTION) {
      switch(match_string(s_arg[i_arg].list[0],  commandline_option,  COMMANDLINE_OPTIONS,  
                          UNIQUE_MATCH)) {
      case CLO_BEAMLINE:
        if(!(beamlineFile=s_arg[i_arg].list[1])){
          bomb("No file given in option -beamline", USAGE);
        }
        break;
      case CLO_VERBOSE:
        verbose=1;
        break;
      case CLO_PIPE:
        if (!processPipeOption(s_arg[i_arg].list+1, s_arg[i_arg].n_items-1, &pipeFlags))
          bomb("invalid -pipe syntax", NULL);
        break;
      default:
        bomb("unrecognized option given",  USAGE);
      }
    }
    else {
      if (!inputfile)
        inputfile = s_arg[i_arg].list[0];
      else if (!outputfile)
        outputfile = s_arg[i_arg].list[0];
      else
        bomb("too many filenames given",  USAGE);
    }
  }
  
  processFilenames("forceCausality", &inputfile, &outputfile, pipeFlags, noWarnings, &tmpfileUsed);

  if (!beamlineFile)
    bomb("beamline file not give", USAGE);
  
  if (!SDDS_InitializeInput(&inputPage, inputfile)) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }
  if (!SDDS_InitializeCopy(&outputPage,  &inputPage,  outputfile,  "w")) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }
  if (!SDDS_InitializeInput(&beamlinePage, beamlineFile)) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }
  
  if ((retval=SDDS_ReadTable(&inputPage))>0) {
    if (!SDDS_CopyTable(&outputPage,  &inputPage)) {
      SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
      exit(1);
    }
    if (!SDDS_WriteLayout(&outputPage)) {
      SDDS_PrintErrors(stderr,  0);
      exit(1);
    }
  }
  if (!retval) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }
  
  /* sort out the string and double columns of the input file */
  inputColumnName=(char**)SDDS_GetColumnNames(&inputPage, &inputColumns);
  inputDoubleColumns=0;
  inputStringColumns=0;
  inputDoubleColumnName=(char**)malloc(inputColumns*sizeof(char*));
  inputStringColumnName=(char**)malloc(inputColumns*sizeof(char*));
  inputRows=SDDS_CountRowsOfInterest(&inputPage);
  outputRows = inputRows;
  if( !SDDS_SetColumnFlags(&inputPage, 0))
    SDDS_PrintErrors(stderr, SDDS_EXIT_PrintErrors|SDDS_VERBOSE_PrintErrors);
  for (i=0; i<inputColumns; i++) {
    if ( SDDS_NUMERIC_TYPE( columnType = SDDS_GetColumnType( &inputPage, i))) {
      inputDoubleColumnName[inputDoubleColumns] = inputColumnName[i];
      SDDS_SetColumnsOfInterest(&inputPage,  SDDS_MATCH_STRING,  inputColumnName[i], SDDS_OR);
      inputDoubleColumns++;
    }
    else if ( columnType == SDDS_STRING ) {
      inputStringColumnName[inputStringColumns]=inputColumnName[i];
      inputStringColumns++;
    }
  }
  if(inputStringColumns) {
    inputControlName = (char**)SDDS_GetColumn(&inputPage, inputStringColumnName[0]);
  }
  
  /* get the control name column of the beamline file */
  /* check for the presence of one of a series of possible names */
  beamlineStringColumns=0;
  beamlineColumnName = (char**)SDDS_GetColumnNames(&beamlinePage, &beamlineColumns);
  beamlineStringColumnName = (char**)malloc(beamlineColumns*sizeof(char*));
  for(i=0;i<beamlineColumns;i++) {
    SDDS_GetColumnInformation(&beamlinePage, "type", type_ptr, SDDS_GET_BY_INDEX, i);
    if(*type_ptr==SDDS_STRING) {
      beamlineStringColumnName[beamlineStringColumns] = beamlineColumnName[i];
      beamlineStringColumns++;
    }
  }
  /* use first match to "Name" */
  for(i=0;i<beamlineStringColumns;i++) {
    if(str_in(beamlineStringColumnName[i], "Name")) {
      foundString_match=1;
      beamlineControlName = beamlineStringColumnName[i];
      break;
    }
  }
  /* or use first string column */
  if(!foundString_match)
    beamlineControlName=beamlineStringColumnName[0];
  
  switch(SDDS_CheckColumn(&beamlinePage, beamlineControlName, NULL, SDDS_STRING, stderr)) {
  case SDDS_CHECK_OKAY:
    break;
  default:
    fprintf(stderr,"Something wrong with column %s.\n", beamlineControlName);
    exit(1);
  }

  if ((retval=SDDS_ReadTable(&beamlinePage))>0) {
    beamlineRows = SDDS_CountRowsOfInterest(&beamlinePage);
    beamlineControlNameValue = (char**)SDDS_GetColumn(&beamlinePage, beamlineControlName);
  }
  if(!retval) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }

  if(inputDoubleColumns && inputRows) {
    m_alloc(&R, inputDoubleColumns, inputRows);
    for(i=0;i<inputDoubleColumns;i++) {
      R->a[i] = SDDS_GetColumnInDoubles(&inputPage, inputDoubleColumnName[i]);
    }
    if(verbose)
      m_show(R,  "%9.6le ",  "Transpose of input matrix:\n",  stderr);
  }
  /* set acausal matrix elements to zero */
  outputDoubleColumns=inputDoubleColumns;
  outputDoubleColumnName = inputDoubleColumnName;
  if(outputDoubleColumns && outputRows) {
    positionOfColumnControlName = (long*)malloc(sizeof(long)*outputDoubleColumns);
    positionOfRowControlName = (long*)malloc(sizeof(long)*outputRows);
    for (i=0;i<outputDoubleColumns;i++) {
      positionOfColumnControlName[i] = match_string(inputDoubleColumnName[i], 
                                                    beamlineControlNameValue, 
                                                    beamlineRows, 
                                                    RETURN_FIRST_MATCH);
      if(verbose)
        fprintf(stderr,"Column name: %s position: %ld.\n", inputDoubleColumnName[i], positionOfColumnControlName[i]);
    }
    for(j=0;j<outputRows;j++) {
      positionOfRowControlName[j] = match_string(inputControlName[j], beamlineControlNameValue, beamlineRows, RETURN_FIRST_MATCH);
      if(verbose)
        fprintf(stderr,"Row name: %s position: %ld.\n", inputControlName[j], positionOfRowControlName[j]);
    }
    for (i=0;i<outputDoubleColumns;i++) {
      if(positionOfColumnControlName[i]!=-1){
        for(j=0;j<outputRows;j++) {
          if(positionOfRowControlName[j]!=-1){
            if(positionOfRowControlName[j]<positionOfColumnControlName[i])
              R->a[i][j]=0;
          }
        }
      }
      SDDS_SetColumn(&outputPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, 
                     R->a[i], outputRows, outputDoubleColumnName[i]);
    }
  }
  if(inputDoubleColumns && inputRows && verbose)
    m_show(R,  "%9.6le ",  "Transpose of output matrix:\n",  stderr);
  
  if (!SDDS_WriteTable(&outputPage)) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }

  if (!SDDS_Terminate(&inputPage) || !SDDS_Terminate(&outputPage)) {
    SDDS_PrintErrors(stderr,  SDDS_VERBOSE_PrintErrors);
    exit(1);
  }

  if (tmpfileUsed && !replaceFileAndBackUp(inputfile, outputfile)) 
    exit(1);
  return(0);
}
