/**
  ******************************************************************************
  * @file          : app_tof.c
  * @author        : IMG SW Application Team
  * @brief         : This file provides code for the configuration
  *                  of the STMicroelectronics.X-CUBE-TOF1.3.2.0 instances.
  ******************************************************************************
  *
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "app_tof.h"
#include "main.h"
#include "sys_app.h"
#include <stdio.h>
#include "usart.h"

#if	(defined(STS_P2)||defined(STS_T6))
#include "VL53L1X_api.h"
#elif defined(L8)
#include "53l8a1_ranging_sensor.h"
#include "app_tof_pin_conf.h"
#elif defined(STS_R1D)||defined(STS_R1)||defined(STS_R5)||defined(STS_R2)
#include  "X-WL55_WLE5_53L0X.h"
#endif
#include "stm32wlxx_nucleo.h"

/* Private typedef -----------------------------------------------------------*/
/*
 * The application is to showcase the threshold detection
 * functionality of this device.
 *
 * When the device detects a target that match the configuration an IT is generated
 * and the host will start printing the measurement information on the serial connection (UART).
 *
 * Here is the default configuration:
 *
 * ITConfig.Criteria = RS_IT_IN_WINDOW;
 * ITConfig.LowThreshold = 200; // distance in mm
 * ITConfig.HighThreshold = 600; // distance in mm
 *
 * Other availables interrupt generation criteria for this device are:
 *
 * - RS_IT_DEFAULT          // IT if new measurement is ready (no thresholds)
 * - RS_IT_IN_WINDOW        // IT if distance > thresh_high
 * - RS_IT_OUT_OF_WINDOW    // IT if distance < LowThreshold OR distance > HighThreshold
 * - RS_IT_BELOW_LOW        // IT if distance <= LowThreshold
 * - RS_IT_ABOVE_HIGH       // IT if distance > HighThreshold
 * - RS_IT_EQUAL_LOW        // IT if distance == LowThreshold
 * - RS_IT_NOT_EQUAL_LOW    // IT if distance != LowThreshold
 */

/* Private define ------------------------------------------------------------*/
#define TIMING_BUDGET (30U) 	/* 16 ms < TimingBudget < 500 ms */
#define POLLING_PERIOD (250U) 	/* refresh rate for polling mode (milliseconds, shall be consistent with TimingBudget value) */

#ifdef L8
/* for VL53L8A1			*/
#define RANGING_FREQUENCY (10U) /* Ranging frequency Hz (shall be consistent with TimingBudget value) */
#define LOW_THRESHOLD  (200U)
#define HIGH_THRESHOLD (600U)

/* Private variables ---------------------------------------------------------*/
static RANGING_SENSOR_Capabilities_t Cap;
static RANGING_SENSOR_ProfileConfig_t Profile;
static RANGING_SENSOR_Result_t Result;

static void MX_53L8A1_ThresholdDetection_Init(void);
static void MX_53L8A1_ThresholdDetection_Process(void);
static void print_result(RANGING_SENSOR_Result_t *Result);

#endif
static int32_t status = 0;

volatile uint8_t ToF_EventDetected = 0;
uint16_t sensor_id=0;

/* Private function prototypes -----------------------------------------------*/
#if	(defined(STS_P2)||defined(STS_T6))
uint8_t sts_vl53lx_ranging(uint16_t *ranged_distance, uint8_t range_mode, uint16_t distance_threshold_mm,
		uint16_t inter_measurement_ms, uint16_t macro_timing,uint16_t roi_width, uint16_t sigma_mm, uint16_t signal_kcps);
#endif
uint8_t IsInterruptDetected(uint16_t dev);
#if	(defined(STS_P2)||defined(STS_T6))
void STS_TOF_VL53LX_Range_Process(uint8_t range_mode, uint16_t *range_distance);
#endif
void MX_TOF_Init(void)
{
  /* USER CODE BEGIN SV */

  /* USER CODE END SV */

  /* USER CODE BEGIN TOF_Init_PreTreatment */

  /* USER CODE END TOF_Init_PreTreatment */

  /* Initialize the peripherals and the TOF components */
  APP_LOG(TS_OFF,VLEVEL_L,"\r\n######################    MX TOF Init... \r\n");

  //MX_53L1A2_SimpleRanging_Init();
  //STS_TOF_VL53LX_PeopleCounting_Process();
#ifdef	L8
  MX_53L8A1_ThresholdDetection_Init();
  MX_53L8A1_ThresholdDetection_Process();
#elif	defined(STS_R1D)||defined(STS_R5)||defined(STS_R1)
  XWL55_WLE5_53L0X_Init();
#endif

  /* USER CODE BEGIN TOF_Init_PostTreatment */

  /* USER CODE END TOF_Init_PostTreatment */
}


/*
 * LM background task
 */
uint16_t MX_TOF_Ranging_Process(void)
{

#if	(defined(STS_P2)||defined(STS_T6))
	uint16_t range_distance=0;
	uint8_t range_mode = 2; //STS_TOF_LONG_RANGE;
	STS_TOF_VL53LX_Range_Process(range_mode, &range_distance);
	APP_LOG(TS_OFF, VLEVEL_M, "\n VL53L1 Range distance =%u mm \n\r", (uint16_t)range_distance);

	return (uint16_t) range_distance;
#elif defined(L8)
	uint16_t range_distance=0;
	uint8_t center_roi[4] = {27,28,35,36};
	uint8_t range_mode = 2; //STS_TOF_LONG_RANGE;
	int status = VL53L8A1_RANGING_SENSOR_GetDistance(VL53L8A1_DEV_CENTER, &Result);

	printf("\r|    27    |    28    |    35    |    36    |\r\n");

    for (uint8_t zone_nbr = 0; zone_nbr < 4; zone_nbr++)
    {
      /* Print distance and status */
        if (Result.ZoneResult[center_roi[zone_nbr]].NumberOfTargets > 0)
        {
          printf("|  %04ld  %2ld",
                 (long)Result.ZoneResult[center_roi[zone_nbr]].Distance[RANGING_SENSOR_NB_TARGET_PER_ZONE-1],
                 (long)Result.ZoneResult[center_roi[zone_nbr]].Status[RANGING_SENSOR_NB_TARGET_PER_ZONE-1]);
        }
        else {
        	printf("|    --    ");
        }

        range_distance += Result.ZoneResult[center_roi[zone_nbr]].Distance[RANGING_SENSOR_NB_TARGET_PER_ZONE-1];

    }
    //printf("\n\r");

    range_distance /=4;
	printf("| %u mm\r\n", (uint16_t)range_distance);

	return (uint16_t) range_distance;
#endif

}
void MX_TOF_Process(void)
{
  /* USER CODE BEGIN TOF_Process_PreTreatment */

  /* USER CODE END TOF_Process_PreTreatment */


	//APP_LOG(TS_OFF,VLEVEL_L,"\r\n#    MX TOF Process... #\r\n");
	//STS_TOF_VL53LX_Range_Process();
	//STS_TOF_VL53LX_PeopleCounting_Process();

	//sts_tof_vl53lx_peoplecount_subprocess();
#ifdef L8
	//MX_53L8A1_ThresholdDetection_Process();
	STS_TOF_L8_Process();
#endif

  /* USER CODE BEGIN TOF_Process_PostTreatment */

  /* USER CODE END TOF_Process_PostTreatment */
}
#ifdef L8
void STS_TOF_L8_Process(void)
{
	  //while (1)
	  {
	    /* interrupt mode */
	    if (ToF_EventDetected != 0)
	    {
	      ToF_EventDetected = 0;

	      status = VL53L8A1_RANGING_SENSOR_GetDistance(VL53L8A1_DEV_CENTER, &Result);

	      if (status == BSP_ERROR_NONE)
	      {
	        //print_result(&Result);
	      }
	    }
	  }
}

/*   VL53L8A1    */
static void MX_53L8A1_ThresholdDetection_Init(void)
{
  /* Initialize Virtual COM Port */
  //BSP_COM_Init(COM1);
  MX_USART2_UART_Init();

  /* Sensor reset */
  HAL_GPIO_WritePin(VL53L8A1_PWR_EN_C_PORT, VL53L8A1_PWR_EN_C_PIN, GPIO_PIN_RESET);
  HAL_Delay(2);
  HAL_GPIO_WritePin(VL53L8A1_PWR_EN_C_PORT, VL53L8A1_PWR_EN_C_PIN, GPIO_PIN_SET);
  HAL_Delay(2);

  HAL_GPIO_WritePin(VL53L8A1_LPn_C_PORT, VL53L8A1_LPn_C_PIN, GPIO_PIN_RESET);
  HAL_Delay(2);
  HAL_GPIO_WritePin(VL53L8A1_LPn_C_PORT, VL53L8A1_LPn_C_PIN, GPIO_PIN_SET);
  HAL_Delay(2);


  status = VL53L8A1_RANGING_SENSOR_Init(VL53L8A1_DEV_CENTER);

  if (status != BSP_ERROR_NONE)
  {
    printf("VL53L8A1_RANGING_SENSOR_Init failed\n");
    //while (1);
  }
  printf("\r\nVL53L8A1_RANGING_SENSOR_Init Success\r\n");
}

static void MX_53L8A1_ThresholdDetection_Process(void)
{
  uint32_t Id;

  RANGING_SENSOR_ITConfig_t ITConfig;

  VL53L8A1_RANGING_SENSOR_ReadID(VL53L8A1_DEV_CENTER, &Id);
  VL53L8A1_RANGING_SENSOR_GetCapabilities(VL53L8A1_DEV_CENTER, &Cap);

  Profile.RangingProfile = RS_PROFILE_8x8_AUTONOMOUS;
  Profile.TimingBudget = TIMING_BUDGET;
  Profile.Frequency = RANGING_FREQUENCY; /* Ranging frequency Hz (shall be consistent with TimingBudget value) */
  Profile.EnableAmbient = 0; /* Enable: 1, Disable: 0 */
  Profile.EnableSignal = 0; /* Enable: 1, Disable: 0 */

  /* set the profile if different from default one */
  VL53L8A1_RANGING_SENSOR_ConfigProfile(VL53L8A1_DEV_CENTER, &Profile);

  /* threshold parameters */
  ITConfig.Criteria = RS_IT_IN_WINDOW;
  ITConfig.LowThreshold = LOW_THRESHOLD; /* mm */
  ITConfig.HighThreshold = HIGH_THRESHOLD; /* mm */

  VL53L8A1_RANGING_SENSOR_ConfigIT(VL53L8A1_DEV_CENTER, &ITConfig);

  status = VL53L8A1_RANGING_SENSOR_Start(VL53L8A1_DEV_CENTER, RS_MODE_ASYNC_CONTINUOUS);

  if (status != BSP_ERROR_NONE)
  {
    printf("VL53L8A1_RANGING_SENSOR_Start failed\n");
    while (1);
  }

  printf("\033[2H\033[2J");
  printf("53L8A1 Threshold Detection demo application\n\r");
  printf("-------------------------------------------\n\r");
  printf("please put a target between %d and %d millimeters from the sensor\n\r",
		  LOW_THRESHOLD, HIGH_THRESHOLD);
#if 0
  //while (1)
  {
    /* interrupt mode */
    if (ToF_EventDetected != 0)
    {
      ToF_EventDetected = 0;

      status = VL53L8A1_RANGING_SENSOR_GetDistance(VL53L8A1_DEV_CENTER, &Result);

      if (status == BSP_ERROR_NONE)
      {
        print_result(&Result);
      }
    }
  }
#endif
}

static void print_result(RANGING_SENSOR_Result_t *Result)
{
  int8_t i;
  int8_t j;
  int8_t k;
  int8_t l;
  uint8_t zones_per_line;

  zones_per_line = ((Profile.RangingProfile == RS_PROFILE_8x8_AUTONOMOUS) ||
                    (Profile.RangingProfile == RS_PROFILE_8x8_CONTINUOUS)) ? 8 : 4;

  printf("%c[2H", 27); /* clear screen */
  printf("53L8A1 Threshold Detection demo application\n\r");
  printf("-------------------------------------------\n\n\r");

  printf("Cell Format :\n\n\r");
  for (l = 0; l < RANGING_SENSOR_NB_TARGET_PER_ZONE; l++)
  {
    printf(" \033[38;5;10m%20s\033[0m : %20s\n", "Distance [mm]", "Status\r");
    if ((Profile.EnableAmbient != 0) || (Profile.EnableSignal != 0))
    {
      printf(" %20s : %20s\n", "Signal [kcps/spad]", "Ambient [kcps/spad]\r");
    }
  }

  printf("\n\n");

  for (j = 0; j < Result->NumberOfZones; j += zones_per_line)
  {
    for (i = 0; i < zones_per_line; i++) /* number of zones per line */
    {
      printf(" -----------------");
    }
    printf("\n");

    for (i = 0; i < zones_per_line; i++)
    {
    	printf("|                 ");
    }
    printf("|\n");

    for (l = 0; l < RANGING_SENSOR_NB_TARGET_PER_ZONE; l++)
    {
      /* Print distance and status */
      for (k = (zones_per_line - 1); k >= 0; k--)
      {
        if (Result->ZoneResult[j + k].NumberOfTargets > 0)
          printf("| \033[38;5;10m%5ld\033[0m  :%2d%5ld ",
                 (long)Result->ZoneResult[j + k].Distance[l],(j+k),
                 (long)Result->ZoneResult[j + k].Status[l]);
        else
          printf("| %5s  :  %5s ", "X", "X");
      }
      printf("|\n");

      if ((Profile.EnableAmbient != 0) || (Profile.EnableSignal != 0))
      {
        /* Print Signal and Ambient */
        for (k = (zones_per_line - 1); k >= 0; k--)
        {
          if (Result->ZoneResult[j + k].NumberOfTargets > 0)
          {
            if (Profile.EnableSignal != 0)
            {
              printf("| %5ld  :  ", (long)Result->ZoneResult[j + k].Signal[l]);
            }
            else
              printf("| %5s  :  ", "X");

            if (Profile.EnableAmbient != 0)
            {
              printf("%5ld ", (long)Result->ZoneResult[j + k].Ambient[l]);
            }
            else
              printf("%5s ", "X");
          }
          else
            printf("| %5s  :  %5s ", "X", "X");
        }
        printf("|\n");
      }
    }
  }

  for (i = 0; i < zones_per_line; i++)
  {
    printf(" -----------------");
  }
  printf("\n");
}



#endif

#if	defined(STS_P2)||defined(STS_T6)
uint8_t IsInterruptDetected(uint16_t dev)
{
	// To be filled with customer HW. This function should
	// return 1 when an interrupt is raised by the ToF on GPIO1 pin (pin7)
	if (ToF_EventDetected )
	{
		APP_LOG(TS_OFF, VLEVEL_L,"###############   TOF EVENT DETECTED \r\n");
		ToF_EventDetected =0;
		return 1;
	} else {
		return 0;
	}
}
#endif

#if	(defined(STS_P2)||defined(STS_T6))
void STS_TOF_VL53LX_Range_Process(uint8_t range_mode, uint16_t *range_distance)
{
	//uint8_t vl53lx_model = STS_TOF_VL53L1X;
	//uint8_t range_mode = STS_TOF_SHORT_RANGE;
	uint16_t	i_distance_measured = 0;
	uint16_t	i_distance_threshold_mm = 800;
	uint16_t	i_inter_measurement_ms=100, i_macro_timing=33;
	uint16_t	i_roi_width=16, i_sigma_mm=30, i_signal_kcps=2000;

	switch (range_mode)
	{
		case STS_TOF_SHORT_RANGE:
			// STS ---002  for short distance
			/* Example for robust and short distance measurements. Max distance reduced
				 * but very low number of false-positives */
				 //status |= VL53L1X_ULP_SetSigmaThreshold(dev, 30);
				//status |= VL53L1X_ULP_SetSignalThreshold(dev, 2000);
			i_sigma_mm = 30;		//increase this for longer distance, reduce for shorter distance
			i_signal_kcps = 2000;		// 1000-  6000 kcps
			i_distance_threshold_mm = 900;
			i_inter_measurement_ms = 100;		// 100 - 1000 ms
			i_macro_timing = 100;
			//i_roi_width = 16;

		break;

		case STS_TOF_LONG_RANGE:
			// STS --- 003 for long range
			/* Relax some limits. Be careful, it can create false-positives !*/
			//status |= VL53L1X_ULP_SetSigmaThreshold(dev, 60);
			//status |= VL53L1X_ULP_SetSignalThreshold(dev, 1200);
			i_sigma_mm = 85;		// increase this for longer distance, reduce for short distance
			i_signal_kcps = 1000;		// 1000-  6000 kcps
			i_distance_threshold_mm = 4000;	//4000;
			i_inter_measurement_ms = 200;			// 100 - 1000 ms
			i_macro_timing = 30;				// 1 - 100 ms
			i_roi_width = 8;


		break;

		case STS_TOF_LOW_POWER_RANGE:
			// STS---001   for ultra low power
			/* Reduce the macro timing to minimum. This is equivalent as reducing the integration time */
			//status = VL53L1X_ULP_SetMacroTiming(dev, 1);
			i_distance_threshold_mm = 4000;
			i_inter_measurement_ms = 100;			// 100 - 1000 ms
			i_macro_timing = 1;
			/* Reduce at maximum the SPADS */
			//status = VL53L1X_ULP_SetROI(dev, 4);
			i_roi_width = 4;

		break;
		default:
		break;
	}

	sts_vl53lx_ranging(&i_distance_measured, range_mode, i_distance_threshold_mm, i_inter_measurement_ms,i_macro_timing, i_roi_width, i_sigma_mm, i_signal_kcps);
	APP_LOG(TS_OFF, VLEVEL_M, "I_DISTANCE_MEASURED = %d \n", i_distance_measured);
	*range_distance = i_distance_measured;

}

void BSP_PB_Callback(Button_TypeDef Button)
{
  //PushButtonDetected = 1;
}


uint8_t sts_vl53lx_ranging(uint16_t *ranged_distance, uint8_t range_mode, uint16_t distance_threshold_mm, uint16_t inter_measurement_ms, uint16_t macro_timing,
uint16_t roi_width, uint16_t sigma_mm, uint16_t signal_kcps)
{
	  uint8_t status=0, dev=0x52;
	  uint16_t estimated_distance_mm=0;
	  status = VL53L1X_SensorInit(dev);
	  status += VL53L1X_SetDistanceMode(dev, 2);  /* 1=short, 2=long, DISTANCE_MODE */
	  status += VL53L1X_SetTimingBudgetInMs(dev, 100);  /* TIMING_BUDGET, in ms possible values [15, 20, 50, 100, 200, 500] */
	  status += VL53L1X_SetInterMeasurementInMs(dev, 50);
	  status += VL53L1X_SetROI(dev, 8, 8); /* minimum ROI 4,4 */
	  if (status != 0) {
		  APP_LOG(TS_OFF, VLEVEL_L,"Initialization or configuration of the device\n");
	    return (-1);
	  }
	  status = VL53L1X_StartRanging(dev);   /* This function has to be called to enable the ranging */
	  if (status != 0) {
	  		  APP_LOG(TS_OFF, VLEVEL_L,"Start Range failed\n");
	  	    return (-1);
	  }

		status = VL53L1X_GetDistance(dev, &estimated_distance_mm);
		APP_LOG(TS_OFF,VLEVEL_M,"Target detected! Distance =%d mm \r\n", estimated_distance_mm );
		*ranged_distance = estimated_distance_mm;
		status = VL53L1X_StopRanging(dev);
		APP_LOG(TS_OFF,VLEVEL_M,"End of VL53L1X Ranging Process\n");
		return status;

#if 0
	/*********************************/
	/*   VL53L1X ranging variables  */
	/*********************************/
	uint8_t 				status, loop;
	uint8_t 				dev;
	//uint16_t 				sensor_id=0;
	uint8_t 				measurement_status;
	uint16_t 				estimated_distance_mm, r_signal_kcps, r_sigma_mm, r_ambient_kcps;
	/*********************************/
	/*      Customer platform        */
	/*********************************/
	/* Default VL53L1X Ultra Low Power I2C address */
	dev = 0x52;

	/* (Optional) Change I2C address */
	// status = VL53L1X_ULP_SetI2CAddress(dev, 0x52);
	// dev = 0x20;

	/*********************************/
	/*   Power on sensor and init    */
	/*********************************/
	APP_LOG(TS_OFF,VLEVEL_L,"Range Mode =%d \r\n",range_mode);
	/* (Optional) Check if there is a VL53L1X sensor connected */
	status = VL53L1X_GetSensorId(dev, &sensor_id);
	APP_LOG(TS_OFF,VLEVEL_L,"VL53L1X address =%X\r\n",sensor_id );
	if(status || (sensor_id != 0xEACC))
	{
		APP_LOG(TS_OFF,VLEVEL_L,"VL53L1X not detected at requested address\n");
		return status;
	}
	/* (Mandatory) Init VL53L1X sensor */
	status = VL53L1X_SensorInit(dev);
	if(status)
	{
		APP_LOG(TS_OFF,VLEVEL_L,"VL53L1X ultra low power Loading failed\n");
		//HAL_Delay(100);
		return status;
	}
	APP_LOG(TS_OFF,VLEVEL_L,"VL53L1X ultra low power ready ! \r\n");
	/*********************************/
	/*     Sensor configuration      */
	/*********************************/
	/* (Optional) Program sensor to raise an interrupt ONLY below 300mm */
	//status = VL53L1X_SetInterruptConfiguration(dev, distance_threshold_mm, 1);  //i_distance_threshold_mm
	status = VL53L1X_SetDistanceThreshold(dev, distance_threshold_mm,distance_threshold_mm, 0,1);  //i_distance_threshold_mm
	/* (Optional) Program a 10Hz ranging frequency */
	status = VL53L1X_SetInterMeasurementInMs(dev, inter_measurement_ms);			// range_interval_ms
	/* Increase the macro timing. This is equivalent as increasing the integration time */
	//status = VL53L1X_SetMacroTiming(dev, macro_timing);				// micro_timing_ms
	status = VL53L1X_SetTimingBudgetInMs(dev, macro_timing);
	/* Enable all the SPADS */
	status = VL53L1X_SetROI(dev, roi_width, roi_width);						// SPADS { 1 -- 16 }

	if(range_mode != STS_TOF_LOW_POWER_RANGE)
	{
		/* Example for robust and short distance measurements. Max distance reduced
		 * but very low number of false-positives */
		status |= VL53L1X_SetSigmaThreshold(dev, sigma_mm);
		status |= VL53L1X_SetSignalThreshold(dev, signal_kcps);
	}

	/*********************************/
	/*         Ranging loop          */
	/*********************************/
	status = VL53L1X_StartRanging(dev);
	if(status)
	{
		APP_LOG(TS_OFF,VLEVEL_L,"VL53L1X_ULP_StartRanging failed with status %u\n", status);
		return status;
	}
	APP_LOG(TS_OFF,VLEVEL_L,"Ranging started. Put your hand close to the sensor to generate an interrupt...\n");
	loop = 0;
	while(loop < 20)
	{
		/* Use this external function to detect when a hardware interrupt is generated on PIN 7 (GPIO1). It means that a new measurement is ready.
		 */

		if(IsInterruptDetected(dev))
		{
			/* (Mandatory) Clear HW interrupt to restart measurements */
			VL53L1X_ClearInterrupt(dev);
			/* Dump debug data */
#if 0
			status = VL53L1X_DumpDebugData(dev, &measurement_status,
						&estimated_distance_mm, &r_sigma_mm, &r_signal_kcps, &r_ambient_kcps);
			*ranged_distance = estimated_distance_mm;
			APP_LOG(TS_OFF,VLEVEL_L,"Target detected! Interrupt raised by sensor, Distance =%d mm \r\n", estimated_distance_mm );
#endif
			status = VL53L1X_GetDistance(dev, &estimated_distance_mm);
			APP_LOG(TS_OFF,VLEVEL_L,"Target detected! Interrupt raised by sensor, Distance =%d mm \r\n", estimated_distance_mm );
			loop++;
		}
	}

	status = VL53L1X_StopRanging(dev);
	APP_LOG(TS_OFF,VLEVEL_L,"End of VL53L1X ultra low power demo\n");
	return status;
#endif
}
#endif

#ifdef __cplusplus
}
#endif
