use crate::types::Bencode;
use std::fmt;

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

/// A collection of possible errors that can be encountered while parsing a bencoded file.
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
    // Indicates an expected 'e' was missing. All bencode types except ByteStr expect an 'e' byte
    // after data to indicate the end of the object.
    MissingSentinel,
    // Indicates that the bytes passed to a parse function are not valid ASCII.
    InvalidASCIIBytes,
    // Indicates that bytes passed to a parse function were valid ascii, but could not be parsed to
    // an integer. (in cases like bytestr length, or an integer value.)
    InvalidIntegerString,
    // Indicates that the length specified by a bytestring is longer than the remaining number of
    // bytes passed into the parse function.
    ByteStringLengthOverflow,
    // Indicates that while parsing a dictionary object, a non bytestring key was found.
    BadDictionaryKey,
    // when a character is encountered by the parse_child function, that is not applicable to any
    // of the bencode types.
    NotAValidBencodeByte,
    // Indicates that the first byte of the array passed to a parsing function was not valid.
    // eg: as_int where first byte is not 'i', as_bstr where the first byte is not [0-9], etc.
    BadFirstByte,
    // Indicates that `as_dict` cannot be called on the given bencode type (Int, BStr, List).
    CannotParseAsDictionary,
    // Indicates that `as_list` cannot be called on the given bencode type (Int, BStr, Dict).
    CannotParseAsList,
    // Indicates that `as_int` cannot be called on the given bencode type (BStr, List, Dict).
    CannotParseAsInteger,
    // Indicates that `as_bstr` cannot be called on the given bencode type (Int, List, Dict).
    CannotParseAsByteString,
}

/// Implement how the error messages will be shown when thrown.
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
        match self {
            Error::MissingSentinel => {
                write!(f, "Expected to find an 'e' to terminate bencode object.")
            }
            _ => write!(f, "unimplemented specific error messages"),
        }
    }
}
/// A Generic Result object that returns <T> or a bencode Error object
pub type GenResult<T> = std::result::Result<T, Error>;
/// A Result object comprised of a BencodeWithOffset object, or a BencodeError
pub type Result = GenResult<(Bencode, usize)>;
