/******************************************************************************
 *
 * Copyright 2013, Silicon Image, Inc.  All rights reserved.
 * No part of this work may be reproduced, modified, distributed, transmitted,
 * transcribed, or translated into any language or computer format, in any form
 * or by any means without written permission of
 * Silicon Image, Inc., 1140 East Arques Avenue, Sunnyvale, California 94085
 *
 *****************************************************************************/
/**
 * @file sii9777rx_example.c
 *
 * @brief example application for sii9777rx
 *
 *****************************************************************************/

/***** #include statements ***************************************************/
#include <stdio.H>
#include <windows.h>

#include "si_datatypes.h"
#include "sii9777rx_api.h"
#include "si_hal_aardvark_api.h"
#include "sii9777_platform_api.h"
#include "sii9777_user_def.h"

/***** global variables *******************************************************/

/***** local macro definitions ***********************************************/

#define PACKET_SIZE    SII9777_USER_DEF__ISP_CASH_SIZE

/***** local variables *******************************************************/

static bool_t           sbSii9777Interrupt = false;
static SiiInst_t        sSii9777Inst       = SII_INST_NULL;
static FILE             *spFile            = NULL;
static bool_t                   sbRunning          = true;
static uint32_t                 sBytesProg         = 0;

static char                     *spFileName        = NULL;
static Sii9777FlashOperation_t  sOperation         = SII9777_FLASH_OPERATION__WRITE;
static Sii9777FlashTarget_t     sTarget            = SII9777_FLASH_TARGET__FULL_DEVICE;
static bool_t                   sbServiceData      = false;

/* Static configuratyion object for hosr driver configuration */
static Sii9777Config_t          sSii9777Config     =
{
    "9777-Rx",                           // pNameStr[8]         - Name of Object
    true,                                // bDeviceReset       - 
};

/***** local functions *******************************************************/

static void sArgvHandler( int argc, char *argv[] )
{
    int c = 1;
    for( ; c < argc; )
    {
        if( argv[c][0] == '-' )
        {
            int i;
            switch( argv[c][1] )
            {
                case 'o':
                    i = argv[c][2] - '0';
                    sOperation = (i == 0) ? SII9777_FLASH_OPERATION__READ : SII9777_FLASH_OPERATION__WRITE;
                    c++;
                    break;

                case 't':
                    i = argv[c][2] - '0';
                    sTarget = (i == 1) ? SII9777_FLASH_TARGET__EDID_TABLES : SII9777_FLASH_TARGET__FULL_DEVICE;
                    c++;
                    break;

                case 's':
                    sbServiceData = true;
                    c++;
                    break;

                default:
                    break;
            }
        }
        else
        {
            spFileName = argv[c++];
        }
    }
}

static bool_t sWaitForSii9777BootReady( void )
{
    Sii9777BootStat_t bootStat;

    printf("Wait for Sii9777 Device to be booted...");
    do {
        Sii9777BootStatusQuery(sSii9777Inst, &bootStat);
    } while( SII9777_BOOT_STAT__IN_PROGRESS == bootStat );

    printf("%s\n", (SII9777_BOOT_STAT__SUCCESS == bootStat) ? "Passed" : "Failed");

    return (SII9777_BOOT_STAT__SUCCESS == bootStat) ? (true) : (false);
}

static void sEnableCallBackNotifications( Sii9777EventFlags_t eventFlagsMask )
{
    Sii9777EventFlagsMaskSet(sSii9777Inst, &eventFlagsMask);
}

static void sLogVersionInformation( void )
{
    uint32_t    chipIdStat   = 0;
    uint32_t    chipRevStat  = 0;
    uint32_t    prodIdStat   = 0;
    uint32_t    versionStat  = 0;

    Sii9777ChipIdQuery(sSii9777Inst, &chipIdStat);
    Sii9777ChipRevisionQuery(sSii9777Inst, &chipRevStat);
    Sii9777ProductIdQuery(sSii9777Inst, &prodIdStat);
    Sii9777FirmwareVersionQuery(sSii9777Inst, &versionStat);

    printf("\n"); 
    printf("Sii9777ChipIdStatusGet           : %08lX\n", chipIdStat); 
    printf("Sii9777ChipRevisionStatusGet     : %02X\n",  (int)chipRevStat); 
    printf("Sii9777ProductIdStatusGet        : %08lX\n", prodIdStat); 
    printf("Sii9777FirmwareVersionStatusGet  : %08lX\n", versionStat); 
}

/* Sii9777rx API Call-Back Handler */
static void sAdapterEventHandler(SiiInst_t inst, Sii9777EventFlags_t EventFlags)
{
    inst = inst;

    if( EventFlags & SII9777_EVENT_FLAGS__BOOT_DONE )
    {
        printf("New firmware restarted successfully.\n");
    }
    if( EventFlags & SII9777_EVENT_FLAGS__FLASH_DONE )
    {
        uint8_t  buf[PACKET_SIZE];
        int      len;

        if( sOperation == SII9777_FLASH_OPERATION__WRITE )
        {
            len = fread(buf, 1, PACKET_SIZE, spFile);
            if( len )
            {
            printf("Sii9777FirmwareUpdate  : %d\n", sBytesProg); 
            sBytesProg += len;
            Sii9777FlashUpdate(sSii9777Inst, buf, (uint16_t)len);
        }
        else
        {
            printf("Sii9777FirmwareRestart\n");
            Sii9777FirmwareRestart(sSii9777Inst);

            // Check if Sii9777rx has booted up
            if( sWaitForSii9777BootReady() )
            {
                // Enable call-back for all notifications
                sEnableCallBackNotifications(SII9777_EVENT_FLAGS__ALL);

                // Retrieve general product/revision info
                sLogVersionInformation();
            }
            else
            {
                printf("\nERROR: Sii9777 device could not be succesffully updated.\n");
                sbRunning = false;
            }
        }
    }
    }
    if( EventFlags & SII9777_EVENT_FLAGS__PLUS_5V_CHNG )      
    {
        bool_t bPlus5v;

        printf("SII9777_EVENT_FLAGS__PLUS_5V_CHNG\n");
        Sii9777Plus5vQuery(sSii9777Inst, SII9777_RX_PORT__0, &bPlus5v);
        printf("    Rx0 : %d\n", (bool_t)bPlus5v);
        Sii9777Plus5vQuery(sSii9777Inst, SII9777_RX_PORT__1, &bPlus5v);
        printf("    Rx1 : %d\n", bPlus5v);
        Sii9777Plus5vQuery(sSii9777Inst, SII9777_RX_PORT__2, &bPlus5v);
        printf("    Rx2 : %d\n", bPlus5v);
        Sii9777Plus5vQuery(sSii9777Inst, SII9777_RX_PORT__3, &bPlus5v);
        printf("    Rx3 : %d\n", bPlus5v);
    }
    if( EventFlags & SII9777_EVENT_FLAGS__EDID_CHNG )         
    {
        printf("SII9777_EVENT_FLAGS__EDID_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__HPD_CHNG )          
    {
        bool_t bHpd;

        printf("SII9777_EVENT_FLAGS__HPD_CHNG\n"); 
        Sii9777HpdQuery(sSii9777Inst, SII9777_RX_PORT__0, &bHpd);
        printf("    Rx0 : %d\n", bHpd);
        Sii9777HpdQuery(sSii9777Inst, SII9777_RX_PORT__1, &bHpd);
        printf("    Rx1 : %d\n", bHpd);
        Sii9777HpdQuery(sSii9777Inst, SII9777_RX_PORT__2, &bHpd);
        printf("    Rx2 : %d\n", bHpd);
        Sii9777HpdQuery(sSii9777Inst, SII9777_RX_PORT__3, &bHpd);
        printf("    Rx3 : %d\n", bHpd);
    }
    if( EventFlags & SII9777_EVENT_FLAGS__AV_LINK_CHNG )      
    {
        Sii9777AvLink_t avLink;

        printf("SII9777_EVENT_FLAGS__AV_LINK_CHNG\n"); 
        printf("    ");
        Sii9777AvLinkQuery(sSii9777Inst, &avLink);
        switch( avLink )
        {
            case SII9777_AV_LINK__NONE  : printf("NONE"); break;
            case SII9777_AV_LINK__DVI   : printf("DVI"); break;
            case SII9777_AV_LINK__HDMI1 : printf("HDMI1"); break;
            case SII9777_AV_LINK__HDMI2 : printf("HDMI2"); break;
            case SII9777_AV_LINK__MHL   : printf("MHL"); break;
        }
        printf("\n");
    }
    if( EventFlags & SII9777_EVENT_FLAGS__AV_MUTE_CHNG )      
    {
        bool_t bAvMute;

        printf("SII9777_EVENT_FLAGS__AV_MUTE_CHNG\n"); 
        Sii9777AvMuteQuery(sSii9777Inst, &bAvMute);
        printf("    %d\n", bAvMute);
    }
    if( EventFlags & SII9777_EVENT_FLAGS__VIDEO_TIMING_CHNG ) 
    {
        Sii9777VideoTiming_t videoTiming;

        printf("SII9777_EVENT_FLAGS__VIDEO_TIMING_CHNG\n"); 
        Sii9777VideoTimingQuery(sSii9777Inst, &videoTiming);
        printf("    interlaced   : %d\n", videoTiming.interlaced);
        printf("    posHsync     : %d\n", videoTiming.posHsync);
        printf("    posVsync     : %d\n", videoTiming.posVsync);
        printf("    pixelFreq    : %ld\n", videoTiming.pixelFreq);
        printf("    vTotal       : %d\n", videoTiming.vTotal);
        printf("    vActive      : %d\n", videoTiming.vActive);
        printf("    vSync        : %d\n", videoTiming.vSync);
        printf("    vFrontPorch  : %d\n", videoTiming.vFrontPorch);
        printf("    hTotal       : %d\n", videoTiming.hTotal);
        printf("    hActive      : %d\n", videoTiming.hActive);
        printf("    hSync        : %d\n", videoTiming.hSync);
        printf("    hFrontPorch  : %d\n", videoTiming.hFrontPorch);
    }
    if( EventFlags & SII9777_EVENT_FLAGS__VIDEO_FMT_CHNG )    
    {
        printf("SII9777_EVENT_FLAGS__VIDEO_FMT_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__AUDIO_FMT_CHNG )    
    {
        printf("SII9777_EVENT_FLAGS__AUDIO_FMT_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__ISRC_CHNG )         
    {
        printf("SII9777_EVENT_FLAGS__ISRC_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__ACP_CHNG )          
    {
        printf("SII9777_EVENT_FLAGS__ACP_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__SPD_CHNG )          
    {
        printf("SII9777_EVENT_FLAGS__SPD_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__HDCP_STREAM_CHNG )  
    {
        printf("SII9777_EVENT_FLAGS__HDCP_STREAM_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__HDCP_STATUS_CHNG )  
    {
        printf("SII9777_EVENT_FLAGS__HDCP_STATUS_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__CD_SENSE_CHNG )     
    {
        printf("SII9777_EVENT_FLAGS__CD_SENSE_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__MHL_VERSION_CHNG )  
    {
        printf("SII9777_EVENT_FLAGS__MHL_VERSION_CHNG\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__CBUS_EVENT )        
    {
        printf("SII9777_EVENT_FLAGS__CBUS_EVENT\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__VBUS_REQUEST )      
    {
        printf("SII9777_EVENT_FLAGS__VBUS_REQUEST\n"); 
    }
    if( EventFlags & SII9777_EVENT_FLAGS__DUAL_LINK_CHNG )    
    {
        printf("SII9777_EVENT_FLAGS__DUAL_LINK_CHNG\n"); 
    }
}

int main(int argc, char *argv[])
{
    printf("Copyright Silicon Image Inc, 2014\n");
    printf("Build Time: " __DATE__ "-" __TIME__ "\n");

    //-----------------------------------------------------------
    // System initialization
    //-----------------------------------------------------------
    // Initialize all system resources

    // (Insert your system initialization code here)
    if( !SiiHalAardvarkCreate() )
	{
        printf("\nERROR: I2c not available\n");
        exit(0);
	}

    /* Check if .bin was provided and available */
    sArgvHandler(argc, argv);
    if( spFileName != NULL )
    {
        if( ( sOperation == SII9777_FLASH_OPERATION__READ ) || ( sbServiceData == true) )
        {
            spFile = fopen(spFileName, "wb");
            if( !spFile )
            {
                printf("\nERROR: Cannot open file: %s\n", spFileName);
                fflush(stdout);
                exit(0);
            }
        }
        else
        {
            spFile = fopen(spFileName, "rb");
            if( !spFile )
            {
                printf("\nERROR: Cannot open file: %s\n", spFileName);
                fflush(stdout);
                exit(0);
            }
            else if( sTarget == SII9777_FLASH_TARGET__EDID_TABLES )
            {
                // check if content has at least 1024 bytes (4 * 256 bytes)
                fseek(spFile, 0, SEEK_END);
                if( ftell(spFile) < 1024 )
                {
                    fclose(spFile);
                    spFile = NULL;
                }
                else
                {
                    rewind(spFile);
                }
            }
        }
    }

    // API host constructor for Sii9777rx product
    sSii9777Inst = Sii9777Create(0, sAdapterEventHandler, &sSii9777Config);
    if( SII_INST_NULL == sSii9777Inst )
    {
        printf("\nERROR: Memory problem\n");
        exit(0);
	}

    // Check if Sii9777rx has booted up
    if( sWaitForSii9777BootReady() )
    {
        // Retrieve general product/revision info
        sLogVersionInformation();
    }
    else
    {
        if( !spFile )
        {
            printf("\nERROR: Boot failure. Use Sii9777rx_example <file.bin>[entre] to update flash.\n");
            exit(0);
        }
    }

    // Enable call-back for all notifications
    sEnableCallBackNotifications(SII9777_EVENT_FLAGS__ALL);

    if( spFile )
    {
        if( sbServiceData )
        {
            uint8_t buf[PACKET_SIZE];
            uint16_t len = 0;

            Sii9777ServiceModeSet(sSii9777Inst, true);
            
            do {
                len = Sii9777ServiceDataGet(sSii9777Inst, buf, PACKET_SIZE);
                if ( len )
                {
                    fwrite(buf, 1, len, spFile);
                }
            } while( len );

            if( spFile )
            {
                fclose(spFile);
            }

            Sii9777Delete(sSii9777Inst);

            SiiHalAardvarkDelete();

            exit(0);
        }
        else
        {
            if( sOperation == SII9777_FLASH_OPERATION__WRITE )
            {
                printf("\nErasing Flash...\n");
            }
            Sii9777FlashInit(sSii9777Inst, sOperation, sTarget);
            if( sOperation == SII9777_FLASH_OPERATION__READ )
            {
                uint8_t     buf[PACKET_SIZE];
                uint32_t    totalLen;

                if( sTarget == SII9777_FLASH_TARGET__EDID_TABLES )
                {
                    totalLen = 4 * 256; // 4 EDIDs
                }
                else
                {
                    totalLen = 0x80000; // flash total size
                }
                while( sBytesProg < totalLen )
                {
                    printf("Sii9777FirmwareUpdate  : %d\n", sBytesProg); 
                    sBytesProg += PACKET_SIZE;
                    Sii9777FlashUpdate(sSii9777Inst, buf, PACKET_SIZE);
                    fwrite(buf, 1, PACKET_SIZE, spFile);
                }
            }
        }
    }

    /* System infinite while loop */
    printf("\nSystem is running...\n");
    printf("\n(Press Ctrl-C to abort program.)\n");
    while( sbRunning )
    {
		if( SiiHalAardvarkInterruptQuery() )
		{
			Sii9777Handle(sSii9777Inst);
		}
        //-----------------------------------------------------------
        // Insert here other application handlers
        //-----------------------------------------------------------
    }
    printf("\nSystem is stopped\n");

    /* Close .bin file */
    if( spFile )
    {
        fclose(spFile);
    }

    // API host destructor for Sii9777rx product
    Sii9777Delete(sSii9777Inst);

    SiiHalAardvarkDelete();
    return 0;
}
