//! Provides encoding types for Bitmessage messages.

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

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

/// Message encoding types.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Encoding {
    /// Ignore encoding type.
    Ignore = 0,
    /// Trivial encoding type.
    Trivial = 1,
    /// Simple encoding type.
    Simple = 2,
    /// Extended encoding type.
    Extended = 3,
}

/// An error which can be returned when parsing a message encoding type.
/// The actual value of the input is returned as a payload of this error.
///
/// This error is used as the error type for
/// the `TryFrom` implementation for [`Encoding`].
///
/// [`Encoding`]: struct.Encoding.html
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct InvalidEncoding(VarInt);

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

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

impl TryFrom<VarInt> for Encoding {
    type Error = InvalidEncoding;

    fn try_from(value: VarInt) -> Result<Self, <Self as TryFrom<VarInt>>::Error> {
        match value.as_u64() {
            0 => Ok(Encoding::Ignore),
            1 => Ok(Encoding::Trivial),
            2 => Ok(Encoding::Simple),
            3 => Ok(Encoding::Extended),
            _ => Err(InvalidEncoding(value)),
        }
    }
}

impl WriteTo for Encoding {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        let v: VarInt = (*self as u64).into();
        v.write_to(w)?;
        Ok(())
    }
}

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

/// A message encoded in simple encoding type.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Simple {
    subject: Vec<u8>,
    body: Vec<u8>,
}

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

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

/// The error type returned when a conversion from a byte array
/// to a simple encoded message fails.
///
/// This error is used as the error type for the `TryFrom` implementation
/// for `Simple`.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TryIntoSimpleError {
    /// Indicates that no subject was found.
    SubjectNotFound,
    /// Indicates that no body was found.
    BodyNotFound,
}

const SUBJECT: &[u8] = b"Subject:";
const BODY: &[u8] = b"Body:";

impl TryFrom<&[u8]> for Simple {
    type Error = TryIntoSimpleError;

    fn try_from(bytes: &[u8]) -> Result<Self, <Self as TryFrom<&[u8]>>::Error> {
        if bytes.len() < SUBJECT.len() {
            return Err(TryIntoSimpleError::SubjectNotFound);
        }
        if &bytes[..SUBJECT.len()] != SUBJECT {
            return Err(TryIntoSimpleError::SubjectNotFound);
        }
        let lf_pos = bytes[SUBJECT.len()..].iter().position(|b| *b == b'\n');
        if lf_pos.is_none() {
            return Err(TryIntoSimpleError::SubjectNotFound);
        }
        let lf_pos = SUBJECT.len() + lf_pos.unwrap();
        let subject = &bytes[SUBJECT.len()..lf_pos];

        if bytes.len() < lf_pos + 1 + BODY.len() {
            return Err(TryIntoSimpleError::BodyNotFound);
        }
        if &bytes[lf_pos + 1..lf_pos + 1 + BODY.len()] != BODY {
            return Err(TryIntoSimpleError::BodyNotFound);
        }
        let body = &bytes[lf_pos + 1 + BODY.len()..];

        Ok(Self {
            subject: subject.to_vec(),
            body: body.to_vec(),
        })
    }
}
