pub static TABLES:ErrorCorrectionTables = ErrorCorrectionTables::new();

macro_rules! wrap_gr_eq {
    ($calc:expr, $tresh_hold:expr) => {
        {
            let value = $calc;
            if value >= $tresh_hold {
                value - $tresh_hold
            }

            else {
                value
            }
        }        
    };
}

type EDC        = [u32; 256];
type GF8LOG     = [u8; 256];
type GF8ILOG    = [u8; 256];
type GF8PRODUCT = [[u16; 256]; 43];

pub struct ErrorCorrectionTables {
    pub edc:         EDC,
    pub gf8_log:     GF8LOG,
    pub gf8_ilog:    GF8ILOG,
    pub gf8_product: GF8PRODUCT
}

impl ErrorCorrectionTables {
    const fn new() -> ErrorCorrectionTables {
        let edc                 = Self::make_edc();
        let (gf8_log, gf8_ilog) = Self::make_gf_logs();
        let gf8_product         = Self::make_gf_product(&gf8_log, &gf8_ilog);

        ErrorCorrectionTables{edc, gf8_log, gf8_ilog, gf8_product}
    }

    const fn make_edc() -> EDC {
        let mut edc = [0; 256];

        let mut i = 0;
        while i < 256 {
            let mut x = i as u32;
            let mut j = 0;

            while j < 8 {
                let carry = x & 0b1;

                x >>= 1;
                if carry != 0 {
                    x ^= 0xD8018001;
                }
                j += 1;
            }

            edc[i] = x;
            i += 1;
        }
        
        edc
    }

    const fn make_gf_logs() -> (GF8LOG, GF8ILOG) {
        let mut gf8_log  = [0; 256];
        let mut gf8_ilog = [0; 256];

        gf8_log[0]    = 0;
        gf8_ilog[255] = 0;
        
        let mut x = 1u8;
        let mut i = 0u8;

        while i < 255 {
            gf8_log[x as usize]  = i;
            gf8_ilog[i as usize] = x;

            let carry_8bit = x & 0b1000_0000;
            x <<= 1;
            if carry_8bit != 0 {
                x ^= 0x1D;
            }

            i += 1;
        }

        (gf8_log, gf8_ilog)
    }

    const fn make_gf_product(gf8_log: &GF8LOG, gf8_ilog: &GF8ILOG) -> GF8PRODUCT {
        let mut gf8_product: GF8PRODUCT = [[0; 256]; 43];

        let mut j = 0;
        while j < 43 {
            let mut xx = gf8_ilog[44 - j];
            let mut yy = Self::subfunc(xx^1, 0x19, &gf8_log, &gf8_ilog);

            xx = Self::subfunc(xx, 0x1, &gf8_log, &gf8_ilog);
            xx = Self::subfunc(xx^1, 0x18, &gf8_log, &gf8_ilog);
            xx = gf8_log[xx as usize];
            yy = gf8_log[yy as usize];

            gf8_product[j][0] = 0;

            let mut i = 1;
            while i < 256 {
                let x = wrap_gr_eq!(xx as u16 + gf8_log[i] as u16, 0xFF) as usize;
                let y = wrap_gr_eq!(yy as u16 + gf8_log[i] as u16, 0xFF) as usize;

                gf8_product[j][i] = gf8_ilog[x] as u16 + ((gf8_ilog[y] as u16) << 8);

                i += 1;
            }

            j += 1;
        }

        gf8_product
    }

    const fn subfunc(mut a: u8, b: u8, gf8_log: &GF8LOG, gf8_ilog: &GF8ILOG) -> u8 {
        if a > 0 {
            let mut tmp_a:i32 = gf8_log[a as usize] as i32 - b as i32;
            if tmp_a < 0 {
                tmp_a += 0xFF;
            }

            a = gf8_ilog[tmp_a as usize];
        }

        a
    }
}
