use std::io::Write;

use crate::protocol::{ProtocolWriter, ProtocolWriterResult};

pub fn encode_tile<W: Write>(
    data: [u8; 256], 
    quality: u8, 
    writer: &mut ProtocolWriter<W>
) -> ProtocolWriterResult<()> {
    let mut coefs: [i32; 256] = [0; 256];

    for i in 0..256 {
        coefs[i] = data[i] as i32;
    }

    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 col in 0..16 {
        transform(col, 16);
    }

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

    // this operation can be efficiently implemented as a heapsort in C
    // but the code is ugly
    // so just use Rust's default sort
    let mut indices = [0; 256];
    for i in 0..256 {
        indices[i] = i;
    }
    indices.sort_by_key(|ix| -(coefs[*ix]).abs());

    // write indices and values
    // (note that the first index will almost always be zero)
    for i in 0..quality {
        let ix = indices[i as usize];
        writer.write_u8(ix as u8)?;
        writer.write_u16(encode_value(coefs[ix]))?;
    };
    Ok(())
}

pub fn encode_value(value: i32) -> u16 {
    return ((value / 16) as i16) as u16;
    /*
    // quantize
    let value = value / 32;

    // mu law
    let sign = value < 0;
    let mut rescaled = value.abs() + 33;
    let mut exponent = 0;

    while rescaled > 64 {
        exponent += 1;
        rescaled >>= 1;
    }
    assert!(exponent <= 7);

    let mantissa = (rescaled >> 1) & 0xf;

    return (
        if sign { 128 } else { 0 } |
        exponent << 4 |
        mantissa
    ) as u8;
}

#[cfg(test)]
mod test {
    use quickcheck_macros::quickcheck;

    use crate::decode_tile::decode_value;

    use super::encode_value;

    #[quickcheck]
    fn test_encode_value(val: u16) -> bool {
        let val = (val & (0x8000 | 0x3fff)) as i16;
        let canon = decode_value(encode_value(val as i32));

        canon == decode_value(encode_value(canon as i32))
    }
    */
}