#include "hardware.h"
#include "structs.h"



extern struct ui_state ui_state;
extern uint8_t g_shift;


void interrupt tc_int (void)
{
    uint32_t disp_register;
    int8_t i_shift;
    static uint8_t multiplex_sel = 0;
    uint8_t keys_tmp;
    static uint8_t keys_tmp_bck;
    static uint8_t debounce = 0;

    //prepare data to shift out to displays, : 2d3 2d2 2d1 2d0 1d3 1d2 1d1 1d0 DP2 G2 F2 E2 D2 C2 B2 A2 DP1 G1 F1 E1 D1 C1 B1 A1
    disp_register = 0xFFFFFF;
    if(multiplex_sel == 0)
        disp_register = ~((((uint32_t)0x1 << 16) & 0xFF0000) | ((~(uint32_t)ui_state.disp1_arr[0] << 8) & 0x00FF00) | (~(uint32_t)ui_state.disp1_arr[1] & 0x0000FF));
    if(multiplex_sel == 1)
        disp_register = ~((((uint32_t)0x1 << 17) & 0xFF0000) | ((~(uint32_t)ui_state.disp1_arr[2] << 8) & 0x00FF00) | (~(uint32_t)ui_state.disp1_arr[3] & 0x0000FF));
    if(multiplex_sel == 2)
        disp_register = ~((((uint32_t)0x1 << 18) & 0xFF0000) | ((~(uint32_t)ui_state.disp1_arr[4] << 8) & 0x00FF00) | (~(uint32_t)ui_state.disp1_arr[5] & 0x0000FF));
    if(multiplex_sel == 3)    
        disp_register = ~((((uint32_t)0x1 << 19) & 0xFF0000) | (~ui_state.leds & 0x00FFFF));
    if(multiplex_sel == 4)
        disp_register = ~((((uint32_t)0x1 << 20) & 0xFF0000) | ((~(uint32_t)ui_state.disp2_arr[0] << 8) & 0x00FF00) | (~(uint32_t)ui_state.disp2_arr[1] & 0x0000FF));
    if(multiplex_sel == 5)
        disp_register = ~((((uint32_t)0x1 << 21) & 0xFF0000) | ((~(uint32_t)ui_state.disp2_arr[2] << 8) & 0x00FF00) | (~(uint32_t)ui_state.disp2_arr[3] & 0x0000FF));
    if(multiplex_sel == 6)
        disp_register = ~((((uint32_t)0x1 << 22) & 0xFF0000) | ((~(uint32_t)ui_state.disp2_arr[4] << 8) & 0x00FF00) | (~(uint32_t)ui_state.disp2_arr[5] & 0x0000FF));
    if(multiplex_sel == 7)
        disp_register = ~((((uint32_t)0x1 << 23) & 0xFF0000) | ((~ui_state.leds >> 16) & 0x00FFFF));
    
    //shift data to display to external SIPO registers - from MSB to LSB
    for(i_shift = 23; i_shift >= 0; --i_shift)
    {
        PEXP_DATA_LAT = (disp_register >> i_shift) & 0x1;
        PEXP_CLK_LAT = 0x0;
        PEXP_CLK_LAT = 0x1;
    }
    PEXP_LATCH_LAT = 0x1;
    PEXP_LATCH_LAT = 0x0;
    
    //read switches
    keys_tmp_bck = keys_tmp;
    keys_tmp = 0x00;
    
    KEY_X0_LAT = 0x0;
    if(KEY_Y0_PORT == 0x0)
        keys_tmp |= 0x01;
    else if(KEY_Y1_PORT == 0x0)
        keys_tmp |= 0x02;
    KEY_X0_LAT = 0x1;
    
    KEY_X1_LAT = 0x0;
    if(KEY_Y0_PORT == 0x0)
        keys_tmp |= 0x04;
    else if(KEY_Y1_PORT == 0x0)
        keys_tmp |= 0x08;
    KEY_X1_LAT = 0x1;
    
    KEY_X2_LAT = 0x0;
    if(KEY_Y0_PORT == 0x0)
        keys_tmp |= 0x10;
    else if(KEY_Y1_PORT == 0x0)
        keys_tmp |= 0x20;
    KEY_X2_LAT = 0x1;
    
    KEY_X3_LAT = 0x0;
    if(KEY_Y0_PORT == 0x0)
        keys_tmp |= 0x40;
    else if(KEY_Y1_PORT == 0x0)
        keys_tmp |= 0x80;
    KEY_X3_LAT = 0x1;
    
    //keys debouncing
    if(keys_tmp == keys_tmp_bck)
        ++debounce;
    if(debounce > 3)
    {
        debounce = 0;
        ui_state.keys = keys_tmp;
    }
    
    //multiplexing - with every ISR send character to next display
    ++multiplex_sel;
    if(multiplex_sel >= 8)
        multiplex_sel = 0;
    
    INTCONbits.TMR0IF = 0x0;
}


void init(struct config *config)
{
    OSCCONbits.IRCF = 0xF;
    OSCCONbits.SCS = 0x02;
    
    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
    
    KEY_X0_TRIS = 0x0;
    KEY_X1_TRIS = 0x0;
    KEY_X2_TRIS = 0x0;
    KEY_X3_TRIS = 0x0;
    KEY_Y0_TRIS = 0x1;
    KEY_Y1_TRIS = 0x1;
    PEXP_LATCH_TRIS = 0x0;
    PEXP_OE_TRIS = 0x0;
    PEXP_DATA_TRIS = 0x0;
    PEXP_CLK_TRIS = 0x0;
    PEXP_OE_LAT = 0x0;
    INT_TRIS = 0x0;
//    INT_LAT = 0x1;
    
    TRISBbits.TRISB5 = 0x1;
    TRISBbits.TRISB7 = 0x0;
    TRISAbits.TRISA2 = 0x0;
    
    TXSTAbits.SYNC = 0x0;   //asynchronous
    TXSTAbits.BRGH = 0x1;   //fast mode
    SPBRG = 51;             //19230 bps
    TXSTAbits.TXEN = 0x1;
    
    RCSTAbits.SPEN = 0x1;   //serial enable
    RCSTAbits.CREN = 0x1;   //enable receiver
    
    leds_disp_format(ui_state.disp_format);
    leds_fq_sel(config->fq_sel);
    leds_amp_sel(config->amp_sel);
    
    OPTION_REGbits.TMR0CS = 0x0;
    OPTION_REGbits.PSA = 0x0;
    OPTION_REGbits.PS = 0x0;
    INTCONbits.GIE = 0x1;
    INTCONbits.PEIE = 0x1;
    INTCONbits.TMR0IE = 0x1;
    
}


void get_result(struct results *results)
{
    PIR1bits.RCIF = 0x0;                //clear interrupt flag indicating that something is received (... but maybe no need, it probably is cleared by reading RCREG)
    results->z_real = get_res_double();
    results->z_imag = get_res_double();
    results->y_real = get_res_double();
    results->y_imag = get_res_double();
    results->z_mod = get_res_double();
    results->z_arg = get_res_double();
    results->y_mod = get_res_double();
    results->y_arg = get_res_double();
}


double get_res_double(void)                     //get double variable through UART as a series of bits
{                                               //WARNING - set 32bit double size in options - otherwise it will be wrong (24bit)
    int32_t i_shift;
    int32_t temp_int32;                         //float 32bit is coded on int32_t
    const int8_t var_size = 4;                  //float sent in 4 chunks
    
    for(i_shift = 0, temp_int32 = 0x00000000; i_shift < var_size; ++i_shift)
    {
        INT_LAT = 0x0;                          //set INT_LAT low to confirm ui is ready for data
        while(!PIR1bits.RCIF);
        INT_LAT = 0x1;                          //immediately after getting byte set INT_LAT high to not allow sending next bytes until ui is ready again
        temp_int32 |= (int32_t)RCREG << (8 * i_shift);
        PIR1bits.RCIF = 0x0;
    }
    
    return *((double*)(&temp_int32));
}
            
        
void send_config(struct config *config)
{
    while(!TXSTAbits.TRMT);
    TXREG = (uint8_t)((config->fq_sel << 0) | (config->amp_sel << 2) | (config->do_the_cal << 4) | (config->comp_open << 5) | (config->comp_short << 6) | (config->turn_off << 7));
}



void leds_disp_format(int8_t disp_format)
{
    static const uint8_t on = 1;
    static const uint8_t off = 0;
    
    D22(off);
    D20(off);
    D6(off);
    D4(off);
    D23(off);
    D24(off);
    D7(off);
    D8(off);
    D21(off);
    D19(off);
    D5(off);
    D3(off);
    switch (disp_format)
    {
    case 0:
        D22(on);
        D20(on);
        break;
    case 1:
        D6(on);
        D4(on);
        break;
    case 2:
        D23(on);
        D24(on);
        break;
    case 3:
        D7(on);
        D8(on);
        break;
    case 4:
        D21(on);
        D19(on);
        break;
    case 5:
        D5(on);
        D3(on);
        break;
    }
}

void leds_fq_sel(uint8_t fq_sel)
{
    static const uint8_t on = 1;
    static const uint8_t off = 0;

    D1(off);
    D17(off);
    D2(off);
    D18(off);
    switch (fq_sel)
    {
    case 0x0:
        D1(on);
        break;
    case 0x1:
        D17(on);
        break;
    case 0x2:
        D2(on);
        break;
    case 0x3:
        D18(on);
        break;
    }
}

void leds_amp_sel(uint8_t amp_sel)
{
    static const uint8_t on = 1;
    static const uint8_t off = 0;

    D14(off);
    D30(off);
    D15(off);
    switch (amp_sel)
    {
    case 0x0:
        D14(on);
        break;
    case 0x1:
        D30(on);
        break;
    case 0x2:
        D15(on);
        break;
    }
}

void leds_shift()
{
    static const uint8_t on = 1;
    static const uint8_t off = 0;

    if(g_shift == 0x1)
        D31(on);
    else
        D31(off);
}
