use std::{
    collections::HashSet,
    convert::TryFrom,
    fmt,
    io::{self, Read, Write},
};

use serde::{Deserialize, Serialize};

use crate::{
    io::{LenBm, ReadFrom, TooLongError, WriteTo},
    var_type::{VarInt, VarIntList},
};

/// A stream number, which is the identification number
/// of the Bitmessage network cluster.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct StreamNumber(u32);

impl fmt::Display for StreamNumber {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl StreamNumber {
    /// Constructs a stream number from a value.
    pub fn new(value: u32) -> Self {
        Self(value)
    }

    /// Returns the value as `u32`.
    pub fn as_u32(self) -> u32 {
        self.0
    }
}

impl From<u32> for StreamNumber {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ConvertStreamNumberError {
    TooLarge { max: u64, value: u64 },
}

impl fmt::Display for ConvertStreamNumberError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::TooLarge { max, value } => {
                write!(f, "stream number must be <={}, but {}", max, value)
            }
        }
    }
}

impl std::error::Error for ConvertStreamNumberError {}

impl TryFrom<VarInt> for StreamNumber {
    type Error = ConvertStreamNumberError;

    fn try_from(vi: VarInt) -> Result<Self, <Self as TryFrom<VarInt>>::Error> {
        if vi.as_u64() <= 0xffff_ffff {
            Ok((vi.as_u64() as u32).into())
        } else {
            Err(Self::Error::TooLarge {
                max: 0xffff_ffff,
                value: vi.as_u64(),
            })
        }
    }
}

impl From<StreamNumber> for VarInt {
    fn from(sn: StreamNumber) -> Self {
        (sn.as_u32() as u64).into()
    }
}

impl WriteTo for StreamNumber {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        let v = VarInt::from(*self);
        v.write_to(w)
    }
}

impl ReadFrom for StreamNumber {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        let v = VarInt::read_from(r)?;
        match Self::try_from(v) {
            Ok(sn) => Ok(sn),
            Err(err) => Err(io::Error::new(io::ErrorKind::Other, err)),
        }
    }
}

impl LenBm for StreamNumber {
    fn len_bm(&self) -> usize {
        let v = VarInt::from(*self);
        v.len_bm()
    }
}

#[test]
fn test_stream_number_write_to() {
    let test: StreamNumber = 0xcd_u32.into();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [0xcd];
    assert_eq!(bytes, expected);

    let test: StreamNumber = 0xfe_u32.into();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [0xfd, 0x00, 0xfe];
    assert_eq!(bytes, expected);

    let test: StreamNumber = 0xcdef_u32.into();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [0xfd, 0xcd, 0xef];
    assert_eq!(bytes, expected);

    let test: StreamNumber = 0x89ab_cdef_u32.into();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [0xfe, 0x89, 0xab, 0xcd, 0xef];
    assert_eq!(bytes, expected);
}

#[test]
fn test_stream_number_read_from() {
    use std::io::Cursor;

    let mut bytes = Cursor::new([0xcd]);
    let test = StreamNumber::read_from(&mut bytes).unwrap();
    let expected: StreamNumber = 0xcd_u32.into();
    assert_eq!(test, expected);

    let mut bytes = Cursor::new([0xfd, 0x00, 0xfe]);
    let test = StreamNumber::read_from(&mut bytes).unwrap();
    let expected: StreamNumber = 0xfe_u32.into();
    assert_eq!(test, expected);

    let mut bytes = Cursor::new([0xfe, 0x89, 0xab, 0xcd, 0xef]);
    let test = StreamNumber::read_from(&mut bytes).unwrap();
    let expected: StreamNumber = 0x89ab_cdef_u32.into();
    assert_eq!(test, expected);
}

#[test]
#[should_panic]
fn test_stream_number_error_u16() {
    use std::io::Cursor;

    let mut bytes = Cursor::new([0xfd, 0x00, 0xab]);
    let test = StreamNumber::read_from(&mut bytes).unwrap();
    let expected: StreamNumber = 0x00ab_u32.into();
    assert_eq!(test, expected);
}

#[test]
#[should_panic]
fn test_stream_number_error_u32() {
    use std::io::Cursor;

    let mut bytes = Cursor::new([0xfe, 0x00, 0x00, 0xcd, 0xef]);
    let test = StreamNumber::read_from(&mut bytes).unwrap();
    let expected: StreamNumber = 0x0000_cdef_u32.into();
    assert_eq!(test, expected);
}

/// A list of stream numbers.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct StreamNumbers(Vec<StreamNumber>);

impl AsRef<[StreamNumber]> for StreamNumbers {
    fn as_ref(&self) -> &[StreamNumber] {
        &self.0
    }
}

impl StreamNumbers {
    /// Constructs a stream numbers object from a list of stream numbers.
    pub fn new(list: Vec<StreamNumber>) -> Self {
        Self(list)
    }

    /// Returns true if the list contains the given stream number.
    pub fn contains(&self, x: StreamNumber) -> bool {
        self.0.contains(&x)
    }

    /// Returns the intersection of two list of stream numbers.
    /// The result list is unique and sorted.
    pub fn intersection(&self, other: &Self) -> Self {
        let a: HashSet<StreamNumber> = self.0.iter().cloned().collect();
        let b: HashSet<StreamNumber> = other.0.iter().cloned().collect();
        let c = a.intersection(&b);
        let mut v: Vec<StreamNumber> = c.cloned().collect();
        v.sort_unstable();
        v.into()
    }
}

impl From<Vec<StreamNumber>> for StreamNumbers {
    fn from(values: Vec<StreamNumber>) -> Self {
        Self(values)
    }
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ConvertStreamNumbersError {
    InvalidStreamNumber(ConvertStreamNumberError),
}

impl fmt::Display for ConvertStreamNumbersError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidStreamNumber(err) => err.fmt(f),
        }
    }
}

impl std::error::Error for ConvertStreamNumbersError {}

impl TryFrom<VarIntList> for StreamNumbers {
    type Error = ConvertStreamNumbersError;

    fn try_from(vil: VarIntList) -> Result<Self, <Self as TryFrom<VarIntList>>::Error> {
        let mut vec: Vec<StreamNumber> = Vec::with_capacity(vil.as_ref().len());
        for vi in vil.as_ref() {
            match StreamNumber::try_from(*vi) {
                Ok(sn) => {
                    vec.push(sn);
                }
                Err(err) => return Err(Self::Error::InvalidStreamNumber(err)),
            }
        }
        Ok(vec.into())
    }
}

impl From<StreamNumbers> for VarIntList {
    fn from(sns: StreamNumbers) -> Self {
        let mut vec: Vec<VarInt> = Vec::with_capacity(sns.as_ref().len());
        for sn in sns.as_ref() {
            vec.push((sn.as_u32() as u64).into());
        }
        vec.into()
    }
}

impl WriteTo for StreamNumbers {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        VarInt::from(self.0.len() as u64).write_to(w)?;
        for value in &self.0 {
            value.write_to(w)?
        }
        Ok(())
    }
}

impl ReadFrom for StreamNumbers {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        const MAX_COUNT: usize = 160_000;
        let length = VarInt::read_from(r)?;
        if length.as_u64() > MAX_COUNT as u64 {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                TooLongError::new(MAX_COUNT, length.as_u64() as usize),
            ));
        }
        let mut values = Vec::with_capacity(length.as_u64() as usize);
        for _ in 0..length.as_u64() {
            values.push(StreamNumber::read_from(r)?);
        }
        Ok(Self(values))
    }
}

#[test]
fn test_stream_numbers_write_to() {
    let test: StreamNumbers = vec![0xcd_u32.into(), 0xfe_u32.into(), 0xcdef_u32.into()].into();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [0x03, 0xcd, 0xfd, 0x00, 0xfe, 0xfd, 0xcd, 0xef];
    assert_eq!(bytes, expected);
}

#[test]
fn test_stream_numbers_read_from() {
    use std::io::Cursor;

    let mut bytes = Cursor::new([0x03, 0xcd, 0xfd, 0x00, 0xfe, 0xfd, 0xcd, 0xef]);
    let test = StreamNumbers::read_from(&mut bytes).unwrap();
    let expected: StreamNumbers = vec![0xcd_u32.into(), 0xfe_u32.into(), 0xcdef_u32.into()].into();
    assert_eq!(test, expected);
}
