use std::hash::Hash;
use minimum_redundancy::{BitsPerFragment, DecodingResult, entropy_to_bpf, Frequencies, ValueSize};
use std::collections::HashMap;
use std::borrow::Borrow;
use std::io::{Read, Write};
use std::io;
use std::convert::TryInto;
use crate::coding::{BuildCoding, Coding, Decoder, SerializableCoding};
use super::U8Code;

impl From<minimum_redundancy::Code> for U8Code {
    fn from(c: minimum_redundancy::Code) -> Self {
        Self { content: c.content.try_into().unwrap(), len: c.len.try_into().unwrap() }
    }
}

impl<'d, DecodedType> Decoder for minimum_redundancy::Decoder<'d, DecodedType, BitsPerFragment> {
    type Value = DecodedType;
    type Decoded = &'d DecodedType;

    #[inline(always)] fn consume_checked(&mut self, fragment: u8) -> DecodingResult<Self::Decoded> {
        Self::consume_checked(self, fragment as _)
    }

    #[inline(always)] fn consume(&mut self, fragment: u8) -> DecodingResult<Self::Decoded> {
        Self::consume(self, fragment as _)
    }
}

impl<Value: Hash + Eq + Clone> Coding for minimum_redundancy::Coding<Value, BitsPerFragment> {
    type Value = Value;
    type Decoder<'d> = minimum_redundancy::Decoder<'d, Value, BitsPerFragment> where Value: 'd;
    type Encoder<'e> = HashMap<&'e Value, U8Code> where Value: 'e;
    type Codeword = U8Code;

    #[inline(always)] fn bits_per_fragment(&self) -> u8 {
        self.degree.0
    }

    #[inline(always)] fn decoder(&self) -> Self::Decoder<'_> {
        Self::decoder(self)
    }

    #[inline(always)] fn encoder(&self) -> Self::Encoder<'_> {
        self.codes().map(|(v, c)| (v, c.into())).collect()
    }

    #[inline(always)] fn len_of(&self, code: U8Code) -> u8 { code.len }

    fn fragment_of(&self, code: Self::Codeword, index: u8) -> u8 {
        self.rev_fragment_of(code, code.len-index-1)
    }

    fn rev_fragment_of(&self, code: Self::Codeword, index: u8) -> u8 {
        let bpf = self.bits_per_fragment();
        code.content.checked_shr(bpf as u32 * index as u32).map_or(0, |v| v & ((1u16 << bpf) - 1) as u8)
    }

    #[inline(always)] fn remove_first_fragment_of(&self, code: &mut U8Code) -> bool {
        code.len -= 1;
        code.len == 0
    }

    fn code_of<'e, Q>(&self, encoder: &Self::Encoder<'e>, to_encode: &Q) -> Self::Codeword where Q: Borrow<Self::Value> {
        encoder[to_encode.borrow()]
    }
}

impl<Value: Hash + Eq + Clone> SerializableCoding for minimum_redundancy::Coding<Value, BitsPerFragment> {
    fn write_bytes(&self, bytes_per_value: usize) -> usize {
        minimum_redundancy::Coding::<Value, BitsPerFragment>::write_size_bytes(self, ValueSize::Const(bytes_per_value))
    }

    fn write<F>(&self, output: &mut dyn Write, write_value: F) -> io::Result<()> where F: FnMut(&mut dyn Write, &Self::Value) -> io::Result<()> {
        minimum_redundancy::Coding::<Value, BitsPerFragment>::write(self, output, write_value)
    }

    fn read<F>(input: &mut dyn Read, read_value: F) -> io::Result<Self> where F: FnMut(&mut dyn Read) -> io::Result<Self::Value>, Self: Sized {
        minimum_redundancy::Coding::read(input, read_value)
    }
}

#[derive(Default, Copy, Clone)]
pub struct BuildMinimumRedundancy {
    pub bits_per_fragment: u8
}

impl<Value: Hash + Eq + Clone> BuildCoding<Value> for BuildMinimumRedundancy {
    type Coding = minimum_redundancy::Coding<Value>;

    fn name(&self) -> String {
        return if self.bits_per_fragment == 0 {
            "minimum_redundancy".to_owned()
        } else {
            format!("minimum_redundancy_b{}", self.bits_per_fragment)
        }
    }

    fn build_from_iter<Iter>(&self, iter: Iter, mut bits_per_fragment: u8) -> Self::Coding
        where Iter: IntoIterator, Iter::Item: Borrow<<Self::Coding as Coding>::Value>
    {
        if bits_per_fragment == 0 { bits_per_fragment = self.bits_per_fragment; }
        let freq = HashMap::<Value, u32>::with_occurrences_of(iter);
        if bits_per_fragment == 0 { bits_per_fragment = entropy_to_bpf(freq.entropy()-0.2) }
        Self::Coding::from_frequencies(BitsPerFragment(bits_per_fragment), freq)
    }
}
