/*
 * cfortran prototypes for the needed fortran calls.
 */

#define MAXFIL 500

#define XPARSE_EOF -1

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Fakeit command implementation
 * Ben Dorman, Raytheon ITSS  April 1999
 *
 * xs_fakeit -- prompt user for information needed for creating 
 * fake data and create it.
 *
 */
#define LOGICAL_STRICT

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <cfortran.h>
#include <pctype.h>
#include <ctype.h>
#include "xstcl.h"
#include "xscf.h"

/* for file status checking */

#include <sys/types.h>
#include <sys/stat.h>


int getFakeitInput(char* promptString, char** batchString, char** batchLine);


#ifdef __cplusplus
}
#endif

int
xs_fakeit(ClientData cdata,Tcl_Interp* interp,int objc, Tcl_Obj* CONST objv[] )
{
        int newFiles[MAXFIL];
        int newFakeType[MAXFIL];
        int dontFake = 0;
        int i = 0;
        unsigned int  parsePosn = 0; /* xparse parse position */
	int fileno = 1;        /* ifile - dataset number */
	int status = 0;         
	int inputStatus  = 0;
	int ndata = 0;         /* holder for number of datasets */
	char* arg = NULL;      /* arguments returned by fortran routines through Tcl result string*/
        char* command = NULL;   /* command string  */
        unsigned int ncommand = 0;  /* length of command, excluding subsequent lines
                                        passed to routine by command script */
        char* tmpString = NULL;/* temporary holder for entire input string */ 
        char* outString = NULL;/* place to write result strings from fortran routines 
                                  passed through the tcl interp token */
	char* outStrPtr = NULL; /* placeholder for garbage collection */
	char* promptString = NULL; /* place to write prompt strings */
        char* batch = NULL;    /* batch string, delimited by "&" characters */
        char* batchPtr = NULL;
	char* buffer = NULL;   /* input buffer from terminal prompts */
        char* tmpBuf = NULL;
        char* tmpBufPtr = NULL;
        char*  type = NULL;   /* type character required for processing filenames */
        char* prefix = NULL;
        char* ckPref = NULL;
        float time = 0;
        float crnm = 0;
        int cmdlen = 0;
        char none[] = "none";
        char* fakeFileName = NULL;
        struct stat* statBuf = NULL;
        Tcl_Obj* cmdlineObj;
        /* statistics reporting */
        
        int dataEnd = 0;
        float statistic = 0.;
        int fakeNone = 0;
        int newFilesForFake = 0;      
        int* numFiles;
        /* declarations for use with regular expression handling */
        
        int invalidFile = 0;
        int ival = 0;
        float rval = 0;
        Tcl_RegExp FakeitExp;
        char* start = NULL;
        char* end   = NULL;
        char* bufEnd = NULL;
        char* dum = NULL;

/* to cope with non-interactive (script) processing, split the input
   into command and batch substrings */

	if ( (ndata = numberOfDataSets()) == 0 && objc == 1)
        {
		char* msg = "Error: no responses defined (perhaps \"fakeit none\" was intended?)";
		xs_tcl_write(msg,5);
		return TCL_OK;
	}
            
        if ( objc > 1 && !strcmp(Tcl_GetString(objv[1]), none) ) fakeNone = 1;
        
        


        cmdlineObj = Tcl_ConcatObj(objc,objv);
        dum = Tcl_GetStringFromObj(cmdlineObj,&cmdlen);
        command = (char *)malloc((cmdlen+1)*sizeof(char));
                 
        ncommand = strcspn(dum,"&");
        strncpy(command,dum,ncommand);
        *(command + ncommand) = (char)NULL; /* strncpy doesn't null-terminate strings */
        parsePosn = strcspn(command," ");
        
        if (ncommand < cmdlen )
        {
                batch = (char *)malloc((cmdlen+1)*sizeof(char));
                strcpy(batch,strchr(dum,'&'));
                batchPtr = batch;
                batch++; /* strip leading "&" */
        }
        else
        {
                batch = (char *)malloc(sizeof(char));
                batchPtr = batch;
                strcpy(batch,"");
        }
        
        
        for (i = 0; i < MAXFIL; i++)
        {
                newFiles[i] = 0;
                newFakeType[i] = 0;
        }
        
/* call gtfkfl... */
       
        getFakeFile(command,parsePosn,newFiles,newFakeType, &dontFake);
        free(command);
        command = NULL;

/* process response file names */

       
        /* dontFake is non-zero if there are no datasets defined or arguments to fakeit. In
           this case, quietly exit */
           
        if (dontFake == 0 )
        {
		promptString = (char *)malloc(256*sizeof(char));

		if ( strlen(Tcl_GetStringResult(interp)) > 0 )
		{
                   outString = (char *)malloc((strlen(Tcl_GetStringResult(interp))+1)*sizeof(char));
                   strcpy(outString, Tcl_GetStringResult(interp));
		} else {
                   outString = (char *)malloc(2*sizeof(char));
                   strcpy(outString, " ");
		}
		outStrPtr = outString;
	        arg = returnDelimitedArgument(&outString," \n");
       
        }
	else 
	{
                free (batchPtr);
		return TCL_ERROR;
	}
        
        while (arg != NULL)
	{
                char r[] = "r"; /* removes warning generated from cfortran call */
                char a[] = "a";
                
		status = sscanf(arg ,"%d",&fileno);

		assert (status == 1 );
                
/* if input is "/*" (inputStatus == XPARSE_EOF) we must delete datasets and exit since
   gtfkfl has already deleted all previous data and set up pointers for the fake data.
   On the other hand, '/' uses default for this value */
                
		sprintf(promptString,
		  "For fake data, file #%4d needs response file: ",fileno);
                   
                inputStatus = getFakeitInput(promptString,&batch,&buffer);
                if( inputStatus == XPARSE_EOF) break;
                FPAUXF(fileno,buffer,r,inputStatus);
                
                if ( (*buffer == '/' && strlen(buffer) <= 2) || strlen(buffer) == 0 )
                {
                        strcpy(buffer,"none");
                        invalidFile = 1;
                        FPAUXF( fileno,none,r,inputStatus);       
                }
                else 
                {
                        newFilesForFake++;
                        invalidFile = 0;
      
                }
                


                if (!invalidFile)
                {
		        sprintf(promptString, 
		        "              ... and ancillary response file: ");
                        inputStatus = getFakeitInput(promptString,&batch,&buffer);
                        if (inputStatus == XPARSE_EOF) break;
                 
                        if ( (*buffer == '/' && strlen(buffer) <= 2) || strlen(buffer)  == 0) 
                        {
                                        FPAUXF( fileno,none,a,inputStatus);
                        }
                        else 
                        {
                                FPAUXF(fileno,buffer,a,inputStatus);
                        }
                }
                free(arg);
                arg = returnDelimitedArgument(&outString," \n");

		fileno++;
        }
                                
        

	if (inputStatus == XPARSE_EOF || invalidFile )
	{
		/* user changed mind and decided not to do this. clear all datasets 
                   and exit quietly after tidying up.*/
                free(batchPtr);
		free(command);
		free(promptString);
		free (buffer);
		Tcl_Eval(interp,"data none");
                return TCL_OK;
	}
        
        /* otherwise, setup the newFiles and newFakeType array for the number of valid 
           responses that can be used for fake data */
        numFiles = (int  *)malloc(sizeof(int));
        setFakeFile(newFiles,newFakeType, *numFiles);
 
        
        /* now process names. If we got here with no command line arguments,
          then the existing dataset response files should be used. Otherwise,
          they will have been set up with setFakeFile */
        
        Tcl_ResetResult(interp);
        strcpy(outString,"");        
        
        
	if (*numFiles > 0) 
        {
                char* tmpPtr = NULL;
        	unsigned int lp = 0;
                outString = setFake(newFakeType);

		/* if existing datasets are to be used, need to allocate
		   memory for the prompt string & return buffer,
 		   since the loop earlier was skipped */

		if ( promptString == NULL )
		{
			promptString = (char *)malloc(65*sizeof(char));
		}
                
                
                /* arg should never be null if we are here, because there must be 
                at least one dataset to provide a response matrix for fake data. */                
                free(arg);
                arg = returnDelimitedArgument(&outString," \n"); 
                
                /* we can also get here with arg = "" if the only data set is
                 a dummy response, which would return a blank string here */
                 
	        if ( arg == NULL )
                {
		        char* msg = "Error: only a dummy response is present (perhaps \"fakeit none\" was intended?)";
		        xs_tcl_write(msg,5);
		        return TCL_OK;
	        }
                
                
                assert (arg != NULL);

   
                /* process the results from the randomization question */
                sprintf(promptString,"Use randomization in creating fake data? (y) ");
                inputStatus = getFakeitInput(promptString,&batch,&buffer);
                if (inputStatus == XPARSE_EOF) goto reset;                        
                while (1)
                {
                        if ( strchr("/yY",*buffer) != NULL || strlen(buffer) == 0) 
                        {
                                DPQFST(1,inputStatus);
                                break;
                        }
                        else
                        {
                                if (strchr("nN",*buffer) != NULL ) 
                                {
                                        DPQFST(0, inputStatus );
                                        break;
                                }
                                else inputStatus = getFakeitInput(
                                        "Answer yes (/Yy) or no (Nn):",&batch,&buffer);
                                if (inputStatus == XPARSE_EOF) goto reset;  
                        }
                }
 
                /* prompt user for prefix for fakeit simulation data. The filename
                  is limited to 15 characters, which affects the length of the prefix. */

            
                type = (char *)malloc(2*sizeof(char));
                status = sscanf(arg,"%d:%s", &fileno, type);

                if (!strcmp(type,"d") ) tmpPtr = dataFilename(fileno);
                else tmpPtr = auxFilename(fileno,type);

                i = strcspn(tmpPtr,".");
		/* however, if we happen to be given a long response filename, give the
                   user a 4 character identifier. */
                lp = MAX(12 - i,4); /* so we get <= 15 char filenames, i = length of 
                             /* datafile stem + .xxx for the extension == 15 - (i+4)
                             /* ... plus one for the terminator. = 15 - i                          
                /* process the name string arguments */

                sprintf(promptString,"Input optional fake file prefix (max %d chars): ",lp);
                inputStatus = getFakeitInput(promptString,&batch,&buffer);
                if (inputStatus == XPARSE_EOF) goto reset;

                if ( strchr("/\n",*buffer) == NULL)
                {
                        prefix = (char* )malloc((lp+1)*sizeof(char));                        
                        strcpy(prefix,"");                        
                        ckPref = buffer;
                        while ( ckPref < (buffer + lp) && (*ckPref != '\0') )
                        {
                                if ( !isdigit(*ckPref) && !isalpha(*ckPref) )
                                {
                                        xs_tcl_write("Prefix must consist of alphanumeric string",5);
                                        inputStatus = getFakeitInput("Prefix:",&batch,&buffer);                                                ckPref = buffer;
                                }
                                else ckPref++;
                        }
                        lp = MIN(strlen(buffer),lp);
                        strncpy(prefix,buffer,lp);
			*(prefix + lp) = '\0';
               }
               else 
               {
                        prefix = (char *)malloc(sizeof(char));
			strcpy(prefix,"");
               }                        

               while (arg != NULL)
               {

                       fakeFileName = (char *)malloc(256*sizeof(char));
                       strcpy(fakeFileName,prefix);
                       strncat(fakeFileName,tmpPtr,i);
                       strcat(fakeFileName,".fak");

                        /* fakefileName defaults to the value generated in NAMFAK */

                        while (1)
                        {
                                sprintf(promptString,
                                   " Fake data filename (%s) [/ to use default]: ",fakeFileName);
                                inputStatus = getFakeitInput(promptString,&batch,&buffer);
				if (inputStatus == XPARSE_EOF) goto reset;
                                /*
                                * Set the name of the fake data output file if they chose not to
                                * use the default.
                                */                       
                                if ( ! (strlen(buffer)==0 || (strlen(buffer)==1 && buffer[0] == '/')) ) 
                                {
                                        if (checkForFileOpen(buffer,"w")) 
                                        {       
                                                strcpy(fakeFileName,buffer);
                                        }
                                        else
                                        {
                                                tmpString = (char* )malloc(256*sizeof(char));
                                                sprintf(tmpString,
                                                    "File %s is not writeable: using default: %s\n",
                                                     buffer,fakeFileName);    
                                                XWRITE(tmpString,5);
                                                free(tmpString);
                                        }   
                                }                           
                                
                                FPFLNM(fileno,fakeFileName,inputStatus);
                                
        /* note for future reference: stat call is system dependent. It is correct for
        Unix. Windows NT requires _stat(). But both behave the same way: they return
        0 if the file exists or ENOENT if not - plus other retcodes for file writing problems 
        Bypass this test if we are executing a script, in which case XSPEC will always overwrite
        the existing file.
        */


                                if (  !executingScript  )
                                {
                                        struct stat* test = (struct stat*) malloc(sizeof(struct stat));
                                        if ( !stat(fakeFileName,test) )
                                        {
                                                sprintf(promptString,
                                                        "File %s exists - overwrite? (yY/) or (nN)",
                                                        fakeFileName);
                                                inputStatus 
                                                  = getFakeitInput(promptString,&batch,&buffer);
                                                if (inputStatus == XPARSE_EOF) break;

                                                if ( strchr("yY/",*buffer) != NULL) 
                                                {
                                                        break;
                                                }
                                                else
                                                {
                                                        sprintf(promptString,"Enter new name: ");
                                                        inputStatus = getFakeitInput(promptString,
                                                                &batch,&buffer);
                                                        if (inputStatus == XPARSE_EOF) break;

                                                        strcpy(fakeFileName,buffer);
                                                        FPFLNM(fileno,fakeFileName,inputStatus);
                                                }
                                        }
                                        free(test);
                                }
                                break;

                        }

                        if (inputStatus == XPARSE_EOF) goto reset;

                        DWTIME(time, fileno);
                        DWCRNM(crnm, fileno);

                        sprintf(promptString," Exposure time, correction norm  (%g, %g): ",
                                time,crnm);
                        inputStatus = getFakeitInput(promptString,&batch,&buffer);
                        if (inputStatus == XPARSE_EOF) break;
                        tmpBuf = (char*)malloc(strlen(buffer)+1);
                        strcpy (tmpBuf,buffer);
                        tmpBufPtr = tmpBuf;

                        ival = 1;
                        if ( strchr("/",*tmpBuf) == NULL && strlen(tmpBuf) != 0)  
                              /* if a '/' wasn't entered to indicate default */
                              /* values are requested, process the input */
                        {
                                while (strlen(tmpBuf) > 0)
                                {
                                        start = returnDelimitedArgument(&tmpBuf,",\t");              
                                        if ( sscanf(start,"%g",&rval))
                                        {
                                                switch(ival)
                                                {
                                                        case 1: DPTIME(fileno,rval,inputStatus); 
                                                        break;
                                                        case 2: DPCRNM(fileno,rval,inputStatus); 
                                                        break;
                                                }
                                        }
                                        else
                                        {
                                                tmpString = (char *)malloc(256*sizeof(char));
                                                sprintf(tmpString,
                                                 "Unable to parse \"%s\" as a number. Default used",
                                                 start);
                                                xs_tcl_write(tmpString,5);
                                                free(tmpString);
                                        }
                                        free(start);
                                        ival++;
                                }
                        }
                        free (tmpBufPtr);
                        free(fakeFileName);
                        free(arg);
                        arg = returnDelimitedArgument(&outString," \n");
			if (arg != NULL)
			{
		               status = sscanf(arg,"%d:%s", &fileno, type);
	
       			       if (!strcmp(type,"d") ) tmpPtr = dataFilename(fileno);
       			       else tmpPtr = auxFilename(fileno,type);
			}
                } /* while arg != NULL */
                
		if (inputStatus == XPARSE_EOF) goto reset;
                dataEnd = endBin(ndata);
                makeFakeData(newFiles,newFakeType, statistic, MAXFIL);
		XPFSTT(statistic, inputStatus);
                writeStat();
       } /* if ndata > 0 */
        
        
        
        free (buffer);
        free(statBuf);
        free(prefix);
        free (promptString);
        free (outStrPtr);
        free (batchPtr);
        return TCL_OK;
        
        reset:
		XWRITE("Error: insufficient arguments supplied to fakeit command\n",5);

                free(statBuf);
        	free(prefix);
        	free (outStrPtr);
                free (promptString);
                free (buffer);
        	free (batchPtr);
                Tcl_Eval(interp,"data none");
                return TCL_ERROR;
}

/* return buffer string either from the terminal prompt or a string */

int getFakeitInput(char* promptString, char** batchString, char** buffer)
{
        int status = 0;
        
	if (executingScript && strlen(*batchString) == 0) return XPARSE_EOF;

        if ( executingScript || (strlen(*batchString) > 0) ) 
        {
                char* bufTmp = NULL;
                char* bufTmpPtr = NULL;
		int nc = 0;
		free(*buffer);
                *buffer = 0;
                bufTmp = returnDelimitedArgument(batchString,"&\n");
                bufTmpPtr = bufTmp;
		if (bufTmp == NULL || strlen(bufTmp) == 0) 
		{
			bufTmp = (char *)malloc(sizeof(char));
			strcpy(bufTmp,"");
		}
                else
                {
                        while (strchr (" \t",*bufTmp) != NULL ) bufTmp++;
                }
 		nc = strcspn(bufTmp," \t");
                *buffer = (char*) malloc((nc+1)*sizeof(char));
                strncpy(*buffer,bufTmp,nc);
		(*buffer)[nc] = '\0';
                free(bufTmpPtr);
                if ( executingScript) 
                {
		        bufTmp = (char *)malloc((strlen(*buffer)+1)*sizeof(char));
                        strcpy(bufTmp,*buffer);
                        xs_tcl_read(promptString,bufTmp,&status);
                        free(bufTmp);
                }
                else
                {
                        char* outStr = NULL;
                        outStr = (char*) malloc(strlen(promptString)+strlen(*buffer)+4);
                        sprintf(outStr,"!%s %s",promptString,*buffer);
                        XWRITE(outStr,5);
                        free(outStr);
                        outStr = NULL;
                }
                
        }
        else
        {
		*buffer = (char *)malloc((XCM_LEN+1)*sizeof(char));
                xs_tcl_read(promptString,*buffer,&status);
        }
        
        return status;
}
