/**

MIT-like-non-ai-license

Copyright (c) 2024 Charles Lohr "CNLohr"

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the two following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

In addition the following restrictions apply:

1. The Software and any modifications made to it may not be used for the
purpose of training or improving machine learning algorithms, including but not
limited to artificial intelligence, natural language processing, or data
mining. This condition applies to any derivatives, modifications, or updates
based on the Software code. Any usage of the Software in an AI-training dataset
is considered a breach of this License.

2. The Software may not be included in any dataset used for training or
improving machine learning algorithms, including but not limited to artificial
intelligence, natural language processing, or data mining.

3. Any person or organization found to be in violation of these restrictions
will be subject to legal action and may be held liable for any damages
resulting from such use.

If any term is unenforcable, other terms remain in-force.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "hal/gpio_types.h"
#include "esp_log.h"
#include "soc/efuse_reg.h"
#include "soc/soc.h"
#include "soc/system_reg.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/gpio_sig_map.h"


#include "soc/io_mux_reg.h"
#include "soc/dedic_gpio_reg.h"

// Funtenna will output on GPIO1 and 14.  Otherwise it will be 17 and 18
//#define FUNTENNA

int global_i = 100;


static inline uint32_t getCycleCount()
{
    uint32_t ccount;
    asm volatile("rsr %0,ccount":"=a" (ccount));
    return ccount;
}


#define IO_MUX_REG(x) XIO_MUX_REG(x)
#define XIO_MUX_REG(x) IO_MUX_GPIO##x##_REG

#define GPIO_NUM(x) XGPIO_NUM(x)
#define XGPIO_NUM(x) GPIO_NUM_##x


#ifdef FUNTENNA

#define RF1_PIN 1
#define RF2_PIN 14
#else
#define RF1_PIN 17
#define RF2_PIN 18
#endif



#include "hal/gpio_types.h"
#include "driver/gpio.h"
#include "rom/gpio.h"
#include "soc/i2s_reg.h"
#include "soc/periph_defs.h"
#include "rom/lldesc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "soc/regi2c_apll.h"
//#include "components/hal/esp32s2/include/hal/regi2c_ctrl_ll.h
#include "hal/regi2c_ctrl_ll.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/regi2c_ctrl.h"
#include "hal/clk_tree_ll.h"

#include "siggen.h"




#define I2C_RTC_WIFI_CLK_EN (SYSCON_WIFI_CLK_EN_REG)

#define I2C_RTC_CLK_GATE_EN    (BIT(18))
#define I2C_RTC_CLK_GATE_EN_M  (BIT(18))
#define I2C_RTC_CLK_GATE_EN_V  0x1
#define I2C_RTC_CLK_GATE_EN_S  18

#define I2C_RTC_CONFIG0  0x6000e048

#define I2C_RTC_MAGIC_CTRL 0x00001FFF
#define I2C_RTC_MAGIC_CTRL_M  ((I2C_RTC_MAGIC_CTRL_V)<<(I2C_RTC_MAGIC_CTRL_S))
#define I2C_RTC_MAGIC_CTRL_V  0x1FFF
#define I2C_RTC_MAGIC_CTRL_S  4

#define I2C_RTC_CONFIG1  0x6000e044

#define I2C_RTC_BOD_MASK (BIT(22))
#define I2C_RTC_BOD_MASK_M  (BIT(22))
#define I2C_RTC_BOD_MASK_V  0x1
#define I2C_RTC_BOD_MASK_S  22

#define I2C_RTC_SAR_MASK (BIT(18))
#define I2C_RTC_SAR_MASK_M  (BIT(18))
#define I2C_RTC_SAR_MASK_V  0x1
#define I2C_RTC_SAR_MASK_S  18

#define I2C_RTC_BBPLL_MASK (BIT(17))
#define I2C_RTC_BBPLL_MASK_M  (BIT(17))
#define I2C_RTC_BBPLL_MASK_V  0x1
#define I2C_RTC_BBPLL_MASK_S  17

#define I2C_RTC_APLL_MASK (BIT(14))
#define I2C_RTC_APLL_MASK_M  (BIT(14))
#define I2C_RTC_APLL_MASK_V  0x1
#define I2C_RTC_APLL_MASK_S  14

#define I2C_RTC_ALL_MASK 0x00007FFF
#define I2C_RTC_ALL_MASK_M  ((I2C_RTC_ALL_MASK_V)<<(I2C_RTC_ALL_MASK_S))
#define I2C_RTC_ALL_MASK_V  0x7FFF
#define I2C_RTC_ALL_MASK_S  8

#define I2C_RTC_CONFIG2  0x6000e000

#define I2C_RTC_BUSY (BIT(25))
#define I2C_RTC_BUSY_M  (BIT(25))
#define I2C_RTC_BUSY_V  0x1
#define I2C_RTC_BUSY_S  25

#define I2C_RTC_WR_CNTL (BIT(24))
#define I2C_RTC_WR_CNTL_M  (BIT(24))
#define I2C_RTC_WR_CNTL_V  0x1
#define I2C_RTC_WR_CNTL_S  24

#define I2C_RTC_DATA 0x000000FF
#define I2C_RTC_DATA_M  ((I2C_RTC_DATA_V)<<(I2C_RTC_DATA_S))
#define I2C_RTC_DATA_V  0xFF
#define I2C_RTC_DATA_S  16

#define I2C_RTC_ADDR 0x000000FF
#define I2C_RTC_ADDR_M  ((I2C_RTC_ADDR_V)<<(I2C_RTC_ADDR_S))
#define I2C_RTC_ADDR_V  0xFF
#define I2C_RTC_ADDR_S  8

#define I2C_RTC_SLAVE_ID 0x000000FF
#define I2C_RTC_SLAVE_ID_M  ((I2C_RTC_SLAVE_ID_V)<<(I2C_RTC_SLAVE_ID_S))
#define I2C_RTC_SLAVE_ID_V  0xFF
#define I2C_RTC_SLAVE_ID_S  0

#define I2C_RTC_MAGIC_DEFAULT (0x1c40)

#define I2C_BOD     0x61
#define I2C_BBPLL   0x66
#define I2C_SAR_ADC 0X69
#define I2C_APLL    0X6D


int lastend = 0;

// Configures APLL = 480 / 4 = 120
// 40 * (SDM2 + SDM1/(2^8) + SDM0/(2^16) + 4) / ( 2 * (ODIV+2) );
// Datasheet recommends that numerator does not exceed 500MHz.
void IRAM_ATTR local_rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2, uint32_t o_div)
{
	REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD, enable ? 0 : 1);
	REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU, enable ? 1 : 0);

	if (enable) {
		REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_DSDM2, sdm2);
		REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_DSDM0, sdm0);
		REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_DSDM1, sdm1);
		REGI2C_WRITE(I2C_APLL, I2C_APLL_SDM_STOP, CLK_LL_APLL_SDM_STOP_VAL_1);
		REGI2C_WRITE(I2C_APLL, I2C_APLL_SDM_STOP, CLK_LL_APLL_SDM_STOP_VAL_2_REV1);
		REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_OR_OUTPUT_DIV, o_div );
	}


	// Settings determined experimentally.
	REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_OC_TSCHGP, 0 ); // 0 or 1
	REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_EN_FAST_CAL, 1 ); // 0 or 1
	REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_OC_DHREF_SEL, 0 ); // 0..3
	REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_OC_DLREF_SEL, 0 ); // 0..3
	REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_SDM_DITHER, 1 ); // 0 or 1
	REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_OC_DVDD, 25 ); // 0 .. 31

}


void IRAM_ATTR regi2c_write_reg_raw_local(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data)
{
    uint32_t temp = ((block & I2C_RTC_SLAVE_ID_V) << I2C_RTC_SLAVE_ID_S)
                    | ((reg_add & I2C_RTC_ADDR_V) << I2C_RTC_ADDR_S)
                    | ((0x1 & I2C_RTC_WR_CNTL_V) << I2C_RTC_WR_CNTL_S)
                    | (((uint32_t)data & I2C_RTC_DATA_V) << I2C_RTC_DATA_S);
    while (REG_GET_BIT(I2C_RTC_CONFIG2, I2C_RTC_BUSY));
    REG_WRITE(I2C_RTC_CONFIG2, temp);
}


void apll_quick_update( uint32_t sdm )
{
	uint8_t sdm2 = sdm>>16;
	uint8_t sdm1 = (sdm>>8)&0xff;
	uint8_t sdm0 = (sdm>>0)&0xff;
	static int last_sdm_0 = -1;
	static int last_sdm_1 = -1;
	static int last_sdm_2 = -1;

	if( sdm2 != last_sdm_2 )
		regi2c_write_reg_raw_local(I2C_APLL, I2C_APLL_HOSTID, I2C_APLL_DSDM2, sdm2);
	if( sdm0 != last_sdm_0 ) 
		regi2c_write_reg_raw_local(I2C_APLL, I2C_APLL_HOSTID, I2C_APLL_DSDM0, sdm0);
	if( sdm1 != last_sdm_1 )
		regi2c_write_reg_raw_local(I2C_APLL, I2C_APLL_HOSTID, I2C_APLL_DSDM1, sdm1);

	last_sdm_2 = sdm2;
	last_sdm_1 = sdm1;
	last_sdm_0 = sdm0;
}



void sandbox_main()
{
	printf( "sandbox_main()\n" );



	DPORT_SET_PERI_REG_MASK( DPORT_CPU_PERI_CLK_EN_REG, DPORT_CLK_EN_DEDICATED_GPIO );
	DPORT_CLEAR_PERI_REG_MASK( DPORT_CPU_PERI_RST_EN_REG, DPORT_RST_EN_DEDICATED_GPIO);

	// Setup GPIO39 to be the PRO ALONE output (For LEDs)
	REG_WRITE( GPIO_OUT1_W1TC_REG, 1<<(39-32) );
	REG_WRITE( GPIO_ENABLE1_W1TS_REG, 1<<(39-32) );
	REG_WRITE( IO_MUX_GPIO39_REG, 2<<FUN_DRV_S );
	REG_WRITE( GPIO_FUNC39_OUT_SEL_CFG_REG, PRO_ALONEGPIO_OUT0_IDX );
	REG_WRITE( DEDIC_GPIO_OUT_CPU_REG, 0x01 ); // Enable CPU instruction output


	// Output clock on P2.

	// Maximize the drive strength.
	gpio_set_drive_capability( GPIO_NUM(RF1_PIN), GPIO_DRIVE_CAP_3 );
	gpio_set_drive_capability( GPIO_NUM(RF2_PIN), GPIO_DRIVE_CAP_3 );

	// Use the IO matrix to create the inverse of TX on pin 17.
	gpio_matrix_out( GPIO_NUM(RF1_PIN), CLK_I2S_MUX_IDX, 1, 0 );
	gpio_matrix_out( GPIO_NUM(RF2_PIN), CLK_I2S_MUX_IDX, 0, 0 );

	periph_module_enable(PERIPH_I2S0_MODULE);

	int use_apll = 1;

	// Start with a default tone.
	int sdm0 = 100;
	int sdm1 = 230;
	int sdm2 = 8;
	int odiv = 0;

	local_rtc_clk_apll_enable( use_apll, sdm0, sdm1, sdm2, odiv );

	if( use_apll )
	{
		WRITE_PERI_REG( I2S_CLKM_CONF_REG(0), (1<<I2S_CLK_SEL_S) | (1<<I2S_CLK_EN_S) | (0<<I2S_CLKM_DIV_A_S) | (0<<I2S_CLKM_DIV_B_S) | (1<<I2S_CLKM_DIV_NUM_S) );
	}
	else
	{
		// fI2S = fCLK / ( N + B/A )
		// DIV_NUM = N
		// Note I2S_CLKM_DIV_NUM minimum = 2 by datasheet.  Less than that and it will ignoreeee you.
		WRITE_PERI_REG( I2S_CLKM_CONF_REG(0), (2<<I2S_CLK_SEL_S) | (1<<I2S_CLK_EN_S) | (0<<I2S_CLKM_DIV_A_S) | (0<<I2S_CLKM_DIV_B_S) | (1<<I2S_CLKM_DIV_NUM_S) );  // Minimum reduction, 2:1
	}

	SigSetupTest(  );

	lastend = getCycleCount();
}



#define DisableISR()            do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0)
#define EnableISR()             do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0)


uint32_t frame = 0;
void sandbox_tick()
{
	//uprintf( "%08x\n", REGI2C_READ(I2C_APLL, I2C_APLL_DSDM2 ));
	// 40 * (SDM2 + SDM1/(2^8) + SDM0/(2^16) + 4) / ( 2 * (ODIV+2) );\n

	// 13rd harmonic.  
	const float fRadiator = 904.50 + 0.00; // 0.02 is specific to this device.
	const float fBandwidth = .125;
	const float fHarmonic = 13.0;
	const float fOffset = -(fBandwidth/2);
	const float fXTAL = 40;

	const float fTarg = (fRadiator+fOffset)/fHarmonic;
	const float fAPLL = fTarg * 2;
	const uint32_t sdmBaseTarget = ( fAPLL * 4 / fXTAL - 4 ) * 65536 + 0.5; // ~649134


	const float fRange = (fBandwidth)/fHarmonic;
	const float fAPLLRange = fRange * 2;
	const float sdmRange = ( fAPLLRange * 4 / fXTAL ) * 65536; // ~126

	// 491520 clocks per chip @ SF8 (2.048ms per chirp)
	const uint32_t sdmDivisor = ( CHIPSSPREAD / sdmRange ) + 0.5;


#if 0
	// For DEBUGGING ONLY
	int k;
//DisableISR();
	for( k = 0; k < 33; k++ )
	{
		int fplv = 0;

		// Send every second
		// If you want to dialate time, do it here. 
		frame = (getCycleCount()/200) % 24000000;
		fplv = SigGen( frame, codeTarg );
		uint32_t codeTargUse = codeTarg + fplv;
		uint32_t sdm = (codeTargUse * 2 / 40 - 4 * 65536);  //XXX WARNING CHARLES, why does this move in pairs?
		apll_quick_update( sdm );
		if( fplv < 0 ) break;

		if( fplv > 0 )
			uprintf( "%d %d\n", (int)sdm, (int)fplv );
	}
//EnableISR();
#else

	uint32_t start = getCycleCount();
	frame = (start) % 24000000;
	int do_send = false;
	if( (uint32_t)(start - lastend) > 240000000 )
	{
		do_send = true;
	}

	{
		int spin;

		gpio_matrix_out( 39, PRO_ALONEGPIO_OUT0_IDX, 0, 0 );
		gpio_set_drive_capability( 39, GPIO_DRIVE_CAP_3 );

		// Create an RGB Rainbow value
		uint32_t ws_out = 0xff00ff;//do_send? 0 : 0xff;

		#define nop __asm__ __volatile__("nop\n")
		int i;
		// Shift out all 24 bits, note the 1 and 3 for timing is baesed off an XTAL clock.
		for( i = 0; i < 24; i++ )
		{
			__asm__ __volatile__ ("set_bit_gpio_out 0x1");

			int high_for = ( ws_out & 0x800000 )?3:1;
			for( spin = 0; spin < high_for; spin++ ) nop;

			__asm__ __volatile__ ("clr_bit_gpio_out 0x1");
			int low_for = ( ws_out & 0x800000 )?1:3;
			for( spin = 0; spin < low_for; spin++ ) nop;

			ws_out<<=1;
		}
		esp_rom_delay_us( 5000 );

	}

	if( do_send )
	{
		SigSetupTest();
		int iterct = 0;
		int dither = 0;

		gpio_matrix_out( GPIO_NUM(RF1_PIN), CLK_I2S_MUX_IDX, 1, 0 );
		gpio_matrix_out( GPIO_NUM(RF2_PIN), CLK_I2S_MUX_IDX, 0, 0 );
		//REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD, 0);
		//REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU, 1);

		//DisableISR();
		while(1)
		{
			int fplv = 0;
			// Send every second
			// If you want to dialate time, do it here. 
			frame = (getCycleCount()) - start;
			fplv = SigGen( frame, sdmBaseTarget );


			//XXX TODO: Experiment more with dither.  It doesn't appear to have an immediate effect?
			//dither = ( dither + sdmDivisor/4) % sdmDivisor;
			uint32_t sdm = sdmBaseTarget + ( fplv + dither ) / sdmDivisor;

			if( fplv < 0 ) break;
			apll_quick_update( sdm );
			iterct++;
		}
		//EnableISR();
		//REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU, 0);
		//REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD, 1);

		gpio_matrix_out( GPIO_NUM(RF1_PIN), CLK_I2S_MUX_IDX, 1, 1 );
		gpio_matrix_out( GPIO_NUM(RF2_PIN), CLK_I2S_MUX_IDX, 0, 1 );

		printf( "Iter: %d\n", iterct );
		lastend = getCycleCount();
	}
#endif

//	vTaskDelay( 1 );
}

struct SandboxStruct
{
	void (*fnIdle)();
};
struct SandboxStruct sandbox_mode =
{
	.fnIdle = sandbox_tick,
};
