/**
 * This is an asynchronous protocol implementation for HTTP. 
 * It includes special features to store binary data coming 
 * from a SINQ http histogram memory in a sinqdata object. 
 * Which has to be specified on initialisation.
 * 
 * copyright: see file COPYRIGHT
 * 
 * Mark Koennecke, June 2008
 */
#include  <ascon.h>
#include  <ascon.i>
#include  <ghttp.h>
#include  <sicsdata.h>
#include <HistMem.h>
/*---------------------------------------------------------------------*/
typedef struct {
	ghttp_request *request;
	char *userName;
	char *password;
	pSICSData binData;
}HttpProt, *pHttpProt;
/*---------------------------------------------------------------------*/
static int configRequest(Ascon *a){
	pHttpProt pHttp = (pHttpProt)a->private;
	pDynString request;
	char *uri = NULL;
	
	request = CreateDynString(64,64);
	if(request == NULL){
		AsconError(a,"Out of memory", 122);
		return 0;
	}
	DynStringConcat(request,"http://");
	DynStringConcat(request,a->hostport);
	DynStringConcatChar(request,'/');
	DynStringConcat(request,GetCharArray(a->wrBuffer));
	uri = GetCharArray(request);
	
	ghttp_clean(pHttp->request);
	ghttp_set_type(pHttp->request,ghttp_type_get);
	ghttp_set_header(pHttp->request,"connection", "keep-alive");
	if(ghttp_set_uri(pHttp->request,uri) < 0){
		AsconError(a,"Bad URL", a->state);
		return 0;
	}
	if(pHttp->userName != NULL && pHttp->password != NULL){
		ghttp_set_authinfo(pHttp->request, 
				pHttp->userName, pHttp->password);
	}
	ghttp_set_sync(pHttp->request,ghttp_async);
	ghttp_prepare(pHttp->request);
	DeleteDynString(request);
	
	return 1;
}
/*---------------------------------------------------------------------*/
static int HttpHandler(Ascon *a) {
  ghttp_status status;
  pHttpProt pHttp = (pHttpProt)a->private;
  char *pPtr = NULL, *pType = NULL;
  HistInt *hmData = NULL;
  int i, len;
  
  switch (a->state) {
  case AsconKillMe: return 0;
  case AsconConnectStart:
	  a->state = AsconConnecting;
    break;
  case AsconConnecting:
      a->state = AsconConnectDone; /* success */
    break;
  case AsconWriteStart:
	if(configRequest(a)){
		a->state = AsconWriting;
	}
    break;
  case AsconWriting:
	status = ghttp_process(pHttp->request);
	if(status == ghttp_error){
		ghttp_close(pHttp->request);
		configRequest(a);
		status = ghttp_process(pHttp->request);
	}
	if(status == ghttp_error){
		AsconError(a,"Server error", 123);
		DynStringConcat(a->rdBuffer,"Server error");
		a->state = AsconReadDone;
	} else {
		a->state = AsconWriteDone;
	}
	a->start = DoubleTime();
	DynStringClear(a->rdBuffer);
    break;
  case AsconReadStart:
	  status = ghttp_process(pHttp->request);
	  switch(status){
	  case ghttp_not_done:
				if(DoubleTime() > a->start + a->timeout){
				  AsconError(a," read timeout", 0);
				  a->state = AsconTimeout;
				  /* this to clear the line */
				  ghttp_close(pHttp->request);
				}
				return 1;
	  case ghttp_done:
				a->state = AsconReading;
				break;
	  case ghttp_error:
			  /*
			   * A first error may not be an error but a 
			   * reconnect
			   */
				ghttp_close(pHttp->request);
				configRequest(a);
				status = ghttp_process(pHttp->request);
				if(status == ghttp_error){
					AsconError(a,"Server error", a->state);
					DynStringConcat(a->rdBuffer,"Server error");
					a->state = AsconReadDone;
				}
				break;
	  default:
		  printf("Hugo\n");
		  break;
	  }
	  break;
  case AsconReading:
	pPtr = ghttp_get_body(pHttp->request);
	len = ghttp_get_body_len(pHttp->request);
	if(strstr(pPtr,"ERROR") != NULL){
		AsconError(a,pPtr, a->state);
		DynStringConcat(a->rdBuffer,pPtr);
	} else if(strstr(pPtr,"Authentication Error") != NULL){
		DynStringConcat(a->rdBuffer,pPtr);
		AsconError(a,pPtr, a->state);
	} else {
		pType = (char *)ghttp_get_header(pHttp->request,"Content-Type");
		if(strstr(pType,"sinqhm")  == NULL){
			/* text data */
			for(i = 0; i < len; i++){
				DynStringConcatChar(a->rdBuffer, pPtr[i]);
			}
		} else {
			hmData = (HistInt *)pPtr;
			clearSICSData(pHttp->binData);
			for(i = 0; i < len/sizeof(HistInt); i++){
				setSICSDataInt(pHttp->binData, i, htonl(hmData[i]));
			}
			DynStringCopy(a->rdBuffer,"SICSDATA");
		}
	}
	a->state = AsconReadDone;
	break;
  default:
	  return AsconStdHandler(a);
  }  
  return 1;
}
/*------------------------------------------------------------------------*/
static void killHttp(void *data){
	pHttpProt prot = (pHttpProt)data;
	if(prot == NULL){
		return;
	}
	if(prot->request != NULL){
		ghttp_close(prot->request);
	}
	if(prot->password != NULL){
		free(prot->password);
	}
	if(prot->userName != NULL){
		free(prot->userName);
	}
	free(prot);
}
/*------------------------------------------------------------------------*/
static int HttpProtInit(Ascon *a, SConnection *con, 
				int argc, char *argv[]){
		pHttpProt pHttp = NULL;
		
		pHttp = calloc(sizeof(HttpProt), 1);
		if(pHttp == NULL){
			SCWrite(con,"ERROR: out of memory in HttpProtInit", eError);
			return 0;
		}
		if(argc < 3){
			return 0;
		}
		a->hostport = strdup(argv[1]);
		pHttp->binData = (pSICSData)FindCommandData(pServ->pSics,
				argv[2], "SICSData");
		if(pHttp->binData == NULL){
			SCWrite(con,"ERROR: SICSData objct not found", eError);
			return 0;
		}
		if(argc > 3){
			a->timeout = atof(argv[3]);
		} else {
			a->timeout = 10.;
		}
		if(argc > 5){
			pHttp->userName = strdup(argv[4]);
			pHttp->password = strdup(argv[5]);
		}
		pHttp->request = ghttp_request_new();
		a->private = pHttp;
		a->killPrivate = killHttp;
		a->state = AsconConnectStart;
		return 1;
}
/*-------------------------------------------------------------------------*/
void AddHttpProtocoll(){
	AsconProtocol *prot = NULL;
	
	prot = calloc(sizeof(AsconProtocol), 1);
	prot->name = strdup("sinqhttp");
	prot->init = HttpProtInit;
	prot->handler = HttpHandler;
	AsconInsertProtocol(prot);
}
