#include "processing.h"

extern struct ui_state ui_state;


void display(struct results *results, struct config *config)
{
    static struct hys_buf_struct hys_buf[2];                                    //to implement hysteresis buffers are needed to keep hystory of both calls of functions (two displays keeping their histories)
    static int8_t last_disp_format;
    
    //clear hysteresis when display format changes
    if(last_disp_format != ui_state.disp_format)
        hys_buf[0].ext_last = hys_buf[1].ext_last = hys_buf[0].hys = hys_buf[1].hys = 0;
    last_disp_format = ui_state.disp_format;
    
    //depending of selected form of displaying impedance (like L+Q, R+X...) prepare data to display
    switch(ui_state.disp_format)                                                //selecting display mode; casting double (set to 32bit) to float (set to 24bit) is to speedup the uC
    {
    case 0:
        float_to_bcd(-1 / (2 * PI * FREQ(config->fq_sel) * (float)results->z_imag), ui_state.disp1_arr, &ui_state.suffix1, hys_buf+0);    //capacitance
        float_to_bcd(-(float)results->z_real / (float)results->z_imag, ui_state.disp2_arr, &ui_state.suffix2, hys_buf+1);                       //dissipation factor
        break;
    case 1:
        float_to_bcd((float)results->z_imag / (2 * PI * FREQ(config->fq_sel)), ui_state.disp1_arr, &ui_state.suffix1, hys_buf+0);        //inductance
        float_to_bcd((float)results->z_imag / (float)results->z_real, ui_state.disp2_arr, &ui_state.suffix2, hys_buf+1);                       //quality factor
        break;
    case 2:
        float_to_bcd((float)results->z_real, ui_state.disp1_arr, &ui_state.suffix1, hys_buf+0);                                         //resistance
        float_to_bcd((float)results->z_imag, ui_state.disp2_arr, &ui_state.suffix2, hys_buf+1);                                         //reactance
        break;
    case 3:
        float_to_bcd((float)results->y_real, ui_state.disp1_arr, &ui_state.suffix1, hys_buf+0);                                         //conductance
        float_to_bcd((float)results->y_imag, ui_state.disp2_arr, &ui_state.suffix2, hys_buf+1);                                         //susceptance
        break;
    case 4:
        float_to_bcd((float)results->z_mod, ui_state.disp1_arr, &ui_state.suffix1, hys_buf+0);                                          //impedance
        float_to_bcd((float)results->z_arg, ui_state.disp2_arr, &ui_state.suffix2, hys_buf+1);                                          //angle
        break;
    case 5:
        float_to_bcd((float)results->y_mod, ui_state.disp1_arr, &ui_state.suffix1, hys_buf+0);                                          //admittance
        float_to_bcd((float)results->y_arg, ui_state.disp2_arr, &ui_state.suffix2, hys_buf+1);                                          //angle
        break;
    }
    
    //display suffixes on LEDs (like kilo, mili, micro...)
    suffix_to_leds(ui_state.suffix1, 1);
    suffix_to_leds(ui_state.suffix2, 2);
}


void float_to_bcd(float float_num, uint8_t *disp_pt, char *suffix, struct hys_buf_struct *hys_buf)
{
    uint8_t ext;
    uint32_t int_num;
    uint8_t i_7s;
    uint8_t dp_pos;
    uint8_t disp_arr[6];
    
    //display '-' if float is negative and remove the sign
    if(float_num < 0)
    {
        float_num = -float_num;
        disp_arr[5] = '-';
    }
    else
    {
        disp_arr[5] = ' ';
    }
    
    //multiply float get large value aligned to left (between 999999999 and 100000000 and cast to fixed point
    for(ext = 0; (float_num * 10 < 999999999) && (ext < 18); float_num *= 10)
        ++ext;
    int_num = (uint32_t)float_num;
    
    //display hysteresis - this is to avoid too frequent jumps of decimal point
    if((ext == hys_buf->ext_last + 1) && (int_num >= 900000000))                    //hysteresis when result changes from 10*10^n to 9.x*10^(n-1) - the result is that decimal point stays on the same position
        hys_buf->hys = 1;
    else if((ext != hys_buf->ext_last) || (int_num < 900000000))                    //reset hysteresis
        hys_buf->hys = 0;
    hys_buf->ext_last = ext;
    if(hys_buf->hys == 1)
        --ext;
    disp_arr[4] = ' ';                                                          //most significant position stays empty when hysteresis active
    
    //decode fixed point value to array of BCD numbers
    disp_arr[0] = (int_num % 100000) / 10000;
    disp_arr[1 - hys_buf->hys] = ((int_num - disp_arr[0]) % 1000000) / 100000;
    disp_arr[2 - hys_buf->hys] = ((int_num - disp_arr[1]) % 10000000) / 1000000;
    disp_arr[3 - hys_buf->hys] = ((int_num - disp_arr[2]) % 100000000) / 10000000;
    disp_arr[4 - hys_buf->hys] = ((int_num - disp_arr[3]) % 1000000000) / 100000000;
    
    //depending of how much float was extended before casting to fixed point, select suffix and position of decimal point
    switch(ext)                                             
    {
    case 0:
        dp_pos = 2;
        *suffix = 'M';
        break;
    case 1:
        dp_pos = 3;
        *suffix = 'M';
        break;
    case 2:
        dp_pos = 4;
        *suffix = 'M';
        break;
    case 3:
        dp_pos = 2;
        *suffix = 'k';
        break;
    case 4:
        dp_pos = 3;
        *suffix = 'k';
        break;
    case 5:
        dp_pos = 4;
        *suffix = 'k';
        break;
    case 6:
        dp_pos = 2;
        *suffix = ' ';
        break;
    case 7:
        dp_pos = 3;
        *suffix = ' ';
        break;
    case 8:
        dp_pos = 4;
        *suffix = ' ';
        break;
    case 9:
        dp_pos = 2;
        *suffix = 'm';
        break;
    case 10:
        dp_pos = 3;
        *suffix = 'm';
        break;
    case 11:
        dp_pos = 4;
        *suffix = 'm';
        break;
    case 12:
        dp_pos = 2;
        *suffix = 'u';
        break;
    case 13:
        dp_pos = 3;
        *suffix = 'u';
        break;
    case 14:
        dp_pos = 4;
        *suffix = 'u';
        break;
    case 15:
        dp_pos = 2;
        *suffix = 'n';
        break;
    case 16:
        dp_pos = 3;
        *suffix = 'n';
        break;
    case 17:
        dp_pos = 4;
        *suffix = 'n';
        break;
    case 18:
        dp_pos = 2;
        *suffix = 'p';
        break;
    case 19:
        dp_pos = 3;
        *suffix = 'p';
        break;
    case 20:
        dp_pos = 4;
        *suffix = 'p';
        break;
    }
    
    //decode array of characters to 7-segments display format ".GFEDCBA"
    for(i_7s = 0; i_7s < 6; ++i_7s)
        disp_pt[i_7s] = char_to_7seg(disp_arr[i_7s]);   //process every digit
    disp_pt[dp_pos] |= 0x80;                                    //put dot in appropriate place    
}


uint8_t char_to_7seg(uint8_t seg_char)
{
    uint8_t seg_decoded;
    switch(seg_char)
    {
    case '-':
        seg_decoded = 0x40;
        break;
    case ' ':
        seg_decoded = 0x00;
        break;
    case 0:
        seg_decoded = 0x3F;
        break;
    case 1:
        seg_decoded = 0x06;
        break;
    case 2:
        seg_decoded = 0x5B;
        break;
    case 3:
        seg_decoded = 0x4F;
        break;
    case 4:
        seg_decoded = 0x66;
        break;
    case 5:
        seg_decoded = 0x6D;
        break;
    case 6:
        seg_decoded = 0x7D;
        break;
    case 7:
        seg_decoded = 0x07;
        break;
    case 8:
        seg_decoded = 0x7F;
        break;
    case 9:
        seg_decoded = 0x6F;
        break;
    default:
        seg_decoded = 0x3F;
    }
    return seg_decoded;
}


suffix_to_leds(char suffix, uint8_t disp_nr)
{
    uint8_t off = 0;
    uint8_t on = 1;
    
    if(disp_nr == 1)
    {
        D25(off);
        D9(off);
        D26(off);
        D10(off);
        D27(off);
        D11(off);
        switch(suffix)
        {
        case 'M':
            D25(on);
            break;
        case 'k':
            D9(on);
            break;
        case 'm':
            D26(on);
            break;
        case 'u':
            D10(on);
            break;
        case 'n':
            D27(on);
            break;
        case 'p':
            D11(on);
            break;
        }
    }
    if(disp_nr == 2)
    {
        D32(0);
        D16(0);
        D28(0);
        D12(0);
        D29(0);
        D13(0);
        switch(suffix)
        {
        case 'M':
            D32(on);
            break;
        case 'k':
            D16(on);
            break;
        case 'm':
            D28(on);
            break;
        case 'u':
            D12(on);
            break;
        case 'n':
            D29(on);
            break;
        case 'p':
            D13(on);
            break;
        }
    }
}
