﻿/*------------------------------------------------------------------------------
 * Copyright (c) 2019 Texas Instruments Incorporated - http://www.ti.com/
 *------------------------------------------------------------------------------
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 * \brief  Sample code for generating internal pattern data and communicating
 *         with the DLP2010-LC and DLP3010-LC EVMs.
 */

#include "dlpc_common.h"
#include "dlpc34xx.h"
#include "dlpc347x_internal_patterns.h"
#include "cypress_i2c.h"
#include "math.h"
#include "stdio.h"
#include "stdint.h"
#include "stdbool.h"
#include "string.h"
#include "time.h"

#include <Windows.h>
#define WaitForSeconds(x) Sleep((x)*1000)
#define	DEBUG_I2C						  FALSE

#define MAX_WIDTH                         DLP3010_WIDTH
#define MAX_HEIGHT                        DLP3010_HEIGHT


#define	PATTERN_STEP_DELAY_MS			  5000
#define NUM_PATTERNS                      4


#define FLASH_WRITE_BLOCK_SIZE            1024
#define FLASH_READ_BLOCK_SIZE             256

#define MAX_WRITE_CMD_PAYLOAD             (FLASH_WRITE_BLOCK_SIZE + 8)
#define MAX_READ_CMD_PAYLOAD              (FLASH_READ_BLOCK_SIZE  + 8)

static uint8_t                                   **s_PatternData = NULL;
static DLPC34XX_INT_PAT_PatternData_s            s_Patterns[NUM_PATTERNS];
static DLPC34XX_INT_PAT_PatternSet_s             s_PatternSet;
static DLPC34XX_INT_PAT_PatternOrderTableEntry_s s_PatternOrderTable;

static uint8_t                                   s_WriteBuffer[MAX_WRITE_CMD_PAYLOAD];
static uint8_t                                   s_ReadBuffer[MAX_READ_CMD_PAYLOAD];

static bool                                      s_StartProgramming;
static uint8_t                                   s_FlashProgramBuffer[FLASH_WRITE_BLOCK_SIZE];
static uint16_t                                  s_FlashProgramBufferPtr;

/**
 * Implement the I2C write transaction here. The sample code here sends
 * data to the controller via the Cypress USB-Serial adapter.
 */
uint32_t WriteI2C(uint16_t             WriteDataLength,
	uint8_t*                           WriteData,
	DLPC_COMMON_CommandProtocolData_s* ProtocolData)
{
	bool Status = true;
	//printf("Write I2C Starts, length %d!!! \n", WriteDataLength);
	if(DEBUG_I2C)
	{
		printf("*--------*\nI2C write: ");
		for (uint16_t i = 0; i < WriteDataLength; i++)
		{
			printf("%02X ", WriteData[i]);
		}
		printf("\n*--------*\n");
	}
	Status = CYPRESS_I2C_WriteI2C(WriteDataLength, WriteData);
	if (Status != true)
	{
		//printf("Write I2C Error!!! \n");
		return FAIL;
	}

	return SUCCESS;
}

/**
 * Implement the I2C write/read transaction here. The sample code here
 * receives data from the controller via the Cypress USB-Serial adapter.
 */
uint32_t ReadI2C(uint16_t              WriteDataLength,
	uint8_t*                           WriteData,
	uint16_t                           ReadDataLength,
	uint8_t*                           ReadData,
	DLPC_COMMON_CommandProtocolData_s* ProtocolData)
{
	bool Status = 0;
	//printf("Write/Read I2C Starts, length %d!!! \n", WriteDataLength);
	Status = CYPRESS_I2C_WriteI2C(WriteDataLength, WriteData);
	if (Status != true)
	{
		//printf("Write I2C Error!!! \n");
		return FAIL;
	}

	Status = CYPRESS_I2C_ReadI2C(ReadDataLength, ReadData);
	if (Status != true)
	{
		//printf("Read I2C Error!!! \n");
		return FAIL;
	}
	
	if(DEBUG_I2C)
	{
		printf("*--------*\nI2C Read: ");
		printf("%02X -> ", WriteData[0]);
		for (uint16_t i = 0; i < ReadDataLength; i++)
		{
			printf("%02X ", ReadData[i]);
		}
		printf("\n*--------*\n");
	}
	return SUCCESS;
}

/**
 * Initialize the command layer by setting up the read/write buffers and 
 * callbacks.
 */
void InitConnectionAndCommandLayer()
{
    DLPC_COMMON_InitCommandLibrary(s_WriteBuffer,
                                   sizeof(s_WriteBuffer),
                                   s_ReadBuffer,
                                   sizeof(s_ReadBuffer),
                                   WriteI2C,
                                   ReadI2C);

    CYPRESS_I2C_ConnectToCyI2C();
}



/**
 * A sample function that generates an 8-bit (gray scale) 1-D pattern
 * The function fills the byte array Data. Each byte in the in array corresponds
 * to a pixel. For an 8-bit pattern the value of each byte can be 0 - 255.
 */
void PopulateEightBitPatternData(uint16_t Length, uint8_t* Data, uint16_t NumBars)
{
    uint16_t PixelPos     = 0;
    uint16_t BarPos       = 0;
    uint16_t BarWidth     = Length / (2 * NumBars);
    uint8_t  PixelData    = 0;
    int16_t  PixelDataInc = (int16_t)ceil(255.0 / BarWidth);

    for (; PixelPos < Length; PixelPos++)
    {
        Data[PixelPos] = PixelData;

        BarPos++;
        if (BarPos >= BarWidth)
        {
            BarPos    = 0;
            PixelDataInc = -PixelDataInc;
        }

        PixelData = (uint8_t)(PixelData + PixelDataInc);
    }
}

/**
 * Populates an array of DLPC34XX_INT_PAT_PatternSet_s
 */
void PopulatePatternSetData(uint16_t DMDWidth, uint16_t DMDHeight)
{
    uint8_t                        Index;
    uint16_t                       NumBars;
    DLPC34XX_INT_PAT_PatternSet_s* PatternSet;

    s_PatternData = malloc(sizeof(uint8_t *)*NUM_PATTERNS);
    for (Index = 0; Index < NUM_PATTERNS; Index++)
    {
        s_PatternData[Index] = malloc(sizeof(uint8_t)*DMDWidth);
    }

    PatternSet = &s_PatternSet;
    PatternSet->BitDepth     = DLPC34XX_INT_PAT_BITDEPTH_EIGHT;
    PatternSet->Direction    = DLPC34XX_INT_PAT_DIRECTION_VERTICAL; // TODO
    PatternSet->PatternCount = NUM_PATTERNS;
    PatternSet->PatternArray = s_Patterns;

    for (Index = 0; Index < NUM_PATTERNS; Index++)
    {
        NumBars = 5 * (Index + 1);
        PopulateEightBitPatternData(DMDWidth, s_PatternData[Index], NumBars);
        s_Patterns[Index].PixelArray      = s_PatternData[Index];
        s_Patterns[Index].PixelArrayCount = DMDWidth;

    }

        DLPC34XX_INT_PAT_PatternOrderTableEntry_s* PatternOrderTableEntry = &s_PatternOrderTable;;

    PatternOrderTableEntry->PatternSetIndex                        = 0;
    PatternOrderTableEntry->NumDisplayPatterns                     = s_PatternSet.PatternCount;
    PatternOrderTableEntry->IlluminationSelect                     = DLPC34XX_INT_PAT_ILLUMINATION_GREEN;
    PatternOrderTableEntry->InvertPatterns                         = false;
    PatternOrderTableEntry->IlluminationTimeInMicroseconds         = 5000;
    PatternOrderTableEntry->PreIlluminationDarkTimeInMicroseconds  = 250;
    PatternOrderTableEntry->PostIlluminationDarkTimeInMicroseconds = 1000;
	PatternOrderTableEntry->PatternEntryIndex 					   = 0;
}

void CopyDataToFlashProgramBuffer(uint8_t* Length, uint8_t** DataPtr)
{
    while ((*Length >= 1) &&
           (s_FlashProgramBufferPtr < sizeof(s_FlashProgramBuffer)))
    {
        s_FlashProgramBuffer[s_FlashProgramBufferPtr] = **DataPtr;
        s_FlashProgramBufferPtr++;
        (*DataPtr)++;
        (*Length)--;
    }
}

void ProgramFlashWithDataInBuffer(uint16_t Length)
{
    s_FlashProgramBufferPtr = 0;

    if (s_StartProgramming)
    {
        s_StartProgramming = false;
        DLPC34XX_WriteFlashStart(Length, s_FlashProgramBuffer);
    }
    else
    {
        DLPC34XX_WriteFlashContinue(Length, s_FlashProgramBuffer);
    }
}

void BufferPatternDataAndProgramToFlash(uint8_t Length, uint8_t* Data)
{
    /* Copy data that can fit in the flash programming buffer */
    CopyDataToFlashProgramBuffer(&Length, &Data);

    /* Write data to flash if the buffer is full */
    if (s_FlashProgramBufferPtr >= sizeof(s_FlashProgramBuffer))
    {
        ProgramFlashWithDataInBuffer((uint16_t)sizeof(s_FlashProgramBuffer));
    }

    /* Copy remaining data (if any) to the flash programming buffer */
    CopyDataToFlashProgramBuffer(&Length, &Data);
}

void GenerateAndProgramPatternData(DLPC34XX_INT_PAT_DMD_e DMD, bool EastWestFlip, bool LongAxisFlip)
{
    s_StartProgramming = true;
    s_FlashProgramBufferPtr = 0;

    /* Let the controller know that we're going to program pattern data */
    DLPC34XX_WriteFlashDataTypeSelect(DLPC34XX_FDTS_ENTIRE_SENS_PATTERN_DATA);
    
    /* Erase the flash sectors that store pattern data */
    DLPC34XX_WriteFlashErase();

	/* Read Short Status to make sure Erase is completed */
	DLPC34XX_ShortStatus_s ShortStatus;
	do
	{
		DLPC34XX_ReadShortStatus(&ShortStatus);
	} while (ShortStatus.FlashEraseComplete == DLPC34XX_FE_NOT_COMPLETE);

	/* To program the flash, send blocks of data of up to 1024 bytes
     * to the controller at a time. Repeat the process until the entire
     * data is programmed to the flash.
     * Let the controller know the size of a data block that will be 
     * transferred at a time.
     */
    DLPC34XX_WriteFlashDataLength(sizeof(s_FlashProgramBuffer));

    /* Generate pattern data and program it to the flash.
     * 
     * The DLPC34XX_INT_PAT_GeneratePatternDataBlock() function calls the
     * BufferPatternDataAndProgramToFlash() function several times while it
     * generates pattern data.
     * 
     * The BufferPatternDataAndProgramToFlash() function buffers data received,
     * programming the buffer content only when it is full. This is done in an
     * effort to make flash writes more efficient, overall greatly reducing the
     * time it takes to program the pattern data.
     * 
     * After returning from the DLPC34XX_INT_PAT_GeneratePatternBlock() function,
     * check if there is any data left in the buffer and program it. This needs 
     * to be done since the BufferPatternDataAndProgramToFlash() function only 
     * programs the buffer content if full.
     */
    DLPC34XX_INT_PAT_GeneratePatternDataBlock(DMD,
                                              1,
                                              &s_PatternSet,
                                              1,
                                              &s_PatternOrderTable,
                                              BufferPatternDataAndProgramToFlash,
											  EastWestFlip,
                                              LongAxisFlip);
    if (s_FlashProgramBufferPtr > 0)
    {
        /* Resend the block size since it could be less than 
         * the previously specified size
         */
        DLPC34XX_WriteFlashDataLength(s_FlashProgramBufferPtr);

        ProgramFlashWithDataInBuffer(s_FlashProgramBufferPtr);
    }
}
/** This function is called after writing FLASH */
void LoadPatternOrderTableEntryfromFlash()
{
	DLPC34XX_PatternOrderTableEntry_s e1;
	/* Reload from Flash */
	DLPC34XX_WritePatternOrderTableEntry(DLPC34XX_WC_RELOAD_FROM_FLASH, &e1);
}

void StepAllPatterns(uint32_t StepDelay, uint32_t RepeatCount)
{



    DLPC34XX_PatternOrderTableEntry_s PatternSet;

    // Read and store all pattern sets, assuming the last one is the first set with 0 patterns

    {
        DLPC34XX_ReadPatternOrderTableEntry(0, &PatternSet);

        if (PatternSet.NumberOfPatternsToDisplay == 0)
        {
            return;
        }

        printf("PatternOrderTableEntry.IlluminationTime: %u \n", PatternSet.IlluminationTime);
        printf("PatternOrderTableEntry.PreIlluminationDarkTime: %u \n", PatternSet.PreIlluminationDarkTime);
        printf("PatternOrderTableEntry.PostIlluminationDarkTime: %u \n", PatternSet.PostIlluminationDarkTime);
    }


    uint32_t Repeats = 0;
    uint32_t rc = 0;
    while (Repeats++ < RepeatCount)
    {		
        DLPC34XX_WritePatternOrderTableEntry(DLPC34XX_WC_START, &PatternSet);

		DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_STOP, 0);

        DLPC34XX_WriteTriggerOutConfiguration(DLPC34XX_TT_TRIGGER1, DLPC34XX_TE_ENABLE, DLPC34XX_TI_NOT_INVERTED, 0);
        DLPC34XX_WriteTriggerOutConfiguration(DLPC34XX_TT_TRIGGER2, DLPC34XX_TE_ENABLE, DLPC34XX_TI_NOT_INVERTED, 0);

        DLPC34XX_WriteTriggerInConfiguration(DLPC34XX_TE_DISABLE, DLPC34XX_TP_ACTIVE_HI);

        DLPC34XX_WritePatternReadyConfiguration(DLPC34XX_TE_ENABLE, DLPC34XX_TP_ACTIVE_HI);

		rc = DLPC34XX_WriteOperatingModeSelect(DLPC34XX_OM_SENS_INTERNAL_PATTERN);
        printf("DLPC34XX_WriteOperatingModeSelect code is %d\n", rc);


		rc = DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_START, 255); printf("DLPC34XX_PC_START code is %d\n", rc);
		rc = DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_PAUSE, 255); printf("DLPC34XX_PC_PAUSE code is %d\n", rc);
		rc = DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_RESET, 255); printf("DLPC34XX_PC_RESET code is %d\n", rc);
        printf("patternId: %d\n", 0);
		Sleep(StepDelay);

        // first pattern gets displayed automatically
        for (int i = 0; i < PatternSet.NumberOfPatternsToDisplay - 1; ++i)
        {
			printf("patternId: %d\n",i+1);
            rc = DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_STEP, 255); printf("DLPC34XX_PC_STEP code is %d\n", rc);
            
            Sleep(StepDelay);
        }

		DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_STOP, 0);

    }
}

void main()
{
    InitConnectionAndCommandLayer();
    printf("START\n");

	bool Status = CYPRESS_I2C_RequestI2CBusAccess();
	if (Status != true)
	{
		printf("Error Request I2C Bus ACCESS!!!\n");
        CYPRESS_I2C_Disconnect();
		return;
	} else {
        printf("wait 0.5s\n");
        Sleep(500);
    }

	DLPC34XX_ControllerDeviceId_e DeviceId = 0;
	uint32_t rc = DLPC34XX_ReadControllerDeviceId(&DeviceId);
    printf("DLPC34XX_ReadControllerDeviceId rc=%u\n", rc);
    printf("Controller Device Id = %d\n", DeviceId);

    if (rc != 0 || DeviceId == 0)
    {
        printf("Read Device ID failed. Stop here.\n");
        CYPRESS_I2C_RelinquishI2CBusAccess();
        Sleep(100);
        CYPRESS_I2C_Disconnect();
        return;
    }
    // DLPC34XX_WriteRgbLedCurrent(400, 500, 500);

uint32_t rc_led;

// rc_led = DLPC34XX_WriteRgbLedCurrent(400, 500, 500);
// printf("WriteRgbLedCurrent(400,500,500) rc=%u\n", rc_led);


    uint16_t RedLedCurrent, GreenLedCurrent, BlueLedCurrent;
    // DLPC34XX_ReadRgbLedMaxCurrent(&RedLedCurrent, &GreenLedCurrent, &BlueLedCurrent);
    // printf("LedCurrent R: %u, G: %u, B: %u\n", RedLedCurrent, GreenLedCurrent, BlueLedCurrent);

    uint32_t tc = DLPC34XX_WriteRgbLedCurrent(500, 500, 500);
    printf("DLPC34XX_WriteRgbLedCurrent tc=%d\n", tc);


    /* Prepare the data for pattern data block generation */
    PopulatePatternSetData(MAX_WIDTH, MAX_HEIGHT);


    /* Stop pattern display */
    DLPC34XX_WriteInternalPatternControl(DLPC34XX_PC_STOP, 0);

    /* Generate and program the pattern data to the controller flash */
    GenerateAndProgramPatternData(DLPC34XX_INT_PAT_DMD_DLP3010, false, false);

    /* Load Pattern Order Table Entry from Flash */
    LoadPatternOrderTableEntryfromFlash();

	StepAllPatterns(PATTERN_STEP_DELAY_MS, 1);


	CYPRESS_I2C_RelinquishI2CBusAccess();
    CYPRESS_I2C_Disconnect();
}
