#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <stdint.h>
#include <wiringPi.h>
#include <string.h>
#include "LocalDimmingLedData.h"
#include "interface.h"

#define SPI_DEVICE "/dev/spidev0.0"  // SPI设备文件路径

#define   AW20144_PAGE0                     (0xA0U)
#define   AW20144_PAGE1                     (0xA2U)
#define   AW20144_PAGE2                     (0xA4U)
#define   AW20144_PAGE3                     (0xA6U)
#define   AW20144_PAGE4                     (0xA8U)

#define  ENABLE                             25
#define  AW20144_CHIP1                      26
#define  AW20144_CHIP2                      11
#define  AW20144_CHIP3                      27
#define  AW20144_CHIP4                      0

char ret[12] = {0xA5, 0x01, 0xff, 0x03, 0x04, 0x05, 0x06, 0xA5, 0x00, 0x00, 0x00, 0x00};
char totalbuf[1024];
int count = 1;

int fileusb;
int fileuart;


int t = 0;

int spi_fd;


static audio_event_cb_t g_callback = NULL;
static void *g_callback_ctx = NULL;

void audio_set_callback(audio_event_cb_t cb, void *user_ctx)
{
    g_callback = cb;
    g_callback_ctx = user_ctx;
}

//xfm
int xfm_Init()
{
    //sleep(60);
   	// ttyUSB0 ttyAMA0gi
	if ((fileusb = serialOpen("/dev/ttyUSB0", 115200)) < 0)
	{
		printf("/dev/ttyUSB0 Open Faild\r\n");
		return 0;
	}
	printf("/dev/ttyUSB0 Open success\r\n");

	if ((fileuart = serialOpen("/dev/ttyAMA0", 9600)) < 0)
	{
		printf("/dev/ttyAMA0 Open Faild\r\n");
		return 0;
	}
	printf("/dev/ttyAMA0 Open success\r\n");
    memset(totalbuf, 0, 1024);
    
    
    return 1;
}

void protocalAck(int fileusb)
{
     printf("protocalAck..\n");
	char retExmaple[12] = {0};
	strcpy(retExmaple, ret);
	retExmaple[3] = 0x04;
	retExmaple[4] = 0x00;
	retExmaple[5] = 0x00;
	retExmaple[6] = 0x00;

	for (int i = 0; i < 11; i++)
	{
		retExmaple[11] += retExmaple[i];
	}
	retExmaple[11] = ~retExmaple[11] + 1;
	retExmaple[11] = 0xb2;
	write(fileusb, retExmaple, 12);
}

// get angle and change into 16bits, return;
int getAngle(char *rece)
{
	int angle;
	int result = 0;
	int i = 0;
	char *p = rece;
	while (*(p + i) != 'a' || *(p + i + 1) != 'n')
	{
		i++;
	}
	i += 8;
	while (*(p + i) != ',')
	{
		if (*(p + i) == '.')
		{
			i++;
			continue;
		}
		int num = *(p + i) - '0';
		result = result * 10 + num;
		i++;
	}
	// printf("result = %d\n", result);
	angle = result;
	return angle;
}
// crc
uint16_t CrcValueCalc(uint8_t *cpucData, uint16_t usLength)   //changed
{
	uint16_t crcValue = 0xffff;
	int i;
	while (usLength--)
	{
		crcValue ^= *cpucData++;
		for (i = 8 - 1; i >= 0; i--)
		{
			if (crcValue & 0x0001)
			{
				crcValue = (crcValue >> 1) ^ 0xa001;
			}
			else
			{
				crcValue = crcValue >> 1;
			}
		}
	}
	return crcValue;
}
extern int get_audio_device_id();


void sendMessageHead(int mode, char* message)   //changed
{
    *(message) = 0x40;
    *(message + 1) = 0x38;   // head 
    if (mode == 0)   // read
    {
        *(message + 2) = 0x07;  //length
        *(message + 3) = 0x03;   //instru
        *(message + 4) = 0x00;   //reg0
        *(message + 6) = 0x01;  //ack
    }
    else{             // write
        *(message + 2) = 0x0b;   //length
        *(message + 3) = 0x06;    //instru
        *(message + 4) = 0x00;    //reg0
        *(message + 5) = 0x02;    //reg1 write 
        *(message + 6) = 0x00;
        *(message + 7) = 0x00;
        *(message + 8) = 0x00;
    }
}

void sentMessageAngle(int fileuart, uint16_t angle)  //changed
{
    char messageAngle[11];
    sendMessageHead(1, messageAngle);
    messageAngle[9] = (angle >> 8) & 0xff;
    messageAngle[10] = angle & 0xff;

    uint16_t crc;
    crc = CrcValueCalc(messageAngle, 11);
    messageAngle[12] = (crc >> 8) & 0xff;
    messageAngle[11] = crc & 0xff;

    write(fileuart, messageAngle, 13);
}

char* readMessageAngle(int fileuart)
{
    char* result = malloc(sizeof(char));
    return result;
}
char* readMessageStatus(int fileuart)
{
    char message[9];
    sendMessageHead(0, message);
    message[5] = 0x01;
    
    uint16_t crc;
    crc = CrcValueCalc(message, 7);
    message[7] = (crc >> 8) & 0xff;
    message[8] = crc & 0xff;

    int t = 0;
    while (!t)
    {
        write(fileuart, message, 9);
        int recbyte = serialDataAvail(fileuart);

        while (1);
    }

    char* result = malloc(sizeof(char));
    return result;
}

int judgeData()
{
    return 1;
}

void waitforReady(int fileuart)
{
    int t = 0;
    while (!t)
    {
        int recbytenum = serialDataAvail(fileuart);
        if (recbytenum > 1)
        {
            char buf[24] = {0};
            int temp = 0;
            while (recbytenum--)  //recbyte
            {
                buf[temp++] = serialGetchar(fileuart);
            }
            
            if (!judgeData) //judge data if not right
            {
                usleep(500);
                readMessageStatus(fileuart);
                continue;
            }
            
            // check receive data .....





            serialFlush(fileuart);   //make status ready for next place
            break;
        }
    }
}

void waitforReady_test(int fileuart)  //test_version
{
    while (1)
    {
        int recbytenum = serialDataAvail(fileuart);
        if (recbytenum > 1)
        {
            char buf[24] = {0};
            int temp = 0;
            while (recbytenum--)
            {
                buf[temp++] = serialGetchar(fileuart);
            }
            for (int i = 0; i < temp; i++)
            {
                printf("%x", buf[i]);
                if ( (i%2) == 0)
                    printf(" ");
            }
            printf("\n");

            serialFlush(fileuart);
            break;
        }
    }
}


int last = 0;
int reality = 0;
int dealAngle(int angle) 
{
	int result = 0;
	int ret = 0;
	if (last <= 1800){
		int max = last + 1800;
		if (angle >= last && angle <= max){
			//turn leftji
			result = angle - last;
			//prshortf("sent message = %d\n", reality + result);
		}
		else {
			//turn right
			if (last > angle){
				result = angle - last;
				//prshortf("sent message = %d\n", reality + result);
			}
			else{
				result = last + (3600 - angle);
				result = -result;
				//prshortf("sent message = %d\n", reality + result);
			}
		}
	}
	else{
		int over = last - 1800;
		if (angle >= over && angle <= last){
			//turn right
			result = angle - last;
			//prshortf("sent message = %d\n", reality + result);
		}
		else {
			//turn left
			if ( angle < over){
				result = angle + 3600 - last;
				//prshortf("sent message = %d\n", reality + result);
			}
			else{
				result = angle - last;
				//prshortf("sent message = %d\n", reality + result);
			}
		}
	}
	ret = reality + result;
	return ret;
}



void sentUart(char *buf, int fileuart, int fileusb)
{
	if (count == 9) //9
	{
		strcat(totalbuf, buf);        
		int angle;
		angle = getAngle(totalbuf);
		printf("usb realize angle is = %d\n", angle);
		int sentangle = dealAngle(3600 - angle);
        last = angle;
		//40 38 0B 06 00 0A 01 XX XX XX XX  CRCL CRCH
		char message[13] = {0x40, 0x38, 0x0B, 0x06, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
        sendMessageHead(1, message); 
		message[7] = (sentangle >> 24) & 0xff;
		message[8] = (sentangle >> 16) & 0xff;
		message[9] = (sentangle >> 8) & 0xff;
		message[10] = sentangle & 0xff;
		uint16_t crc = CrcValueCalc(message, 11);
		message[11] = crc & 0xff;
		message[12] = (crc >> 8) & 0xff;
        if (g_callback != NULL)
        {    
            char buf[256] = {0};
            snprintf(buf, 256, "cvlc --play-and-exit --aout alsa --alsa-audio-device hw:%d,0 /usr/local/bin/Videos/aaaa.mp3 &", get_audio_device_id());
            //system("mpg321 /usr/local/bin/Videos/aaaa.mp3 &");
            //system("cvlc --play-and-exit --aout alsa --alsa-audio-device hw:2,0 /usr/local/bin/Videos/aaaa.mp3 &");
            system(buf);

            g_callback(g_callback_ctx, 2);
        }
        usleep(100);
		write(fileuart, message, 13);   printf("                          %d\n", __LINE__);
		
        count = 1;
		memset(totalbuf, 0, 1024);
        serialFlush(fileusb);
	}
	else
	{
		count++;
		strcat(totalbuf, buf);
	}
}

/*
void sentUart(char *buf, int fileuart, int fileusb)    //changed
{
	if (count == 9)
	{
		strcat(totalbuf, buf);
		uint16_t angle;
		angle = getAngle(totalbuf);
		printf("angle = %x\n", angle);
        sentMessageAngle(fileuart, angle);
        //sentMessageAngle(fileuart, angle);
        // 0x40 0x38 0x0b 0x06 0x00 0x02 0x01 00 -- -- -- 1d 08    0
          //                                        04 b0 1e 7c    120
        // char message1[13] = {0x40, 0x38, 0x0b, 0x06, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x08};
        // char message2[13] = {0x40, 0x38, 0x0b, 0x06, 0x00, 0x02, 0x01, 0x00, 0x00, 0x04, 0xb0, 0x1e, 0x7c};
        // if (t == 0){
        //     write(fileuart, message1, 13);
        //     t = 1;
        // }
        // else{
        //     write(fileuart, message2, 13);
        //     t = 0;
        // }
            
		count = 1;
		memset(totalbuf, 0, 1024);
        protocalAck(fileusb);

        if (g_callback != NULL)
        {    
            char buf[256] = {0};
            snprintf(buf, 256, "cvlc --play-and-exit --aout alsa --alsa-audio-device hw:%d,0 /usr/local/bin/Videos/aaaa.mp3 &", get_audio_device_id());
            //system("mpg321 /usr/local/bin/Videos/aaaa.mp3 &");
            //system("cvlc --play-and-exit --aout alsa --alsa-audio-device hw:2,0 /usr/local/bin/Videos/aaaa.mp3 &");
            system(buf);
            g_callback(g_callback_ctx, 2);
        }

        //waitforReady_test(fileuart);

	}
	else
	{
		count++;
		strcat(totalbuf, buf);
	}

}
*/

void circleReading()    //changed
{
    printf("cicleReading..\n");
    serialFlush(fileusb);
    while (1)
    {
        int recbytenum = serialDataAvail(fileusb);
		char buf[1024] = {0};
        if (recbytenum > 1)
		{
			int temp = 0;
			int totalbytes = recbytenum;
			while (totalbytes--)
			{
				buf[temp++] = serialGetchar(fileusb);
			}

			if (buf[0] == 0xa5 && buf[2] == 0x01)
			{
				protocalAck(fileusb);        printf("                          %d\n", __LINE__);
			}
			else
			{
				sentUart(buf, fileuart, fileusb);     printf("                          %d\n", __LINE__);
                //serialFlush(fileusb);  //guarantee buf wont affect next input
			}
		}
    }
    return;
}

//localdimming
void LocalDimming_Init()
{
    spi_fd = open(SPI_DEVICE, O_RDWR);
    if (spi_fd < 0) {
        perror("Unable to open SPI device");
        exit(1);
    }
    // 配置SPI模式、位数、速度等参数
    uint8_t spi_mode = SPI_MODE_0;
    uint8_t spi_bits = 8;
    uint32_t spi_speed = 600000;  // SPI速度;

    usleep(2000);
    if (ioctl(spi_fd, SPI_IOC_WR_MODE, &spi_mode) == -1) {
        perror("Unable to set SPI mode");
        exit(1);
    }
    if (ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits) == -1) {
        perror("Unable to set SPI bits per word");
        exit(1);
    }
    // provided 
    if (ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed) == -1) {
        perror("Unable to set SPI speed");
        exit(1);
    }
    
    //raspi
    if (wiringPiSetup() == -1)
    {
        //printf("");
        //return -1;
    }

    usleep(2000);
    pinMode(25, OUTPUT);
    pinMode(26, OUTPUT);
    pinMode(11, OUTPUT);
    pinMode(27, OUTPUT);
    pinMode(0, OUTPUT);

     // enable set to 0;
    setGpioLevel(25, 0); 
    // cs set to high;
    setGpioLevel(26, 1);
    setGpioLevel(11, 1);
    setGpioLevel(27, 1);
    setGpioLevel(0, 1);
    setGpioLevel(25, 1);
    
    usleep(2000);
    AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE0, 0x2FU, 0xAEU);
    AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE0, 0x2FU, 0xAEU);
    AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE0, 0x2FU, 0xAEU);
    AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE0, 0x2FU, 0xAEU);
    
    usleep(2000);
    AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE0, 0x00U, 0x71U);
    AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE0, 0x00U, 0x71U);
    AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE0, 0x00U, 0x71U);
    AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE0, 0x00U, 0x71U);

    AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE0, 0x01U, 0xFFU);
    AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE0, 0x01U, 0xFFU);
    AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE0, 0x01U, 0xFFU);
    AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE0, 0x01U, 0xFFU);
   
    AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE0, 0x4DU, 0x07U);
    AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE0, 0x4DU, 0x07U);
    AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE0, 0x4DU, 0x07U);
    AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE0, 0x4DU, 0x07U);
    //sl for max
    for (unsigned int i = 0U; i < 144U; i++)
    {
         AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE2, i, 0xFFU);
         AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE2, i, 0xFFU);
         AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE2, i, 0xFFU);
         AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE2, i, 0xFFU);
    }
}

 void setGpioLevel(int num, int level)
{
    digitalWrite(num, level);  
    return;
}

 int SPI_Transfer(const char *pu8TxBuf, char *pu8RxBuf, int i32len)
{

    int i32Rtn;

    if (spi_fd < 0)
    {
        return -1;
    }
    
    struct spi_ioc_transfer stTrans =
    {
        .tx_buf      = (unsigned long)pu8TxBuf,
        .rx_buf      = (unsigned long)pu8RxBuf,
        .len         = i32len,
        .delay_usecs = 0,
    };
    
    if (ioctl(spi_fd, SPI_IOC_MESSAGE(1), &stTrans) < 0)
    {
        printf("failed to transfer data through SPI0 \n");
        return -1;
    }

    return 0;
}

 int AW20144_Byte_Write(int i32ChipSelect, char u8Page, char u8Addr, char u8Val)
{
    char u8DataBuffer[3];

    //GPIO_Set_Level(i32ChipSelect, GPIO_LEVEL_LOW);
    setGpioLevel(i32ChipSelect, 0); 
    u8DataBuffer[0] = u8Page & 0xFEU;
    u8DataBuffer[1] = u8Addr;
    u8DataBuffer[2] = u8Val;
    SPI_Transfer(u8DataBuffer, NULL, 3);
    setGpioLevel(i32ChipSelect, 1); 
    //GPIO_Set_Level(i32ChipSelect, GPIO_LEVEL_HIGH);

    return 0;
}

void resetPwm()
{
    for (unsigned int i = 0U; i < 144U; i++)
    {
         AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE1, i, 0x00U);
         AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE1, i, 0x00U);
         AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE1, i, 0x00U);
         AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE1, i, 0x00U);
    }
    return;
}
void setPwmtomax()
{
    for (unsigned int i = 0U; i < 144U; i++)
    {
         AW20144_Byte_Write(AW20144_CHIP1, AW20144_PAGE1, i, 0x0FU);
         AW20144_Byte_Write(AW20144_CHIP2, AW20144_PAGE1, i, 0x0FU);
         AW20144_Byte_Write(AW20144_CHIP3, AW20144_PAGE1, i, 0x0FU);
         AW20144_Byte_Write(AW20144_CHIP4, AW20144_PAGE1, i, 0x0FU);
    }
    return;
}
//*(*(ptr + k) + temp)
void eachPicWrite(int num, int pic)
{
    const char (*LocalDimmingLedData)[198];
    
    switch (num)
    {
        case 2:
            LocalDimmingLedData = LocalDimmingLedData_Ack;
            pic = 271 - pic;
            break;
        case 3:
            LocalDimmingLedData = LocalDimmingLedData_Right;
            pic = 91 - pic;
            break;
        case 4:
            LocalDimmingLedData = LocalDimmingLedData_Left;
            pic = 91 - pic;
            break;
        default:
            LocalDimmingLedData = LocalDimmingLedData_Standby;
            pic = 161 - pic;
            break;
    }

    int temp = 0;
    int count = 198;
    int chip[11] = {26, 26, 26, 11, 11, 11, 27, 27, 27, 0, 0}; 
    int addr[11] = {0, 72, 36, 0, 72, 36, 0, 72, 36, 0, 36};
    int op[11] = {18, 90, 54, 18, 90, 54, 18, 90, 54, 18, 54};
        for (int i = 0; i < 11; i++)
        {
            //int num = 9;
            int num = 9;
            int offset = addr[i];
            int chips = chip[i];
            int ops = op[i];
            for (int j = 0; j < 18; j++)
            {
                /*
                if (LocalDimmingLedData[pic][i*18 + j] == 0x00){
                    AW20144_Byte_Write(chips, AW20144_PAGE1, num+offset, 0x00); 
                
                }
                else{
                    AW20144_Byte_Write(chips, AW20144_PAGE1, num+offset, LocalDimmingLedData[pic][i*18 + j] + 0x30); 
                }
                */
                AW20144_Byte_Write(chips, AW20144_PAGE1, num+offset, 0xff); //LocalDimmingLedData[pic][i*18 + j]
                //AW20144_Byte_Write(chips, AW20144_PAGE1, num+offset, *(a + temp));
                //printf("%d\n", num+offset);
                //printf("%x\n", *(a + temp));
                //printf("");
                num = (num+1) % 18;
            }
        
        }
}
