#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "microblaze_sleep.h"
#include "xgpio.h"
#include "xstatus.h"

/************************** Constant Definitions *****************************/
/* The following constant maps to the name of the hardware instances that
 * were created in the Vivado system design. */

#define PWM_BASE_ADDRESS				XPAR_PWM_W_INT_0_S00_AXI_BASEADDR

/* following constant is used to determine which channel of the GPIO is
 * used if there are 2 channels supported in the GPIO.
 */
#define PB_GPIO_CHANNEL 1
#define LED_GPIO_CHANNEL 2
#define DIP_GPIO_CHANNEL 1
#define DEVICE_0_ID 	XPAR_AXI_GPIO_0_DEVICE_ID
#define DEVICE_1_ID 	XPAR_AXI_GPIO_1_DEVICE_ID

/************************** Variable Definitions **************************/
/*
 * The following are declared globally so they are zeroed and so they are
 * easily accessible from a debugger
 */
XGpio GpioOutput; /* The driver instance for GPIO Device configured as OUT */
XGpio Gpio_DIP_Input;  /* The driver instance for GPIO Device configured as IN */
XGpio Gpio_PB_Input;  /* The driver instance for GPIO Device configured as IN */

/*****************************************************************************/

void print_banner();
int get_choice();
void print_pwm();
int do_led_cylon();
int do_led_walk();

void print(char *str);

int main() {

	int Status;
	int choice;

	init_platform();

	/*
	 * Initialize the GPIO driver so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h
	*/
	Status = XGpio_Initialize(&GpioOutput, DEVICE_1_ID);
	if (Status != XST_SUCCESS)  {
		return XST_FAILURE;
	}

	Status = XGpio_Initialize(&Gpio_DIP_Input, DEVICE_0_ID);
	if (Status != XST_SUCCESS)  {
		return XST_FAILURE;
	}

	Status = XGpio_Initialize(&Gpio_PB_Input, DEVICE_1_ID);
	if (Status != XST_SUCCESS)  {
		return XST_FAILURE;
	}

	/*
	 * Set the direction for all signals to be inputs for the DIP and PB switches
	 */
	XGpio_SetDataDirection(&Gpio_DIP_Input, DIP_GPIO_CHANNEL, 0xFFFFFFFF);
	XGpio_SetDataDirection(&Gpio_PB_Input, PB_GPIO_CHANNEL, 0xFFFFFFFF);

	/*
	 * Set the direction for all signals to be outputs for the LEDs
	 */
	XGpio_SetDataDirection(&GpioOutput, LED_GPIO_CHANNEL, 0x0);

	/*
	 * Set the GPIO outputs to red
	 */
	XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, 0xFFFFF000);

	/* Initialize the LED Dimmer controller to a safe PWM value. */
	Xil_Out32(PWM_BASE_ADDRESS, 0);

	print_banner();

	while(1) {
   	    choice = get_choice();

		switch(choice) {
			case 0x1:
				print_pwm();
				break;
			case 0x2:
				do_led_cylon();
				break;
			case 0x4:
				do_led_walk();
				break;
			default:
				break;
		}
	}
	return 0;
}


void print_banner() {
	print("\033[2J"); // Clear the screen
    print("\n\r********************************************************");
    print("\n\r********************************************************");
    print("\n\r**        Avnet/Digilent Arty Evaluation Board        **");
    print("\n\r**        LEDs and switches GPIO Demonstration        **");
    print("\n\r********************************************************");
    print("\n\r********************************************************\r\n");
    print("**\r\n");
    print("Choose Task:\r\n");
    print("BTN0: Print PWM value.\r\n");
    print("BTN1: 'Cylon' LED display.\r\n");
    print("BTN2: Scrolling LED display.\r\n");
    print("BTN3: Return to this menu.\r\n");
    print("\r\n");
}

int get_choice() {
	u8 choice;
	choice = XGpio_DiscreteRead(&Gpio_PB_Input, PB_GPIO_CHANNEL); // Capture PB press
	return choice;
}

void print_pwm() {
	unsigned int LuxReading=0;
	u32 brightness = 0;
	u8 FirstRead;
	u8 SecondRead;
	u8 pb_hit;

	print("Switch BTN0 pressed.\r\n");
	print("Printing PWM value.  Press BTN3 to exit.\r\n");
	print("Toggle DIP switches to change LED brightness.\r\n");

	LuxReading = XGpio_DiscreteRead(&Gpio_DIP_Input, DIP_GPIO_CHANNEL);
	brightness = LuxReading * 20000;
	xil_printf("LED PWM Value = %d\r", brightness);
	/* Write the duty_cycle width (Period) out to the PL PWM peripheral. */
	Xil_Out32(PWM_BASE_ADDRESS, brightness);

	pb_hit = XGpio_DiscreteRead(&Gpio_PB_Input, PB_GPIO_CHANNEL);
	while (pb_hit != 0x8) {
		FirstRead = LuxReading;
		MB_Sleep(100); // sleep ms
		LuxReading = XGpio_DiscreteRead(&Gpio_DIP_Input, DIP_GPIO_CHANNEL);
		SecondRead = LuxReading;

		if (FirstRead != SecondRead) {
			brightness = LuxReading * 20000;
			xil_printf("LED PWM Value = %6d\r", brightness);
			/* Write the duty_cycle width (Period) out to the PL PWM peripheral. */
			Xil_Out32(PWM_BASE_ADDRESS, brightness);
		}
		pb_hit = XGpio_DiscreteRead(&Gpio_PB_Input, PB_GPIO_CHANNEL);
	}

	Xil_Out32(PWM_BASE_ADDRESS, 0); //turn off the PWM LEDs
	print("\033[2J"); // Clear the screen
	print_banner();
	return;
}

int do_led_cylon() {
	u16 led_value = 1;
	u8 pb_hit = 0;
	u8 dir = 0;
	u8 i,j,k;

	print("Switch BTN1 pressed.\r\n");
    print("Display 'Cylon' pattern on RGB LEDs in R,G,B sequence.  Press BTN3 to exit.\r\n");

    for(;;) {
		led_value = 1;

		for (k=0; k<3; k++) {
			for (j=0; j<2; j++) {
				for (i=0; i<3; i++) {
					XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, led_value);
					MB_Sleep(200); // sleep ms
					pb_hit = XGpio_DiscreteRead(&Gpio_PB_Input, PB_GPIO_CHANNEL);
					if (pb_hit == 0x8) {
						XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, 0x00);
						print("\033[2J"); // Clear the screen
						print_banner();
						return 0;
					}
					if (dir == 0)
						led_value = led_value <<3 ;
					else
						led_value = led_value >>3 ;
				}
				dir = !dir;
			}
			led_value = led_value <<1;
		}
	}
}

int do_led_walk() {
	u16 led_value = 0x249;
//	u16 led_value = 0;
	u8 pb_hit = 0;
	u16 i;

	print("Switch BTN2 pressed.\r\n");
    print("Scroll through each combination of R,G,B on LEDs.  Press BTN3 to exit.\r\n");


    for(;;) {
    	for (i=0; i<7; i++) {
    		XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, led_value);
    		MB_Sleep(500); // sleep ms
    		//scroll through each combination of r,g,b
    		led_value = led_value + 0x249;
			pb_hit = XGpio_DiscreteRead(&Gpio_PB_Input, PB_GPIO_CHANNEL);
    		if (pb_hit == 0x8) {
    			XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, 0x00);
    			print("\033[2J"); // Clear the screen
    			print_banner();
    			return 0;
    		}
    	}
    	led_value = 0x249;
    }

/*
    for(;;) {
    	for (i=0; i<4095; i++) {
    		XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, led_value);
    		MB_Sleep(50); // sleep ms
    		//scroll through each combination of r,g,b
    		led_value = led_value +1;
			pb_hit = XGpio_DiscreteRead(&Gpio_PB_Input, PB_GPIO_CHANNEL);
    		if (pb_hit == 0x8) {
    			XGpio_DiscreteWrite(&GpioOutput, LED_GPIO_CHANNEL, 0x00);
    			print("\033[2J"); // Clear the screen
    			print_banner();
    			return 0;
    		}
    	}
    	led_value = 0;
    }
*/
}

