#include <stdio.h>
#include <stdlib.h>


#include "tdc1190_driver_high.h"
#include "console.h"

#include "keyboard.h"
#include "tdc_acq.h"
 

/*--------------------------------------------------------------*/
typedef char string[50];
string main_menu[10]={"9","Set/Add a TDC","Base Address","Save/Load to/from file--\n",
		     "Micro-controller setup", "More Micro-controller setup", "Register setup\n", 
		     "Debug mode", "Acquisition mode\n","Exit"};

string micro_control_menu_1[13]={"12","Module Reset\n",
		      "Set Trigger Matching/Continuous Storage Mode", "Set/Clear token\n",
		      "Set window width and offset", "Set extra search and reject margins\n",
		      "Enable/Disable substraction trigger time",
		      "Measurement Type", "Resolution\n", "DeadTime", "Maximum Hits\n", 			  
		      "More Micro-controler setup\n", "Exit"};

string micro_control_menu_2[13]={"12","Enable/Disable Header and Trailer in readout","Enable/Disable TDC error mark in readout",
			"Enable/Disbale bypass if error", "Set TDC internal error type (not used)","Set FIFO size\n",			
			  "Set TDC global offset", "Set channel offset",
			  "Enable/Disbable all channels","Enable/Disable one channel\n",
			  "Enable test mode","Disable test mode\n", "Exit"};

string register_menu[13]={"12","Software Clear Register", "Event Reset Register\n","Set control register",
			  "Set GEO address","Set MCST base adresse","Set MCST/CBLT control\n",
			  "Set almost full register","Set interrupt level","Set interrupt vector register\n",
			  "Set OUT_PROG Control Register","Set Testreg register\n","Exit"};

string debug_menu[11]={"10","Micro-controller setup", "More Micro-controler setup", "Register setup","Software Clear Register\n",
			   "Show registers status", "Send software trigger register\n",
			   "Read one event from output register","Read events continuously\n","Time calibration\n","Exit"};

string acquisition_menu[8]={"7","Micro-controller setup", "More Micro-controller setup", "Register setup\n","Acquisition Parameters","Local Acquisition","Remote Acquisition\n","Exit"};

/*--------------------------------------------------------------*/
tdcList acq;

/*--------------------------------------------------------------*/
static int menu(string *table);
static int yes_no(void);
static void select_tdc(tdcList *aList);
static int micro_control(tdcList *aList);
static int more_micro_control(tdcList *aList);
static int registers(tdcList *aList);
static int debug_mode(tdcList *aList);
static int acquisition_mode(tdcList *aList);
/*--------------------------------------------------------------*/
static void select_tdc(tdcList *aList)
{ 
  char s[80];
  int i; 
  do {
    printf("\nEnter the TDC number (%i max) :",aList->numberOfTdc-1);
    scanf("%s",s);
    i=atoi(s);
  } while (i<0||i>aList->numberOfTdc-1);
  aList->currentTdc=i;
}
/*--------------------------------------------------------------*/
static int menu(string *table)
{  
  int imax,i,result,ok=0;
  char s[80];
  do {
    imax=atoi(table[0]);
    printf("\n\n");
    for(i=1; i<=imax;i++) {
      printf("%i/ %s \n",i,table[i]);
    } 
    printf("\n\n");
    printf("Enter a choice :");
    scanf("%s",s);
    result=atoi(s);
    if ((result>0)&&(result<=imax)) ok=1;
  } while (!ok);
  return result;
}
/*--------------------------------------------------------------*/
static int yes_no(void)
{ 
  int c;

  do {
    c=getchar();
    /*printf("%c<--> %i",c,c);*/
  } while ((c!='y')&&(c!='n'));

  if (c=='y') return 1;
  return 0;
}
/*--------------------------------------------------------------*/
static int micro_control(tdcList *aList)
{
  tdc1190 *aTdc=&aList->listOfTdc[aList->currentTdc]; 
  int ok=0,error=0;
  unsigned int a,b;
  int c;
  do {
    PrintTdc_MicroStatus_1(aTdc);
    printf("\n ===== MICRO CONTROLLER MENU =====");
    switch (menu(micro_control_menu_1)) 
      {	     
      case 1: /* Module Reset Register*/ 
	error=SetTdc_ModuleReset(aTdc);
	break;

      case 2: /* Acquisition Mode */
	  if (aTdc->Acquisition_Mode) printf("Acquisition Mode : Trigger Matching Mode");
	  else printf("Acquisition Mode : Continuous Storage Mode");
	  printf("\n Do you want to change ? (y/n)");
	  if (yes_no()) {
	    if (aTdc->Acquisition_Mode) error=SetTdc_AcqMode(aTdc,0);
	    else error=SetTdc_AcqMode(aTdc,1);
	    if (error) return error;
	  };
	break;

      case 3: /* Keep Token */
	  printf("\n Set Keep Token ? (y/n)");
	  error=SetTdc_KeepToken(aTdc,yes_no());if (error) return error;
	break;

      case 4: /* Set window */
	  printf("\nThe actual window width is %u ns",aTdc->Trigger_Config[0]);
	  printf("\n Set window width (<102376 ns) : ");
	  scanf("%ui",&a);
	  printf("\nThe actual window offset is %i ns",aTdc->Trigger_Config[1]);
	  printf("\n Set window margin (-51200 ns -> 1000 ns) : ");
	  scanf("%i",&c);
	  error=SetTdc_Window(aTdc,a,c);if (error) return error;
	break;

      case 5: /* Set margins */ 
	  printf("\nThe actual search margin is %i ns",aTdc->Trigger_Config[2]);
	  printf("\n Set extra search margin (recommended < 1250 ns) : ");
	  scanf("%ui",&a);	  
	  printf("\nThe actual reject margin is %i ns",aTdc->Trigger_Config[3]);
	  printf("\n Set reject margin (recommended=25 ns) : ");
	  scanf("%ui",&b);
	  error=SetTdc_Margin(aTdc,a,b);
	  if (error) return error; 
	break;

      case 6: /* Trigger substraction */
	  if (aTdc->Trigger_Config[4]) printf("Disable trigger substraction");
	  else printf("Enable trigger substraction");
	  printf("\n Do you want to change ? (y/n)");
	  if (yes_no()) {
	    if (aTdc->Trigger_Config[4]) error=SetTdc_TriggerSubstraction(aTdc,0);
	    else error=SetTdc_TriggerSubstraction(aTdc,1);
	    if (error) return error;
	  };
	break;

      case 7: /* Measurement type */
	  printf("\nThe actual measurment type is : %s",aTdc->Measurement);
	  printf("\n Set measurement type (O: leading edge and pulse width, 1: trailing edge,\n 2: leading edge, 3:leading and trailing edge) :");
	  scanf("%u",&a);
	  error=SetTdc_MeasurementConfig(aTdc,a);
	  if (error) return error;
	break;

      case 8: /* Resolution */
	  printf("\nThe actual edge resolution is %i ps", aTdc->Edge_Resolution);
	  printf("\n Set the edge resolution (100 ps, 200 ps... see manual) :");
	  scanf("%u",&a);
	  if (!(aTdc->Measurement_Config)) {
	    printf("\nThe actual pulse width resolution is %i ps", aTdc->Width_Resolution);
	    printf("\n Set the width resolution (100 ps, 200 ps... see manual):");
	    scanf("%u",&b);
	  }
	  else b=0;
	  error=SetTdc_MeasurementResolution(aTdc,a,b);
	  if (error) return error;
	break;

      case 9: /* Dead Time */
	  printf("\nThe actual channel dead time is : %i",aTdc->Channel_DeadTime);
	  printf("\n Set the channel dead time (5,10,30,100 ns) :");
	  scanf("%i",&c);
	  error=SetTdc_ChannelsDeadTime(aTdc,c);
	  if (error) return error;
	break;

      case 10: /* Maximum Hits */
	  printf("\nThe actual number of maximum number of hits per event is : %i",aTdc->Event_MaxHit);
	  printf("\n Set the maximum number of hits per event (0,1,2,4,8,16... see manual) :");
	  scanf("%i",&c);
	  error=SetTdc_EventMaxHit(aTdc,c);
	  if (error) return error;
	break;
	
      case 11: /* Advanced micro_control */
	  more_micro_control(aList);
	  PrintTdc_MicroStatus_2(aTdc);
	break;
	
      case 12:
	ok=1;
	break;
      }
  } while (!ok);
  
  return 0;
}

/*--------------------------------------------------------------*/
static int more_micro_control(tdcList *aList)
{
  tdc1190 *aTdc=&aList->listOfTdc[aList->currentTdc];
  int ok=0,error=0;
  unsigned int a,b;
  int c;
  do {
    PrintTdc_MicroStatus_2(aTdc);
    printf("\n ===== MORE MICRO CONTROLLER MENU =====");
    switch (menu(micro_control_menu_2)) 
      {
     case 1: /* Enable/disable TDC header and trailer */
	printf("\nThe current status for the TDC header and trailer is : " );
	if (aTdc->Event_HeaderTrailer) printf("enable");
	else printf("disable"); 
	printf("\n Do you want to change ? (y/n)");
	if (yes_no()) {
	  if (aTdc->Event_HeaderTrailer) error=SetTdc_EventHeaderTrailer(aTdc,0);
	  else error=SetTdc_EventHeaderTrailer(aTdc,1);
	}
  	break;  

      case 2: /* Enable/disbale TDC error mark*/
	printf("\nDo you want to enable TDC error mark ? (y/n)");
	if (yes_no()) error=SetTdc_ErrorMark(aTdc,1);
	else error=SetTdc_ErrorMark(aTdc,0);
	break;
	
      case 3: /* Enable/disable bypass if error */
	printf("\nDo you want to enable TDC bypass if error ? (y/n)");
	if (yes_no()) error=SetTdc_ErrorBypass(aTdc,1);
	else error=SetTdc_ErrorBypass(aTdc,0);
	break;

      case 4: /* Show/set TDC internal error type */ 
	/* TO BE DONE */
	break;

      case 5: /* Set FIFO Size */
	printf("\nThe actual FIFO size is : %u words", aTdc->FifoSize);
	printf("\n Set the new FIFO size : ");
	scanf("%i",&c);
	error=SetTdc_FifoSize(aTdc,c);
	break;
	
      case 6: /* Set global time offset */
	printf("\nThe global time offset is %i (coarse) %i (fine)",aTdc->TimeOffset[0],aTdc->TimeOffset[1]);
	printf("\n Set the global coarse time offset  (<2048) :");
	scanf("%u",&a);
	printf("\n Set the global fine time offset (<32) :");
	scanf("%u",&b);
	error=SetTdc_GlobalOffset(aTdc,a,b);
	break;

      case 7: /* Set channel time offset */
	printf("\nWhich channel do you want to change  (1-128) ? ");
	scanf("%i",&c);
	printf("\n The current offset of this channel is : %i", aTdc->Channel_Offset[c]);
	printf("\n Set a new offset (<128) :");
	scanf("%i",&a);
	error=SetTdc_ChannelNOffset(aTdc, c, a);
	break;

      case 8: /* Enable/disable all channels */
	printf("\nDo you want to enable all channels ? (y/n)");
	if (yes_no()) {
	  error=SetTdc_ChannelsActive(aTdc,1);
	}
	else {
	  printf("\nDo you want to disable all channels ? (y/n)");
	  if (yes_no()) {
	    error=SetTdc_ChannelsActive(aTdc,0);
	  }
	}
	break;

      case 9: /* Enable/disable one channel */
	printf("\nWhich channel do you want to enable/disable (1-128) ? ");
	scanf("%i",&c);
	if (aTdc->Channel_Active[c]) {
		printf("\n This channel is enable. Do you want to disable it ? (y/n)");
		if (yes_no()) error=SetTdc_ChannelNActive(aTdc,c,0);
		}
	else {
		printf("\n This channel is disable. Do you want to enable it ? (y/n)");
		if (yes_no()) error=SetTdc_ChannelNActive(aTdc,c,1);
		}
	
	break;
	
      case 10: /* Enable test mode */
	printf("\nEnter the LSB word (hex): ");
	scanf("%x",&a);
	printf("\nEnter the HSB word (hex): ");
	scanf("%x",&b);
	error=SetTdc_TestMode(aTdc,a,b,1);
	break;
	
      case 11: /* Disable test mode */
	error=SetTdc_TestMode(aTdc,0,0,0);
	break;
       
      case 12:
	ok=1;
	break;
      }
  } while (!ok);
  return 0;
}
/*--------------------------------------------------------------*/
static int registers(tdcList *aList)
{
  tdc1190 *aTdc=&aList->listOfTdc[aList->currentTdc];
  int ok=0,error=0;
  unsigned int a;
  do {
    PrintTdc_RegisterStatus(aTdc);    
    printf("\n ===== REGISTER MENU =====");
    switch (menu(register_menu)) 
      {
     case 1: /* Software Clear Register*/
	error=SetTdc_ClearRegister(aTdc);
	break;

	 case 2: /* Event Reset Register*/
	error=SetTdc_ClearEvent(aTdc);
	break;
	
	case 3: /* Show/Set control register */
	printf("\nThe control register status is 0x%x : bx%s", aTdc->CtrlReg, Set2Bit(aTdc->CtrlReg));
	printf("\n Do you want to change ? (y/n)");
	if (yes_no()) {
	  printf("Enter a new value (hex):");
	  scanf("%x",&a);
	  error=SetTdc_Control(aTdc,a);
	}	  
	break;	

	case 4: /* Set GEO address */
	printf("\nThe actual GEO address is : %u", aTdc->GeoAddress);
	printf("\n Set the new GEO address :");
	scanf("%u",&a);
	error=SetTdc_GEO_Address(aTdc,a);
	break;

	case 5: /* Set MCST base address */
	printf("\nThe actual MCST base address is : %u", aTdc->MCSTAddress);
	printf("\n Set the new MCST base address :");
	scanf("%u",&a);
	error=SetTdc_MCST_Address(aTdc,a);
	break;

	case 6: /*set MCST/CBLT control */
	printf("\nThe actual MCST/CBLT control is : %u", aTdc->MCSTControl);
	printf("\n Set the new MCST/CBLT control (0: disable, 1: Active last, 2: Active first, 3: Active intermediate) :");
	scanf("%u",&a);
	error=SetTdc_MCST_Control(aTdc,a);
	break;

	case 7: /* Set almost full register */
	printf("\nThe actual almost full register is : %u", aTdc->AlmostFull);
	printf("\n Enter a new value : ");
	scanf("%u",&a);
	error=SetTdc_AlmostFull(aTdc, a);
	break;
			
	case 8: /* Set Interrupt level */ 
	printf("\nThe actual Interrupt Level is : %u", aTdc->IrqLevel);
	printf("\n Set the new Interrupt Level :");
	scanf("%u",&a);
	error=SetTdc_InterruptLevel(aTdc,a);
	break;
	
	case 9: /* Set interrupt vector */
	printf("\nThe actual Interrupt Vector Register is : %u", aTdc->IrqVector);
	printf("\n Set the new Interrupt Vector :");
	scanf("%u",&a);
	error=SetTdc_InterruptVector(aTdc,a);	
	break;

	case 10: /* Set OUT_PROG control register */
	printf("\nThe actual OUT_PROG Control Register is : %u",aTdc->OutProg);
	printf("\n Set the new control (0: Data Ready, 1: Full, 2: Almost Full, 3: Error) :");
	scanf("%u",&a);
	error=SetTdc_OutProg_Control(aTdc,a);
	break;

	case 11: /* Show/Set Testreg register */
	printf("\nThe actual Testreg value is : %x",aTdc->TestReg);
	printf("\n Do you want to change ? (y/n)");
	if (yes_no()) {
	  printf("Enter a new value (hex):");
	  scanf("%x",&a);
	  error=SetTdc_TestReg(aTdc,a);
	}	
	break;
       
	case 12:
	ok=1;
	break;
      }
  } while (!ok);
  return 0;
}

/*--------------------------------------------------------------*/
static int debug_mode(tdcList *aList)
{
  tdc1190 *aTdc=&aList->listOfTdc[aList->currentTdc];
  long out[5];
  int ok=0, error=0;
  float average, sigma;
  long ref, time, dt, dtmin, dtmax; 
  int i=0, event=0, nbevent=0;
  
  do {
    printf("\n ===== DEBUG MODE MENU =====");
    switch (menu(debug_menu)) {
    case 1: /* Go to micro control menu*/     
      micro_control(aList);     
      break;
      
    case 2: /* Go to more micro control */
      more_micro_control(aList);
      break;
      
    case 3: /* Go to register */
      registers(aList);
      break;
      
    case 4: /* Software Clear Register*/
      error=SetTdc_ClearRegister(aTdc);
      break;
      
    case 5: /* print general status */
      PrintTdc_RegisterStatus(aTdc);
      break;	
      
    case 6: /* Send soft trigger */
      error=SetTdc_SoftTrigger(aTdc);
      break;
      
    case 7: /* Read output buffer*/
      do {
	error=read_MMAPoutput(&aTdc->tdc_access, out);
	print_output(out);
      } while ((out[0]!=24)&&(out[0]!=16)&&(!error));
      break;
      
    case 8: /* Read output buffer continuously*/
      SetTdc_ClearRegister(aTdc);
      do {
	error=read_MMAPoutput(&aTdc->tdc_access, out);
	if (out[0]!=24) print_output(out);
	if (out[0]==16) printf("\n");
      } while (!Keypressed());
      break;

    case 9: /* Time calibration output buffer continuously*/
      printf("Enter the number of event to read :");
      scanf("%i",&nbevent);
      i=0;
      dtmin=65534;
      dtmax=0;
      average=0;
      sigma=0;
      event=0;
      /* SetTdc_ClearRegister(aTdc);*/
      do {
	error=read_MMAPoutput(&aTdc->tdc_access, out);
	/*if (out[0]!=24) print_output(out);*/
	if (out[0]==8) {
	  event=1;
	}
	if ((out[0]==0)&&(event)) {
	  if (out[2]==95) {
	    ref=out[3];	    
	    /*printf("Ref=%lu\n",ref);*/
	  }
	  else {
	    time=out[3];
	    /*printf("Time=%lu\n",time);*/
	  }
	}
	if ((out[0]==16)&&(event)) {	  
	  dt=abs(time-ref);
	  if (dt<dtmin) dtmin=dt;
	  if (dt>dtmax) dtmax=dt;
	  average=average+(float)dt;
	  sigma=sigma+dt*dt;
	  /*printf("--> dt=%f\n",average);*/
	  event=0;
	  i+=1;
	}
      } while (i<nbevent);
      /* average/=nbevent;
	 sigma=sigma/nbevent-average*average;*/
      dtmax-=dtmin;
      printf("\n<dt>=%f ",average);
      printf("sigma=%f ",sigma);
      printf("max=%i\n",(int)dtmax);
      break;
      
    case 10:
      ok=1;
      break;
    }
  } while (!ok);
  
  return 0;
}

/*--------------------------------------------------------------*/
static int acquisition_mode(tdcList *aList)
{
  int ok=0;
  
  do {
    printf("\n ===== ACQUISITION MODE MENU =====");
    switch (menu(acquisition_menu)) 
      {
      case 1: /* Go to micro control menu*/
	micro_control(aList);
	break;
	
      case 2: /* Go to more micro control */
	more_micro_control(aList);
	break;
	
      case 3: /* Go to register */
	registers(aList);
	break;
	
      case 4: /* Start/stop acquisition_mode*/
	printf("\n Enter the average rate (Hz/10) : %i \n", aList->rate);
	scanf("%i",&aList->rate);
	break;

      case 5: /* Start/stop acquisition_mode*/
	    acquireLocal(aList);
	break;

      case 6: /* Start/stop acquisition_mode*/
	    acquireRemote(aList);
	break;

      case 7:
	ok=1;
	break;
      }
  } while(!ok);
  return 0;
}
/*--------------------------------------------------------------*/
int main(void)
{
  int init=0,ok=0;
  unsigned int address=0;

  acq.numberOfTdc=1;
  acq.currentTdc=0;
  acq.mode=0;
  acq.rate=64;

  do {
    printf("\n ===== MAIN MENU =====");
    switch (menu(main_menu)) 
      {
      case 1:
	printf("\nThere is %i TDC defined (max 4)",acq.numberOfTdc);
	printf("\nDo you want to add another TDC ? (y/n)");
	if (yes_no()) {
	  acq.numberOfTdc+=1;
	  acq.currentTdc=acq.numberOfTdc;
	  init=0;
	}
	else {
	  select_tdc(&acq);
	}
	break;
	
      case 2: /* Base Address */
	printf("\n--------- TDC %i ---------",acq.currentTdc);
	printf("\nEnter the base address (0: default <-> 0x1220000):");
	scanf("%x:",&address);
	if (address==0) address=0x1220000;
	InitTdc(&acq.listOfTdc[acq.currentTdc],address,128);
	init=1;
	break;
	
      case 4: /* micro_control */ 
	if (init)  micro_control(&acq);
	else printf("You must set up the base address first !!!\n");
	break;
	
      case 5: /* more_micro_control */ 
	if (init)  more_micro_control(&acq);
	else printf("You must set up the base address first !!!\n");
	break;
	
      case 6: /* registers */ 
	if (init)  registers(&acq);
	else printf("You must set up the base address first !!!\n");
	break;
	
      case 7: /*debug_mode*/
	if (init) debug_mode(&acq);
	else printf("You must set up the base address first !!!\n");
	break;
	
      case 8: /*acquisition mode*/
	if (init) acquisition_mode (&acq);
	else printf("You must set up the base address first !!!\n");
	break;

      case 9:
	ok=1;
	break;
      }
  } while (!ok);
  for (init=0;init<acq.numberOfTdc;init++) close_vme(&acq.listOfTdc[init].tdc_access);
  return 0;
}
