//! Message types defined in the Bitmessage protocol.

use std::{
    convert::TryInto,
    fmt,
    io::{self, Read, Write},
    net::{Ipv4Addr, SocketAddrV4},
};

use rand::random;
use serde::{Deserialize, Serialize};

use crate::{
    __impl_index,
    config::Config,
    hash::double_sha512,
    io::{LenBm, LimitedReadFrom, ReadFrom, SizedReadFrom, WriteTo},
    net::SocketAddr,
    object,
    packet::{self, Command, Packet},
    pow,
    priv_util::ToHexString,
    time::Time,
    var_type::{VarInt, VarStr},
};

pub use crate::{
    io::TooLongError,
    net_addr::{NetAddr, Services},
    stream::{StreamNumber, StreamNumbers},
};

/// Indicates that this object is a Bitmessage message
/// and informs what type of message it is.
pub trait Message {
    /// What type of message this is.
    const COMMAND: Command;
}

/// Provides a method that creates a Bitmessage packet from the Bitmessage message.
pub trait Pack {
    /// Returns a Bitmessage packet created from the Bitmessage message.
    fn pack(&self, config: &Config) -> packet::Result<Packet>;
}

impl<T> Pack for T
where
    T: WriteTo + Message,
{
    fn pack(&self, config: &Config) -> packet::Result<Packet> {
        let mut bytes = Vec::new();
        self.write_to(&mut bytes).unwrap();
        Packet::new(config, Self::COMMAND, bytes)
    }
}

/// A fatality level of an error message.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Fatal(VarInt);

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

impl Fatal {
    /// Constructs a fatality level from a level value.
    pub fn new(value: u64) -> Self {
        Self(value.into())
    }

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

impl From<u64> for Fatal {
    fn from(value: u64) -> Self {
        Self(value.into())
    }
}

impl WriteTo for Fatal {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl ReadFrom for Fatal {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(VarInt::read_from(r)?))
    }
}

/// A text of an error message.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ErrorText(VarStr);

impl ErrorText {
    /// Constructs an error text from a byte string.
    pub fn new(bytes: Vec<u8>) -> Self {
        Self(VarStr::new(bytes))
    }
}

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

impl From<Vec<u8>> for ErrorText {
    fn from(bytes: Vec<u8>) -> Self {
        Self(bytes.into())
    }
}

impl WriteTo for ErrorText {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl LimitedReadFrom for ErrorText {
    fn limited_read_from(r: &mut dyn Read, max_len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(VarStr::limited_read_from(r, max_len)?))
    }
}

/// An "error" message that is issued when some error occurred
/// in a communication.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Error {
    fatal: Fatal,
    ban_time: VarInt,
    // inventory_vector: Inv,
    inventory_vector: VarStr,
    error_text: ErrorText,
}

impl Error {
    /// Constructs an "error" message
    /// from a fatality level and an error text.
    pub fn new(fatal: Fatal, error_text: ErrorText) -> Self {
        Self {
            fatal,
            ban_time: 0u64.into(),
            inventory_vector: b"".to_vec().into(),
            error_text,
        }
    }

    /// Returns the fatality level.
    pub fn fatal(&self) -> Fatal {
        self.fatal
    }

    /// Returns the error text.
    pub fn error_text(&self) -> &ErrorText {
        &self.error_text
    }
}

impl WriteTo for Error {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.fatal.write_to(w)?;
        self.ban_time.write_to(w)?;
        self.inventory_vector.write_to(w)?;
        self.error_text.write_to(w)?;
        Ok(())
    }
}

impl ReadFrom for Error {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        const MAX_VAR_STR_LENGTH: usize = 5000; // XXX

        Ok(Self {
            fatal: Fatal::read_from(r)?,
            ban_time: VarInt::read_from(r)?,
            inventory_vector: VarStr::limited_read_from(r, MAX_VAR_STR_LENGTH)?,
            error_text: ErrorText::limited_read_from(r, MAX_VAR_STR_LENGTH)?,
        })
    }
}

impl Message for Error {
    const COMMAND: Command = Command::ERROR;
}

#[test]
fn test_error_write_to() {
    let test = Error::new(1u64.into(), b"hello".to_vec().into());
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [1, 0, 0, 5, b'h', b'e', b'l', b'l', b'o'];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new([1, 0, 0, 5, b'h', b'e', b'l', b'l', b'o']);
    let test = Error::read_from(&mut bytes).unwrap();
    let expected = Error::new(1u64.into(), b"hello".to_vec().into());
    assert_eq!(test, expected);
}

/// A hash value of an object message, used to specify the object message.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct InvHash([u8; 32]);

impl InvHash {
    /// Constructs an inventory hash from a byte array.
    pub fn new(value: [u8; 32]) -> Self {
        Self(value)
    }
}

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

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

impl WriteTo for InvHash {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl ReadFrom for InvHash {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(<[u8; 32]>::read_from(r)?))
    }
}

/// A "getdata" message that is issued when a node
/// want to retrieve objects.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Getdata {
    list: Vec<InvHash>,
}

impl AsRef<[InvHash]> for Getdata {
    fn as_ref(&self) -> &[InvHash] {
        &self.list
    }
}

__impl_index!(Getdata, list, InvHash);

impl Getdata {
    /// The maximum count of the elements of a list the message can convey.
    pub const MAX_COUNT: usize = 50000;

    /// Constructs a "getdata" message from a list of inventory hashes.
    pub fn new(list: Vec<InvHash>) -> Result<Self, TooLongError> {
        if list.len() > Self::MAX_COUNT {
            return Err(TooLongError::new(Self::MAX_COUNT, list.len()));
        }
        Ok(Self { list })
    }
}

impl WriteTo for Getdata {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        let count: VarInt = self.list.len().into();
        count.write_to(w)?;
        for a in &self.list {
            a.write_to(w)?
        }
        Ok(())
    }
}

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

impl Message for Getdata {
    const COMMAND: Command = Command::GETDATA;
}

#[test]
fn test_getdata_write_to() {
    let inv0 = InvHash::new([0x01; 32]);
    let inv1 = InvHash::new([0x23; 32]);
    let inv2 = InvHash::new([0x45; 32]);
    let test = Getdata::new([inv0, inv1, inv2].to_vec()).unwrap();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [
        3, //
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, //
        0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
        0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
        0x23, 0x23, //
        0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
        0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
        0x45, 0x45, //
    ];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new(
        [
            3, //
            0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x01, 0x01, 0x01, //
            0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
            0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
            0x23, 0x23, 0x23, 0x23, //
            0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
            0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
            0x45, 0x45, 0x45, 0x45, //
        ]
        .to_vec(),
    );
    let test = Getdata::read_from(&mut bytes).unwrap();
    let inv0 = InvHash::new([0x01; 32]);
    let inv1 = InvHash::new([0x23; 32]);
    let inv2 = InvHash::new([0x45; 32]);
    let expected = Getdata::new([inv0, inv1, inv2].to_vec()).unwrap();
    assert_eq!(test, expected);
}

/// An "inv" message that is used by a node to advertise
/// what objects it has.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Inv {
    list: Vec<InvHash>,
}

impl AsRef<[InvHash]> for Inv {
    fn as_ref(&self) -> &[InvHash] {
        &self.list
    }
}

__impl_index!(Inv, list, InvHash);

impl Inv {
    const MAX_COUNT: usize = 50000;

    /// The maximum count of the elements of a list the message can convey.
    /// This is the fixed value due to a historical bug of PyBitmessage.
    pub const MAX_COUNT_FIXED: usize = Self::MAX_COUNT - 1;

    /// Constructs an "inv" message from a list of inventory hashes.
    pub fn new(list: Vec<InvHash>) -> Result<Self, TooLongError> {
        if list.len() > Self::MAX_COUNT_FIXED {
            return Err(TooLongError::new(Self::MAX_COUNT_FIXED, list.len()));
        }
        Ok(Self { list })
    }
}

impl WriteTo for Inv {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        let count: VarInt = self.list.len().into();
        count.write_to(w)?;
        for a in &self.list {
            a.write_to(w)?
        }
        Ok(())
    }
}

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

impl Message for Inv {
    const COMMAND: Command = Command::INV;
}

#[test]
fn test_inv_write_to() {
    let inv0 = InvHash::new([0x01; 32]);
    let inv1 = InvHash::new([0x23; 32]);
    let inv2 = InvHash::new([0x45; 32]);
    let test = Inv::new([inv0, inv1, inv2].to_vec()).unwrap();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [
        3, //
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
        0x01, 0x01, //
        0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
        0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
        0x23, 0x23, //
        0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
        0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
        0x45, 0x45, //
    ];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new(
        [
            3, //
            0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
            0x01, 0x01, 0x01, 0x01, //
            0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
            0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
            0x23, 0x23, 0x23, 0x23, //
            0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
            0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
            0x45, 0x45, 0x45, 0x45, //
        ]
        .to_vec(),
    );
    let test = Inv::read_from(&mut bytes).unwrap();
    let inv0 = InvHash::new([0x01; 32]);
    let inv1 = InvHash::new([0x23; 32]);
    let inv2 = InvHash::new([0x45; 32]);
    let expected = Inv::new([inv0, inv1, inv2].to_vec()).unwrap();
    assert_eq!(test, expected);
}

// TODO Dinv

/// An "object" message that consists of a object
/// such as a public key or an one-to-one message.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Object {
    nonce: pow::Nonce,
    header: object::Header,
    object_payload: Vec<u8>,
}

impl Object {
    /// The maximum length of the payload of a object message.
    pub const MAX_OBJECT_PAYLOAD_LENGTH: usize = 1 << 18;

    /// Constructs an object message from the specified parameters.
    pub fn new(
        nonce: pow::Nonce,
        header: object::Header,
        object_payload: Vec<u8>,
    ) -> Result<Self, TooLongError> {
        if object_payload.len() > Self::MAX_OBJECT_PAYLOAD_LENGTH {
            return Err(TooLongError::new(
                Self::MAX_OBJECT_PAYLOAD_LENGTH,
                object_payload.len(),
            ));
        }
        Ok(Self {
            nonce,
            header,
            object_payload,
        })
    }

    /// Returns the nonce of the proof of work.
    pub fn nonce(&self) -> pow::Nonce {
        self.nonce
    }

    /// Returns the object header.
    pub fn header(&self) -> &object::Header {
        &self.header
    }

    /// Returns the payload of the object message.
    pub fn object_payload(&self) -> &[u8] {
        &self.object_payload
    }

    /// Returns the inventory hash of the object message.
    pub fn inv_hash(&self) -> InvHash {
        let mut bytes: Vec<u8> = Vec::new();
        self.write_to(&mut bytes).unwrap();
        InvHash(double_sha512(bytes)[0..32].try_into().unwrap())
    }

    /// Validates the proof of work of the object message
    /// with the specified custom parameters.
    pub fn validate_pow_custom(
        &self,
        config: &Config,
        nonce_trials_per_byte: pow::NonceTrialsPerByte,
        extra_bytes: pow::PayloadLengthExtraBytes,
        now: Time,
    ) -> Result<(), pow::ValidateError> {
        let mut bytes =
            Vec::with_capacity(self.header.len_bm() as usize + self.object_payload.len());
        self.header.write_to(&mut bytes).unwrap();
        self.object_payload.write_to(&mut bytes).unwrap();
        let initial_hash = pow::initial_hash(&bytes);
        let target = pow::target(
            config,
            bytes.len(),
            self.header.expires_time(),
            now,
            nonce_trials_per_byte,
            extra_bytes,
        );
        pow::validate(initial_hash, target, self.nonce)
    }

    /// Validates the proof of work of the object message
    /// with the default parameters.
    pub fn validate_pow(&self, config: &Config) -> Result<(), pow::ValidateError> {
        self.validate_pow_custom(
            config,
            config.nonce_trials_per_byte(),
            config.payload_length_extra_bytes(),
            Time::now(),
        )
    }
}

impl WriteTo for Object {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.nonce.write_to(w)?;
        self.header.write_to(w)?;
        self.object_payload.write_to(w)?;
        Ok(())
    }
}

impl SizedReadFrom for Object {
    fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        let nonce = pow::Nonce::read_from(r)?;
        let header = object::Header::read_from(r)?;
        let object_payload_len = len - nonce.len_bm() - header.len_bm();
        if object_payload_len > Self::MAX_OBJECT_PAYLOAD_LENGTH {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                TooLongError::new(Self::MAX_OBJECT_PAYLOAD_LENGTH, object_payload_len),
            ));
        }
        let object_payload = Vec::<u8>::sized_read_from(r, object_payload_len)?;
        Ok(Self {
            nonce,
            header,
            object_payload,
        })
    }
}

impl Message for Object {
    const COMMAND: Command = Command::OBJECT;
}

#[test]
fn test_object_write_to() {
    let nonce = pow::Nonce::new(0xfedc_ba98_7654_3210);
    let header = object::Header::new(
        0x0123_4567_89ab_cdef.into(),
        2.into(),
        3u64.into(),
        1u32.into(),
    );
    let object_payload = [0xff; 23].to_vec();
    let test = Object::new(nonce, header, object_payload).unwrap();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, //
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x02, 3, 1, //
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
    ];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new(
        [
            0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, //
            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x02, 3, 1, //
            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
        ]
        .to_vec(),
    );
    let test = Object::sized_read_from(&mut bytes, 45).unwrap();
    let nonce = pow::Nonce::new(0xfedc_ba98_7654_3210);
    let header = object::Header::new(
        0x0123_4567_89ab_cdef.into(),
        2.into(),
        3u64.into(),
        1u32.into(),
    );
    let object_payload = [0xff; 23].to_vec();
    let expected = Object::new(nonce, header, object_payload).unwrap();
    assert_eq!(test, expected);
}

/// An "addr" message that consists of a list of
/// [`NetAddr`](struct.NetAddr.html).
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Addr {
    list: Vec<NetAddr>,
}

impl AsRef<[NetAddr]> for Addr {
    fn as_ref(&self) -> &[NetAddr] {
        &self.list
    }
}

__impl_index!(Addr, list, NetAddr);

impl Addr {
    /// The maximum count of the elements of a list the message can convey.
    pub const MAX_COUNT: usize = 1000;

    /// Constructs an "addr" message from a list of
    /// [`NetAddr`](struct.NetAddr.html).
    pub fn new(list: Vec<NetAddr>) -> Result<Self, TooLongError> {
        if list.len() > Self::MAX_COUNT {
            return Err(TooLongError::new(Self::MAX_COUNT, list.len()));
        }
        Ok(Self { list })
    }
}

impl WriteTo for Addr {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        let count: VarInt = self.list.len().into();
        count.write_to(w)?;
        for a in &self.list {
            a.write_to(w)?
        }
        Ok(())
    }
}

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

impl Message for Addr {
    const COMMAND: Command = Command::ADDR;
}

#[test]
fn test_addr_write_to() {
    let addr0 = NetAddr::new(
        0x0123_4567_89ab_cdef.into(),
        1.into(),
        Services::NETWORK,
        LOCAL_SOCKET_ADDR.clone(),
    );
    let addr1 = NetAddr::new(
        0x0123_4567_89ab_cdef.into(),
        1.into(),
        Services::NETWORK,
        LOCAL_SOCKET_ADDR.clone(),
    );
    let addr2 = NetAddr::new(
        0x0123_4567_89ab_cdef.into(),
        1.into(),
        Services::NETWORK,
        LOCAL_SOCKET_ADDR.clone(),
    );
    let test = Addr::new([addr0, addr1, addr2].to_vec()).unwrap();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [
        3, //
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
        0x00, 0x00, 0x00, 0x01, //
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
        0x00, 0x00, 0x00, 0x01, //
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
        0x00, 0x00, 0x00, 0x01, //
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
    ];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new(
        [
            3, //
            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
            0x00, 0x00, 0x00, 0x01, //
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
            0x00, 0x00, 0x00, 0x01, //
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
            0x00, 0x00, 0x00, 0x01, //
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
        ]
        .to_vec(),
    );
    let test = Addr::read_from(&mut bytes).unwrap();
    let addr0 = NetAddr::new(
        0x0123_4567_89ab_cdef.into(),
        1.into(),
        Services::NETWORK,
        LOCAL_SOCKET_ADDR.clone(),
    );
    let addr1 = NetAddr::new(
        0x0123_4567_89ab_cdef.into(),
        1.into(),
        Services::NETWORK,
        LOCAL_SOCKET_ADDR.clone(),
    );
    let addr2 = NetAddr::new(
        0x0123_4567_89ab_cdef.into(),
        1.into(),
        Services::NETWORK,
        LOCAL_SOCKET_ADDR.clone(),
    );
    let expected = Addr::new([addr0, addr1, addr2].to_vec()).unwrap();
    assert_eq!(test, expected);
}

// TODO Portcheck

/// A "ping" message that is used to keep a network connection alive.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Ping {
    payload: Vec<u8>,
}

impl Default for Ping {
    fn default() -> Self {
        Self {
            payload: Vec::with_capacity(0),
        }
    }
}

impl Ping {
    /// Constructs a ping message with empty payload.
    pub fn new() -> Self {
        Self::default()
    }

    /// Constructs a ping message with a payload.
    pub fn with_payload(payload: Vec<u8>) -> Self {
        Self { payload }
    }

    /// Returns the payload.
    pub fn payload(&self) -> &[u8] {
        &self.payload
    }
}

impl WriteTo for Ping {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.payload.write_to(w)?;
        Ok(())
    }
}

impl SizedReadFrom for Ping {
    fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            payload: Vec::<u8>::sized_read_from(r, len)?,
        })
    }
}

impl Message for Ping {
    const COMMAND: Command = Command::PING;
}

#[test]
fn test_ping_write_to() {
    let test = Ping::with_payload(b"hello".to_vec());
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [b'h', b'e', b'l', b'l', b'o'];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new([b'h', b'e', b'l', b'l', b'o']);
    let test = Ping::sized_read_from(&mut bytes, 5).unwrap();
    let expected = Ping::with_payload(b"hello".to_vec());
    assert_eq!(test, expected);
}

/// A "pong" message that is sent as a reply to a ping message.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Pong {
    payload: Vec<u8>,
}

impl Default for Pong {
    fn default() -> Self {
        Self {
            payload: Vec::with_capacity(0),
        }
    }
}

impl Pong {
    /// Constructs a pong message with empty payload.
    pub fn new() -> Self {
        Self::default()
    }

    /// Constructs a  pong message with a payload.
    pub fn with_payload(payload: Vec<u8>) -> Self {
        Self { payload }
    }

    /// Returns the payload.
    pub fn payload(&self) -> &[u8] {
        &self.payload
    }
}

impl WriteTo for Pong {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.payload.write_to(w)?;
        Ok(())
    }
}

impl SizedReadFrom for Pong {
    fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            payload: Vec::<u8>::sized_read_from(r, len)?,
        })
    }
}

impl Message for Pong {
    const COMMAND: Command = Command::PONG;
}

#[test]
fn test_pong_write_to() {
    let test = Pong::with_payload(b"hello".to_vec());
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [b'h', b'e', b'l', b'l', b'o'];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new([b'h', b'e', b'l', b'l', b'o']);
    let test = Pong::sized_read_from(&mut bytes, 5).unwrap();
    let expected = Pong::with_payload(b"hello".to_vec());
    assert_eq!(test, expected);
}

/// A "verack" message that is sent as a reply to a version message,
/// which indicates the connection is accepted.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Verack {}

impl Default for Verack {
    fn default() -> Self {
        Self {}
    }
}

impl Verack {
    /// Constructs a verack message.
    pub fn new() -> Self {
        Self::default()
    }
}

impl WriteTo for Verack {
    fn write_to(&self, _w: &mut dyn Write) -> io::Result<()> {
        Ok(())
    }
}

impl ReadFrom for Verack {
    fn read_from(_r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self {})
    }
}

impl Message for Verack {
    const COMMAND: Command = Command::VERACK;
}

#[test]
fn test_verack_write_to() {
    let test = Verack::new();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    let expected = [];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new([]);
    let test = Verack::read_from(&mut bytes).unwrap();
    let expected = Verack::new();
    assert_eq!(test, expected);
}

/// A Bitmessage protocol version number used in a version message.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct ProtocolVersion(u32);

impl ProtocolVersion {
    /// Constructs a protocol version 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 fmt::Display for ProtocolVersion {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

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

impl WriteTo for ProtocolVersion {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl ReadFrom for ProtocolVersion {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(u32::read_from(r)?))
    }
}

/// A random value specific to a node, which is used by the node
/// to detect if connecting to itself.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct NodeNonce(u64);

impl NodeNonce {
    /// Constructs a node nonce from a value.
    pub fn new(value: u64) -> Self {
        Self(value)
    }

    /// Constructs a node nonce from a random value.
    pub fn random() -> Self {
        Self(random::<u64>())
    }

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

impl fmt::Display for NodeNonce {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:016x}", self.0)
    }
}

impl From<u64> for NodeNonce {
    fn from(value: u64) -> Self {
        Self(value)
    }
}

impl WriteTo for NodeNonce {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl ReadFrom for NodeNonce {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(u64::read_from(r)?))
    }
}

/// A user agent string of a Bitmessage node.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
pub struct UserAgent(VarStr);

impl UserAgent {
    /// Constructs a user agent string from a byte array.
    pub fn new(bytes: Vec<u8>) -> Self {
        Self(VarStr::new(bytes))
    }
}

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

impl From<Vec<u8>> for UserAgent {
    fn from(bytes: Vec<u8>) -> Self {
        Self(bytes.into())
    }
}

impl WriteTo for UserAgent {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.0.write_to(w)
    }
}

impl LimitedReadFrom for UserAgent {
    fn limited_read_from(r: &mut dyn Read, max_len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self(VarStr::limited_read_from(r, max_len)?))
    }
}

/// A "version" message that is exchanged between nodes when connected,
/// which informs what type of node it is.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Version {
    version: ProtocolVersion,
    services: Services,
    timestamp: Time,
    remote_services: Services,
    addr_recv: SocketAddr,
    services2: Services,
    addr_from: SocketAddr,
    nonce: NodeNonce,
    user_agent: UserAgent,
    stream_numbers: StreamNumbers,
}

lazy_static! {
    static ref LOCAL_SOCKET_ADDR: SocketAddr =
        SocketAddr::Ipv4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8444));
}

impl Version {
    const MAX_USER_AGENT_LENGTH: usize = 5000 - 3;

    /// Constructs a builder for building a version message.
    pub fn builder(config: &Config, nonce: NodeNonce, user_agent: UserAgent) -> VersionBuilder {
        VersionBuilder::new(config, nonce, user_agent)
    }

    /// Returns the protocol version.
    pub fn version(&self) -> ProtocolVersion {
        self.version
    }

    /// Returns the flags what features the node serves.
    pub fn services(&self) -> Services {
        self.services
    }

    /// Returns the timestamp.
    pub fn timestamp(&self) -> Time {
        self.timestamp
    }

    /*
        /// Returns the remote services.
        pub fn remote_services(&self) -> Services {
            self.remote_services
        }

        /// Returns the addr recv.
        pub fn addr_recv(&self) -> &SocketAddr {
            &self.addr_recv
        }

        /// Returns the services2.
        pub fn services2(&self) -> Services {
            self.services2
        }

        /// Returns the addr from.
        pub fn addr_from(&self) -> &SocketAddr {
            &self.addr_from
        }
    */

    /// Returns the node nonce.
    pub fn nonce(&self) -> NodeNonce {
        self.nonce
    }

    /// Returns the user agent.
    pub fn user_agent(&self) -> &UserAgent {
        &self.user_agent
    }

    /// Returns the list of stream numbers.
    pub fn stream_numbers(&self) -> &StreamNumbers {
        &self.stream_numbers
    }
}

impl WriteTo for Version {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.version.write_to(w)?;
        self.services.write_to(w)?;
        self.timestamp.write_to(w)?;
        self.remote_services.write_to(w)?;
        self.addr_recv.write_to(w)?;
        self.services2.write_to(w)?;
        self.addr_from.write_to(w)?;
        self.nonce.write_to(w)?;
        self.user_agent.write_to(w)?;
        self.stream_numbers.write_to(w)?;
        Ok(())
    }
}

impl ReadFrom for Version {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        Ok(Self {
            version: ProtocolVersion::read_from(r)?,
            services: Services::read_from(r)?,
            timestamp: Time::read_from(r)?,
            remote_services: Services::read_from(r)?,
            addr_recv: SocketAddr::read_from(r)?,
            services2: Services::read_from(r)?,
            addr_from: SocketAddr::read_from(r)?,
            nonce: NodeNonce::read_from(r)?,
            user_agent: UserAgent::limited_read_from(r, Self::MAX_USER_AGENT_LENGTH)?,
            stream_numbers: StreamNumbers::read_from(r)?,
        })
    }
}

impl Message for Version {
    const COMMAND: Command = Command::VERSION;
}

/// A builder for building a version message.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VersionBuilder {
    message: Version,
}

impl VersionBuilder {
    fn new(config: &Config, nonce: NodeNonce, user_agent: UserAgent) -> Self {
        let stream_numbers: StreamNumbers = vec![1_u32.into()].into();
        let message = Version {
            version: config.protocol_version(),
            services: Services::NETWORK,
            timestamp: Time::now(),
            remote_services: Services::NETWORK,
            addr_recv: LOCAL_SOCKET_ADDR.clone(),
            services2: Services::NETWORK,
            addr_from: LOCAL_SOCKET_ADDR.clone(),
            nonce,
            user_agent,
            stream_numbers,
        };
        Self { message }
    }

    /// Sets the flags what features the node serves.
    pub fn services(&mut self, services: Services) -> &mut Self {
        self.message.services = services;
        self
    }

    /// Sets the timestamp.
    pub fn timestamp(&mut self, timestamp: Time) -> &mut Self {
        self.message.timestamp = timestamp;
        self
    }

    /*
        /// Sets the remote services.
        pub fn remote_services(&mut self, services: Services) -> &mut Self {
            self.message.remote_services = services;
            self
        }

        /// Sets the addr recv.
        pub fn addr_recv(&mut self, addr: SocketAddr) -> &mut Self {
            self.message.addr_recv = addr;
            self
        }

        /// Sets the services2.
        pub fn services2(&mut self, services: Services) -> &mut Self {
            self.message.services2 = services;
            self
        }

        /// Sets the addr from.
        pub fn addr_from(&mut self, addr: SocketAddr) -> &mut Self {
            self.message.addr_from = addr;
            self
        }
    */

    /// Sets the list of stream numbers.
    pub fn stream_numbers(&mut self, list: StreamNumbers) -> &mut Self {
        self.message.stream_numbers = list;
        self
    }

    /// Returns the version message this builder represents.
    pub fn build(&self) -> Version {
        self.message.clone()
    }
}

#[test]
fn test_version_write_to() {
    let config = Config::new();
    let stream_numbers: StreamNumbers = vec![1_u32.into(), 2_u32.into()].into();
    let test = Version::builder(
        &config,
        0x0123_4567_89ab_cdef.into(),
        b"hello".to_vec().into(),
    )
    .stream_numbers(stream_numbers)
    .build();
    let mut bytes = Vec::new();
    test.write_to(&mut bytes).unwrap();
    bytes[12..20].copy_from_slice(&[0xff; 8]);
    let expected = [
        0, 0, 0, 3, //
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
        5, b'h', b'e', b'l', b'l', b'o', //
        2, 1, 2, //
    ];
    assert_eq!(bytes, expected.to_vec());
}

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

    let mut bytes = Cursor::new(
        [
            0, 0, 0, 3, //
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, //
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1, 0x20, 0xfc, //
            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, //
            5, b'h', b'e', b'l', b'l', b'o', //
            2, 1, 2, //
        ]
        .to_vec(),
    );
    let test = Version::read_from(&mut bytes).unwrap();
    let stream_numbers: StreamNumbers = vec![1_u32.into(), 2_u32.into()].into();
    let config = Config::new();
    let mut expected = Version::builder(
        &config,
        0x0123_4567_89ab_cdef.into(),
        b"hello".to_vec().into(),
    )
    .stream_numbers(stream_numbers)
    .build();
    expected.timestamp = 0xffff_ffff_ffff_ffff.into();
    assert_eq!(test, expected);
}
