/**
 * adc.c
 *
 * Initialization and communication for the Analog to Digital Converter (ADC) hardware.
 * 
 * Copyright (c) 2022 Bigscreen, Inc.
 */

#include <asf.h>
#include "adc.h"
#include "displayboard.h"
#include "temp_sense.h"

void adc_end_conversion(void);

// callback function to clear interrupt by reading ADC data
void usbcpd_update_adc_values(uint16_t val1, uint16_t val2);

void init_adc(void)
{
	struct adc_config adc_cfg;
	pmc_enable_periph_clk(ID_ADC);
	
	adc_select_clock_source_mck(ADC);

	adc_get_config_defaults(&adc_cfg);

	adc_init(ADC, &adc_cfg);
	
	// Prescaler set to max makes the temp sense channel settle down.
	// For some reason, if it's not maxed out the resulting ADC conversion is super jumpy
	ADC->ADC_MR &= ~(ADC_MR_PRESCAL_Msk);
	ADC->ADC_MR |= ADC_MR_PRESCAL(0xFF);
	
	// TODO: check if all three channels are converted
	// with a single trigger
	adc_channel_enable(ADC, USBC_ADC_CC1_CH);
	adc_channel_enable(ADC, USBC_ADC_CC2_CH);
	adc_channel_enable(ADC, TEMP_SENSE_ADC_CH);

	adc_set_trigger(ADC, ADC_TRIG_SW);

	adc_set_callback(ADC, ADC_INTERRUPT_EOC_3,
	adc_end_conversion, 1);

	adc_set_resolution(ADC, ADC_12_BITS);
	adc_start_calibration(ADC);
	
	NVIC_EnableIRQ(ADC_IRQn);
	NVIC_SetPriority(ADC_IRQn, ADC_INTERRUPT_PRIO);
}

void trig_adc(void)
{
	adc_start_software_conversion(ADC);
}

void adc_end_conversion(void)
{
	// Retrieve results of ADC conversions
	temp_sense_update_adc_value(ADC->ADC_CDR[TEMP_SENSE_ADC_CH]);
	usbcpd_update_adc_values(ADC->ADC_CDR[USBC_ADC_CC1_CH], ADC->ADC_CDR[USBC_ADC_CC2_CH]);
}

uint16_t cc1val = UINT16_MAX;
uint16_t cc2val = UINT16_MAX;
void usbcpd_update_adc_values(uint16_t val1, uint16_t val2)
{
	cc1val = val1;
	cc2val = val2;
}

uint16_t get_cc1_val()
{
	return cc1val;
}

uint16_t get_cc2_val()
{
	return cc2val;
}

USB_Flip_State_t get_flip_status()
{
	USB_Flip_State_t retval = Flip_Undefined;

#if 0
	// Unitialized? Check if cc1 or cc2 is still at the UINT16_MAX
	if((cc1val == UINT16_MAX) || (cc2val == UINT16_MAX))
	{
		retval = Flip_Undefined;
	}
	// CC1 active, CC2 low - cable is inserted "unflipped", with CC = CC1
	else if((CC_ACTIVE_THRESH_LOW <= cc1val) && (cc1val <= CC_ACTIVE_THRESH_HIGH) && (cc2val <= CC_INACTIVE_THRESH))
	{
		retval = Flip_CC1;
	}
	// CC2 active, CC1 low - cable is inserted "flipped", with CC = CC2
	else if((CC_ACTIVE_THRESH_LOW <= cc2val) && (cc2val <= CC_ACTIVE_THRESH_HIGH) && (cc1val <= CC_INACTIVE_THRESH))
	{
		retval = Flip_CC2;
	}
	// If the CC1 or CC2 value is above the "wrong way" threshhold, then the junction box cable end is inserted the wrong way
	else if((cc1val >= CC_WRONG_WAY_THRESH) && (cc2val <= CC_INACTIVE_THRESH))
	{
		retval = Flip_CC1_JcnBoxWrongWay;
	}
	else if((cc2val >= CC_WRONG_WAY_THRESH) && (cc1val <= CC_INACTIVE_THRESH))
	{
		retval = Flip_CC2_JcnBoxWrongWay;
	}	
#endif

	if((cc1val >= CC_WRONG_WAY_THRESH) && (cc2val <= CC_INACTIVE_THRESH))
	{
		retval = Flip_CC1_JcnBoxWrongWay;
	}
	else if((cc2val >= CC_WRONG_WAY_THRESH) && (cc1val <= CC_INACTIVE_THRESH))
	{
		retval = Flip_CC2_JcnBoxWrongWay;
	}	
	else if(cc1val > cc2val) {
		retval = Flip_CC1;
	} else {
		retval = Flip_CC2;
	}
	return retval;
}
