use std::io::Read;

use crate::protocol::{ProtocolReader, ProtocolReaderResult};

pub fn decode_tile<R: Read>(
    data: &mut [u8; 256], 
    quality: u8, 
    reader: &mut ProtocolReader<R>
) -> ProtocolReaderResult<()> {
    let mut coefs = [0_i32; 256];

    for _ in 0..quality {
        let ix = reader.read_u8()?;
        let val = reader.read_u16()? as i32;
        coefs[ix as usize] = val;
    }

    let mut transform = |zero: usize, stride: usize| {
        let mut ixs = [0; 16];
        ixs[0] = zero;
        for i in 1..16 { ixs[i] = ixs[i - 1] + stride; }

        for bit in [8, 4, 2, 1] {
            for i in 0..16 {
                if i & bit == 0 {
                    let j = i | bit;

                    let ival = coefs[ixs[i]];
                    let jval = coefs[ixs[j]];

                    coefs[ixs[i]] = ival.wrapping_add(jval);
                    coefs[ixs[j]] = ival.wrapping_sub(jval);
                }
            }
        }

        for i in 0..16 {
            coefs[ixs[i]] /= 16;
        }
    };

    for row in 0..16 { transform(row * 16, 1); }
    for col in 0..16 { transform(col, 16); }

    for i in 0..256 {
        data[i] = coefs[i].max(0).min(255) as u8;
    }
    Ok(())
}

pub fn decode_value(enc: u8) -> i32 {
    let sign = enc & 0x80 != 0;
    let exponent = (enc >> 4) & 0x7;
    let mantissa = enc & 0xf;

    let magnitude = ((((mantissa as i32) << 1) | 0b100001) << exponent) - 33;
    if sign { return -magnitude * 16 }
    return magnitude * 16;
}