//			March, 97, start
// cdev $vector $device $message ... ..

#include <tcl.h>
#include "blt.h"

#include <ctype.h>
#include <string.h>
#include <iostream.h>
#include <math.h>
#include <dl.h>

// cdev includes
#include <cdevSystem.h>
#include <cdevRequestObject.h>
#include <cdevDevice.h>
#include <cdevGroup.h>
#include <cdevData.h>

static int VALUE_TAG_ID =- 1;
#define VALUE_TAG ((VALUE_TAG_ID<0 && cdevData::tagC2I("value", &VALUE_TAG_ID)!= CDEV_SUCCESS)?-1:VALUE_TAG_ID)
// static int STATUS_TAG_ID =- 1;
// #define STATUS_TAG ((STATUS_TAG_ID<0 && cdevData::tagC2I("status", &STATUS_TAG_ID)!= CDEV_SUCCESS)?-1:STATUS_TAG_ID)
// static int SEVERITY_TAG_ID =- 1;
// #define SEVERITY_TAG ((SEVERITY_TAG_ID<0 && cdevData::tagC2I("severity", &SEVERITY_TAG_ID)!= CDEV_SUCCESS)?-1:SEVERITY_TAG_ID)

static int cdevdebug = 0;

class tclCdevDataV {
	public:
	tclCdevDataV();
	void parse(Tcl_Interp*, char* *, int);
	~tclCdevDataV();
	
	cdevData out, context;
	int hascontext;
	
	int tagn, *taglist;
	char *callbacktag;
	
	int state, async;
};

tclCdevDataV::~tclCdevDataV() {
	delete taglist;
}

tclCdevDataV::tclCdevDataV():hascontext(0), tagn(0), taglist(NULL), async(0), state(TCL_ERROR) {
};

// Parses command line, fills up the outgoing cdevData and the context (if any)
void tclCdevDataV::parse(Tcl_Interp *interp, char **argv, int argc) {
	int i, c1, c2;
	cdevData *ptr;
	char **s1 = NULL;
	char **s2 = NULL;

	// Point to 'out' cdevData until we hit the context (if any)
	ptr = &out;
	for(i = 3; i < argc; i++) {
		// skip 'cdev' '$device' and '$message'
		if(Tcl_SplitList(interp, argv[i], &c1, &s1) != TCL_OK) {
			return;
		}
		if(c1 == 2) {
			// {tag value} pair, check if the value is a scalar
			if(Tcl_SplitList(interp, s1[1], &c2, &s2) != TCL_OK) {
				free((char *) s1);
				return;
			}
			if(c2 == 1) {
				// we have a scalar
				ptr->insert(s1[0], s1[1]);
				// insert scalar
			}
			else {
				ptr->insert(s1[0], s2, c2);
				// insert vector of scalars, does not handle matrices
			}
			free((char *) s2);
		}
		else if(c1 == 1) {
			// can be one of "-context" , "-tags" or a callbacktag
			if(strcmp(argv[i], "-context") == 0) {
				// all remaining 'tag value' pairs go in the context
				ptr = &context;
				hascontext = 1;
			}
			else if(strcmp(argv[i], "-tags") == 0) {
				// get the list of tags to print out from the result data
				i++;
				// points to next entry in argv
				if(i == argc) {
					interp->result = "expected tag names";
					free((char *) s1);
					return;
				}
				if(Tcl_SplitList(interp, argv[i], &tagn, &s2) != TCL_OK) {
					interp->result = "tag problem";
					free((char *) s1);
					return;
				}
				else {
					taglist = new int[tagn];
					for(int j = 0; j < tagn; j++) {
						if(cdevData::tagC2I(s2[j], &taglist[j]) != CDEV_SUCCESS) {
							interp->result = "unknown tag";
							free((char *) s2);
							free((char *) s1);
							return;
						}
					}
				}
				free((char *) s2);
			}
			else {
				// single entry, a callbacktag, better be at end of input
				if(i != argc-1) {
					interp->result = "expecting callbacktag at end";
					free((char *) s1);
					return;
				}
				// Here we have a callback tag, hence we want to send asynchronously
				callbacktag = argv[i];
				async = 1;
			}
		}
		else {
			// c1 > 2
			// tag / string pair (i.e. "value this is a string with spaces embedded"
			char **av = s1;
			av++;
			char *concat = Tcl_Concat(c1-1, av);
			ptr->insert(s1[0], concat);
			if(cdevdebug == 1) {
				fprintf(stderr, "inserted: %s, with value: %s\n", s1[0], concat);
			}
			free(concat);
		}
		free((char *) s1);
	}
	state = TCL_OK;
}

class callbackInfoV {
	public:
	callbackInfoV(Tcl_Interp*, char *, Blt_Vector); // vectorname
	callbackInfoV(Tcl_Interp*, char *, Blt_Vector, int); // vectorname
	~callbackInfoV();
	Tcl_Interp *interp;
	cdevCallback *callback;
	Blt_Vector vecInfo;
	char *vecName;
	int index;
};

callbackInfoV::callbackInfoV(Tcl_Interp *e, char *arg, Blt_Vector i) {
	interp = e;
	vecName = new char[strlen(arg)+1];
	vecInfo = i;
	strcpy(vecName, arg);
	callback = NULL;
	index = -1;
}

callbackInfoV::callbackInfoV(Tcl_Interp *e, char *arg, Blt_Vector i, int ind) {
	interp = e;
	vecName = new char[strlen(arg)+1];
	vecInfo = i;
	strcpy(vecName, arg);
	callback = NULL;
	index = ind;
}

callbackInfoV::~callbackInfoV() {
	delete vecName;
}

cdevBounds *BoundsV;
int DimV;

int HandleTagV(Tcl_Interp *interp, cdevData &result, Blt_Vector *vecInfo, char* vecname, int index) {
	int i, len;
	size_t dim;
	
	if(result.getDim(VALUE_TAG, &dim) != CDEV_SUCCESS) {
		return TCL_ERROR;
	}

	if( ((dim == 0) && (index < 0)) || ((dim == 1) && (index >= 0)) ) {
		return TCL_ERROR;
	}

	if (dim == 0) {
		if (vecInfo->numValues <= index) {
			return TCL_ERROR;
		}
		vecInfo->valueArr[index] = (double)result;
		return Blt_ResetVector(interp, vecname, vecInfo, TCL_DYNAMIC);
	}
	
	// dim = 1
	DimV = (int) dim;
	BoundsV = new cdevBounds[DimV];
	result.getBounds(VALUE_TAG, BoundsV, DimV);
	len = 1;
	for(i = 0; i < DimV; i++) {
		len *= BoundsV[i].length;
	}
	
	if (vecInfo->arraySize < len) { // resize if necc.
		if (Blt_ResizeVector(interp, vecname, len) != TCL_OK) return TCL_ERROR;
		Blt_GetVector(interp, vecname, vecInfo);
	}
	
	result.get(VALUE_TAG, vecInfo->valueArr);

	delete BoundsV;
	if (Blt_ResetVector(interp, vecname, vecInfo, TCL_DYNAMIC) != TCL_OK) {
		return TCL_ERROR;
	}
	return TCL_OK;
}

// executes inside poll() whenever 'file' is readable
void Cdev_CallbackFunctionV(int, void *userarg, cdevRequestObject&, cdevData &result) {
	// could check status and do appropriate action like: set Error(info->s) ???
	callbackInfoV *info = (callbackInfoV *) userarg;
	HandleTagV(info->interp, result, &info->vecInfo, info->vecName, info->index);
}

// same as above, plus removes callbackinfo
void Cdev_CallbackFunctionOnceV(int, void *userarg, cdevRequestObject&, cdevData &result) {
	callbackInfoV *info = (callbackInfoV *) userarg;
	HandleTagV(info->interp, result, &info->vecInfo, info->vecName, info->index);
	delete info;
}

typedef cdevRequestObject *cdevRequestObjectPtr;

int CdevVectorCmd(ClientData, Tcl_Interp *interp, int argc, char **argv) {
	// cdevvector vector device message {tag value} {..} cb
	
	cdevData result;
	cdevCallback *cb = NULL;
	callbackInfoV *info;
	tclCdevDataV data;
	int n, delinfo = 0;
	Blt_Vector vecInfo;
	char **devices = NULL;
	cdevRequestObjectPtr request, *requests;
	
	if(argc < 4) {
		Tcl_AppendResult(interp, "cdevvector: wrong # args", (char *) NULL);
		return TCL_ERROR;
	}

	if (!Blt_VectorExists(interp, argv[1])) {
		if (Blt_CreateVector(interp, argv[1], 64, &vecInfo) != TCL_OK) {
			interp->result = "vector creation error";
			return TCL_ERROR;
		}
	} else {
		if (Blt_GetVector(interp, argv[1], &vecInfo) != TCL_OK) {
		return TCL_ERROR;
		}
	}

	int ndevices = 0;

	if(Tcl_SplitList(interp, argv[2], &ndevices, &devices) != TCL_OK) {
		return TCL_ERROR;
	}

	if (ndevices == 1) {
		request = cdevRequestObject::attachPtr(argv[2], argv[3]);
		free((char*)devices);
	} else {
		requests = new cdevRequestObjectPtr[ndevices];
		for(n = 0; n < ndevices; n++) {
			requests[n] = cdevRequestObject::attachPtr(devices[n], argv[3]);
		}
		if (vecInfo.arraySize < ndevices) { // resize if necc.
			if (Blt_ResizeVector(interp, argv[1], ndevices) != TCL_OK) return TCL_ERROR;
			Blt_GetVector(interp, argv[1], &vecInfo);
		}
		free((char *) devices);
	}
	
	data.parse(interp, argv+1, argc-1);
	
	if(data.state != TCL_OK) {
		return TCL_ERROR;
	}

	if(strncmp(argv[3], "monitorOn", 9) == 0) data.async = 1;

	if(data.async) {
		// we want to sent it async

		if (ndevices == 1) {
			info = new callbackInfoV(interp, argv[1], vecInfo);

			if(strncmp(argv[3], "monitorOn", 9) == 0) {
				cb = new cdevCallback(Cdev_CallbackFunctionV, (void *) info);
			}
			else if(strncmp(argv[3], "monitorOff", 10) == 0) {
				delinfo = 1;
				cb = new cdevCallback(NULL, NULL);
			}
			else {
				cb = new cdevCallback(Cdev_CallbackFunctionOnceV, (void *) info);
			}

			info->callback = cb;

			if(data.hascontext) {
				if(request->setContext(data.context) != CDEV_SUCCESS) {
					interp->result = "setContext error";
					delete info;
					return TCL_ERROR;
				}
			}

			if(request->sendCallback(data.out, *cb) != CDEV_SUCCESS) {
				delete info;
				return TCL_ERROR;
			} else {
				if(delinfo) delete info;
				return TCL_OK;
			}
		} else { // ndevices > 1
				int j = 0;
			
			if(strncmp(argv[3], "monitorOn", 9) == 0) {
				j = 1;
			}
			else if(strncmp(argv[3], "monitorOff", 10) == 0) {
				j = 2;
			}
				
			for(n = 0; n < ndevices; n++) {
				info = new callbackInfoV(interp, argv[1], vecInfo, n);

				if(j == 1) {
					cb = new cdevCallback(Cdev_CallbackFunctionV, (void *) info);
				}
				else if(j == 2) {
					delinfo = 1;
					cb = new cdevCallback(NULL, NULL);
				}
				else {
					cb = new cdevCallback(Cdev_CallbackFunctionOnceV, (void *) info);
				}
				
				info->callback = cb;
				
				if(data.hascontext) {
					if(requests[n]->setContext(data.context) != 0) {
						interp->result = "setContext error";
						delete [] requests;
						delete info;
						return TCL_ERROR;
					}
				}
				if(requests[n]->sendCallback(data.out, *cb) != CDEV_SUCCESS) {
					delete [] requests;
					delete info;
					return TCL_ERROR;
				} else {
						if(delinfo) delete info;
				}
			}
			delete [] requests;
			return TCL_OK;
		}
	}	

	// send it sync
	if (ndevices == 1) {
		if(data.hascontext) {
			if(request->setContext(data.context) != CDEV_SUCCESS) {
				interp->result = "setContext error";
				return TCL_ERROR;
			}
		}

		if(request->send(data.out, result) != CDEV_SUCCESS) {
			sprintf(interp->result, "error in send: %s %s", argv[2], argv[3]);
			return TCL_ERROR;
		}

		HandleTagV(interp, result, &vecInfo, argv[1], -1);

		return TCL_OK;
	} else {
		for(n = 0; n < ndevices; n++) {
			if(data.hascontext) {
					if(requests[n]->setContext(data.context) != 0) {
						interp->result = "setContext error";
						delete [] requests;
						delete info;
						return TCL_ERROR;
					}
				}
			if(requests[n]->send(data.out, result) != CDEV_SUCCESS) {
					delete [] requests;
					delete info;
					return TCL_ERROR;
				} else {
					if(delinfo) delete info;
					HandleTagV(interp, result, &vecInfo, argv[1], n);
				}
			}
			delete [] requests;
			return TCL_OK;
		}
}

extern "C" int Cdevvector_Init(Tcl_Interp *interp) {

	Tcl_PkgRequire(interp, "Blt", "2.1", 0);
	Tcl_PkgRequire(interp, "Cdev", CDEV_VERSION, 0);

	if(Tcl_PkgProvide(interp, "Cdevvector", CDEV_VERSION) != TCL_OK) {
		return TCL_ERROR;
	}
	
	Tcl_CreateCommand(interp, "cdevvector", CdevVectorCmd, (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);

	return TCL_OK;
}



