// Copyright (c) 2016, XMOS Ltd, All rights reserved
#include <xs1.h>
#include <platform.h>


//port SPI_MOSI  =   XS1_PORT_1D;  // e.g., XS1_PORT_1D
//port  SPI_MISO =   XS1_PORT_1A;  // e.g., XS1_PORT_1A
//port SPI_CLK =    XS1_PORT_1C;    // e.g., XS1_PORT_1C
//port SPI_SS   =   XS1_PORT_1B;     // e.g., XS1_PORT_1B



#if defined(__XS2A__)
/* Note range 0x7FFC8 - 0x7FFFF guarenteed to be untouched by tools */
#warning Building xCORE-200 compatible loader
#define FLAG_ADDRESS 0x7ffcc
#else
/* Note range 0xFFFC8 - 0xFFFFF guarenteed to be untouched by tools */
#warning Building xcore.ai compatible loader
#define FLAG_ADDRESS 0xfffcc
#endif

/* Store Flag to fixed address */
void SetRoleSwitchFlag(unsigned x)
{
    asm volatile("stw %0, %1[0]" :: "r"(x), "r"(FLAG_ADDRESS));
}

/* Load flag from fixed address */
unsigned GetRoleSwitchFlag()
{
    unsigned x;
    asm volatile("ldw %0, %1[0]" : "=r"(x) : "r"(FLAG_ADDRESS));
    return x;
}


void delay_cycles(int count) {
    for (int i = 0; i < count; i++) {
        asm volatile (""); // Prevent compiler removal.
    }
}

/*
 * spi_transact_32_full: Sends 32 bits out on MOSI and simultaneously reads 32 bits from MISO.
 *
 * Parameters:
 *   unsigned data - The 32-bit word to send over MOSI.
 *
 * Returns:
 *   The 32-bit word received from MISO.
 *
 * Assumptions:
 *   - The chip-select (SS) is already asserted (active low).
 *   - The SPI_CLK, SPI_MOSI, and SPI_MISO ports have been correctly defined in the XN file.
 *   - The transfer is LSB-first; adjust shifting/ordering if your device expects MSB-first.
 */
//unsigned spi_transact_32_full(unsigned data) {
//    unsigned received = 0;
//    for (int bit = 0; bit < 32; bit++) {
//        // Drive clock low.
//        SPI_CLK <: 0;
//        delay_cycles(5);
//
//        // Set MOSI line according to the current LSB of data.
//        if (data & 0x1)
//            SPI_MOSI <: 1;
//        else
//            SPI_MOSI <: 0;
//        delay_cycles(5);
//
//        // Raise clock; the rising edge clocks both the outgoing bit and makes the incoming bit valid.
//        SPI_CLK <: 1;
//        delay_cycles(5);
//
//        // Read the corresponding MISO bit.
//        int bit_val = 0; 
//        SPI_MISO :> bit_val;
//        bit_val &= 0x1;
//
//        received |= (bit_val << bit);
//
//        // Prepare the next bit to be transmitted.
//        data >>= 1;
//    }
//    // Ensure the clock ends in a low state.
//    SPI_CLK <: 0;
//    return received;
//}


/* Port for button on xk-audio-316 board. */
in port p_button                            = XS1_PORT_4E;
out port port_audio_config                  = XS1_PORT_8D;


/* Enum for representing button state. */
enum button_val
{
    MODE_STEREO,
    MODE_71_GAME,
    MODE_TOTAL
};
/* Store the button position. */
int buttonPosition;
/* Enum for representing the potential interest in the image. */
enum interest
{
    NOT_INTERESTED = 0,
    INTERESTED = 1
};

#define SWITCH_TO_STEREO_DNR_OFF  0x01
#define SWITCH_TO_STEREO_DNR_ON  0x02
#define SWITCH_TO_71_DNR_OFF  0x03
#define SWITCH_TO_71_DNR_ON  0x04

int get_ids() {
    // TODO, consider just bit bashing SPI at a low speed to just get the darn FlashIdNum!
    int success;
    // success = fl_connect(SPI);
    // Check success
  
    // int identifier = fl_getFlashIdNum();
  
    // unsigned char idStr[MAX_LEN];
    // success = fl_getFlashIdStr(idStr, MAX_LEN);
    // Check success

//    success = spi_transact_32_full(0x9F);
//    success = spi_transact_32_full(0x9F);
//    success = spi_transact_32_full(0x9F);


    return success;

  }

/* Store the version of the image in memory that will potentially be booted. */
int candidateImageVersion = -1;
/* Store the address of the image in memory that will potentially be booted. */
unsigned candidateImageAddress;
void init(void)
{
    //port_audio_config <: 0b00100000;
    delay_milliseconds(10);
    unsigned buttonVal;
/* Read state of button. */
    //buttonVal=get_ids();//p_button :> buttonVal;
    buttonVal = GetRoleSwitchFlag();
    if (buttonVal == 0) {
        buttonPosition = MODE_TOTAL;
    } else {
        if ( (buttonVal == SWITCH_TO_STEREO_DNR_OFF) || (buttonVal == SWITCH_TO_STEREO_DNR_ON)) {
            buttonPosition = MODE_STEREO;
        } else {
            buttonPosition = MODE_71_GAME;
        }
    }
    

#if 0
/* Button is up. */
    if((buttonVal) > 1) 
    {
        buttonPosition = MODE_STEREO;
    }
/* Button is down. */
    else
    {
        buttonPosition = MODE_71_GAME;
    }
#endif
}
extern int checkCandidateImageVersion(int imageVersion)
{
/* If the button is up and imageVersion is even and imageVersion is higher */
/* than the last candidateImageVersion then this is a potential version */
/* for booting. */
    if (buttonPosition == MODE_TOTAL) return INTERESTED;
    if ((buttonPosition == MODE_71_GAME) &&
    ((imageVersion % 2) == 0) /*&&*/
    /*(imageVersion > candidateImageVersion)*/)
    {
        return INTERESTED;
    }
/* If the button is down and imageVersion is odd and imageVersion is higher */
/* than the last candidateImageVersion then this is a potential version */
/* for booting. */
    else if ((buttonPosition == MODE_STEREO) &&
    ((imageVersion % 2) != 0) /*&&*/
    /*(imageVersion > candidateImageVersion)*/)
    {
        return INTERESTED;
    }
/* Not a potential firmware image in all other cases. */
    return NOT_INTERESTED;
}
extern void recordCandidateImage(int imageVersion, unsigned imageAddress)
{
/* Save the imageVersion that we are interested in. */
    candidateImageVersion = imageVersion;
/* Save the imageAddress of the imageVersion that we are interested in. */
    candidateImageAddress = imageAddress;
}
extern unsigned reportSelectedImage(void)
{
/* Return the candidateImageAddress of the image that we are interested in. */
    return candidateImageAddress;
}

