use std::ffi::{CStr, CString};

pub enum Message {
    Backend(BackendMessage),
    Frontend(FrontendMessage),
}

#[derive(Debug)]
pub enum MessageError {
    MalformedMessage,
    TypeByteDecodeError(char),
    IoError(std::io::Error),
}

/// Upper bound on a single message's declared length. A peer (or a corrupt
/// length field) claiming an absurd size would otherwise leave the framing
/// loop waiting forever for bytes that never arrive; capping it turns that
/// hang into a clean `MalformedMessage`. 1 GiB - 1 matches Postgres's own
/// MaxAllocSize, the largest message it will ever send.
const MAX_MESSAGE_LEN: usize = 0x3FFF_FFFF;

#[repr(u8)]
#[derive(PartialEq, Copy,Clone)]
pub enum FrontendMessageTypeByte {
    Bind = b'B',
    Close = b'C',
    CopyData = b'd',
    CopyDone = b'c',
    CopyFail = b'f',
    Describe = b'D',
    Execute = b'E',
    Flush = b'H',
    FunctionCall = b'F',
    Parse = b'P',
    Query = b'Q',
    AuthCredMessage = b'p',
    Sync = b'S',
    Terminate = b'X',
}

impl FrontendMessageTypeByte {
    fn to_type(&self, expecting: Option<FrontendMessageType>) -> Result<FrontendMessageType, MessageError> {
        Ok(match self {
            FrontendMessageTypeByte::Bind => FrontendMessageType::Bind,
            FrontendMessageTypeByte::Close => FrontendMessageType::Close,
            FrontendMessageTypeByte::CopyData => FrontendMessageType::CopyData,
            FrontendMessageTypeByte::CopyDone => FrontendMessageType::CopyDone,
            FrontendMessageTypeByte::CopyFail => FrontendMessageType::CopyFail,
            FrontendMessageTypeByte::Describe => FrontendMessageType::Describe,
            FrontendMessageTypeByte::Execute => FrontendMessageType::Execute,
            FrontendMessageTypeByte::Flush => FrontendMessageType::Flush,
            FrontendMessageTypeByte::FunctionCall => FrontendMessageType::FunctionCall,
            FrontendMessageTypeByte::Parse => FrontendMessageType::Parse,
            FrontendMessageTypeByte::Query => FrontendMessageType::Query,
            FrontendMessageTypeByte::Sync => FrontendMessageType::Sync,
            FrontendMessageTypeByte::Terminate => FrontendMessageType::Terminate,
            FrontendMessageTypeByte::AuthCredMessage => if expecting.ok_or(MessageError::MalformedMessage)?.is_auth_cred() { expecting.unwrap() } else {Err(MessageError::MalformedMessage)?}
        })
    }
}

impl TryFrom<u8> for FrontendMessageTypeByte {
    type Error = MessageError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            b'B' => FrontendMessageTypeByte::Bind,
            b'C' => FrontendMessageTypeByte::Close,
            b'd' => FrontendMessageTypeByte::CopyData,
            b'c' => FrontendMessageTypeByte::CopyDone,
            b'f' => FrontendMessageTypeByte::CopyFail,
            b'D' => FrontendMessageTypeByte::Describe,
            b'E' => FrontendMessageTypeByte::Execute,
            b'H' => FrontendMessageTypeByte::Flush,
            b'F' => FrontendMessageTypeByte::FunctionCall,
            b'P' => FrontendMessageTypeByte::Parse,
            b'Q' => FrontendMessageTypeByte::Query,
            b'p' => FrontendMessageTypeByte::AuthCredMessage,
            b'S' => FrontendMessageTypeByte::Sync,
            b'X' => FrontendMessageTypeByte::Terminate,
            _ => Err(Self::Error::MalformedMessage)?,
        })
    }
}

#[derive(Copy,Clone)]
pub enum FrontendMessageType {
    // Type Byte-less variants.
    Startup,
    CancelRequest,
    SslRequest,
    GssEnc,

    // 'p' byte variants.
    GssResponse,
    SaslInitialResponse,
    SaslResponse,
    PasswordMessage,

    // Normal unique variants.
    Bind,
    Close,
    CopyData,
    CopyDone,
    CopyFail,
    Describe,
    Execute,
    Flush,
    FunctionCall,
    Parse,
    Query,
    Sync,
    Terminate,
}

impl FrontendMessageType {
    fn from_discrim(discrim: i32, is_startup: bool) -> Result<Self, MessageError> {
        match discrim {
            80877104 => Ok(FrontendMessageType::GssEnc),
            80877103 => Ok(FrontendMessageType::SslRequest),
            80877102 => Ok(FrontendMessageType::CancelRequest),
            _ => if is_startup { Ok(FrontendMessageType::Startup) } else { Err(MessageError::MalformedMessage) }, //Hopefully.
        }
    }

    fn is_auth_cred(&self) -> bool {
        match self {
            Self::GssResponse | Self::SaslInitialResponse | Self::SaslResponse | Self::PasswordMessage => true,
            _ => false,
        }
    }

    fn is_startup(&self) -> bool {
        match self {
            Self::Startup => true,
            _ => false,
        }
    }
}

pub enum FrontendMessage {
    // Type Byte-less variants.
    Startup {
        version_major: i16,
        version_minor: i16,
        params: Vec<(CString,CString)>,
    },
    CancelRequest {
        pid: i32,
        key: Vec<u8>,
    },
    SslRequest,
    GssEnc,

    // 'p' byte variants.
    GssResponse {
        data: Vec<u8>,
    },
    SaslInitialResponse {
        auth_mech: CString,
        sasl_initial: Option<Vec<u8>>,
    },
    SaslResponse {
        data: Vec<u8>,
    },
    PasswordMessage {
        password: CString,
    },

    // Normal unique variants.
    Bind {
        dest_portal: CString,
        src_prepared_stmt: CString,
        formats: Vec<Format>,
        params: Vec<Option<Vec<u8>>>,
        result_formats: Vec<Format>,
    }, //'B'
    Close {
        target: Target,
        target_name: CString,

    },
    CopyData {
        data: Vec<u8>,
    },
    CopyDone,
    CopyFail {
        error_message: CString,
    },
    Describe {
        target: Target,
        target_name: CString,
    },
    Execute {
        portal_name: CString,
        num_rows: i32,
    },
    Flush,
    FunctionCall {
        oid: i32,
        formats: Vec<Format>,
        arguments: Vec<Vec<u8>>,
        result_format: Format,
    },
    Parse {
        dest_stmt_name: CString,
        query: CString,
        param_data_type_oids: Vec<i32>,
    },
    Query {
        query: CString,
    },
    Sync,
    Terminate,
}

impl FrontendMessage {
    /// The leading type byte for this message, or `None` for the startup-class
    /// messages (Startup, CancelRequest, SSLRequest, GSSENCRequest) which are
    /// length-prefixed only and carry no type byte.
    fn type_byte(&self) -> Option<u8> {
        Some(match self {
            FrontendMessage::Startup { .. }
            | FrontendMessage::CancelRequest { .. }
            | FrontendMessage::SslRequest
            | FrontendMessage::GssEnc => return None,
            FrontendMessage::GssResponse { .. }
            | FrontendMessage::SaslInitialResponse { .. }
            | FrontendMessage::SaslResponse { .. }
            | FrontendMessage::PasswordMessage { .. } => b'p',
            FrontendMessage::Bind { .. } => b'B',
            FrontendMessage::Close { .. } => b'C',
            FrontendMessage::CopyData { .. } => b'd',
            FrontendMessage::CopyDone => b'c',
            FrontendMessage::CopyFail { .. } => b'f',
            FrontendMessage::Describe { .. } => b'D',
            FrontendMessage::Execute { .. } => b'E',
            FrontendMessage::Flush => b'H',
            FrontendMessage::FunctionCall { .. } => b'F',
            FrontendMessage::Parse { .. } => b'P',
            FrontendMessage::Query { .. } => b'Q',
            FrontendMessage::Sync => b'S',
            FrontendMessage::Terminate => b'X',
        })
    }

    fn to_buffer(&self) -> Result<Vec<u8>,MessageError> {
        let mut buf: Vec<u8> = vec!();
        let mut body: Vec<u8> = vec!();

        let length = 4i32 + match self {
            FrontendMessage::Startup { version_major, version_minor, params } => {
                let mut length = 4;
                body.extend_from_slice(version_major.to_be_bytes().as_slice());
                body.extend_from_slice(version_minor.to_be_bytes().as_slice());
                for param in params {
                    body.extend_from_slice(param.0.as_bytes_with_nul());
                    body.extend_from_slice(param.1.as_bytes_with_nul());
                    length += param.0.count_bytes() as i32 + param.1.count_bytes() as i32 + 2;
                }
                body.push(b'\0'); // parameter list terminated by a final zero byte
                length + 1
            }, FrontendMessage::CancelRequest { pid, key } => {
                body.extend_from_slice(80877102i32.to_be_bytes().as_slice());
                body.extend_from_slice(pid.to_be_bytes().as_slice());
                body.extend_from_slice(key.as_slice());
                8 + key.len() as i32
            }, FrontendMessage::SslRequest => {
                body.extend_from_slice(80877103i32.to_be_bytes().as_slice());
                4
            }, FrontendMessage::GssEnc => {
                body.extend_from_slice(80877104i32.to_be_bytes().as_slice());
                4
            }, FrontendMessage::GssResponse { data } => {
                body.extend_from_slice((data.len() as i32).to_be_bytes().as_slice());
                body.extend_from_slice(data.as_slice());
                4 + data.len() as i32
            }, FrontendMessage::SaslInitialResponse { auth_mech, sasl_initial } => {
                body.extend_from_slice(auth_mech.as_bytes_with_nul());
                1 + auth_mech.count_bytes() as i32 + match sasl_initial {
                    Some(si) => {
                        body.extend_from_slice((si.len() as i32).to_be_bytes().as_slice());
                        body.extend_from_slice(si.as_slice());
                        4 + si.len() as i32
                    }, None => {
                        body.extend_from_slice((-1i32).to_be_bytes().as_slice());
                        4i32
                    }
                }
            }, FrontendMessage::SaslResponse { data } => {
                body.extend_from_slice(data.as_slice());
                data.len() as i32
            }, FrontendMessage::PasswordMessage { password } => {
                body.extend_from_slice(password.as_bytes_with_nul());
                password.count_bytes() as i32 + 1
            },  FrontendMessage::Bind { dest_portal, src_prepared_stmt, formats, params, result_formats } => {
                //TODO: This one *really* needs double-checking
                body.extend_from_slice(dest_portal.as_bytes_with_nul());
                body.extend_from_slice(src_prepared_stmt.as_bytes_with_nul());
                body.extend_from_slice((formats.len() as i16).to_be_bytes().as_slice());
                for format in formats {
                    body.extend_from_slice((*format as i16).to_be_bytes().as_slice());
                }
                body.extend_from_slice((params.len() as i16).to_be_bytes().as_slice());
                let mut params_len = 0i32;
                for param in params {
                    params_len += match param {
                        Some(p) => {
                            body.extend_from_slice((p.len() as i32).to_be_bytes().as_slice());
                            body.extend_from_slice(p.as_slice());
                            p.len() as i32 + 4
                        }, None => {
                            body.extend_from_slice((-1i32).to_be_bytes().as_slice());
                            4
                        },
                    }
                }
                body.extend_from_slice((result_formats.len() as i16).to_be_bytes().as_slice());
                for format in result_formats {
                    body.extend_from_slice((*format as i16).to_be_bytes().as_slice());
                }
                dest_portal.count_bytes() as i32 + 1 + src_prepared_stmt.count_bytes() as i32 + 1 +
                2 + (formats.len() * 2) as i32 + 2 + params_len + 2 + (result_formats.len() * 2) as i32
            }
            FrontendMessage::Close { target, target_name } => {
                body.push((*target) as u8);
                body.extend_from_slice(target_name.as_bytes_with_nul());
                target_name.count_bytes() as i32 + 2
            }
            FrontendMessage::CopyData { data } => {
                body.extend_from_slice(data.as_slice());
                data.len() as i32
            }, FrontendMessage::CopyDone => {
                0
            }, FrontendMessage::CopyFail { error_message } => {
                body.extend_from_slice(error_message.as_bytes_with_nul());
                error_message.count_bytes() as i32 + 1
            }
            FrontendMessage::Describe { target, target_name } => {
                body.push((*target) as u8);
                body.extend_from_slice(target_name.as_bytes_with_nul());
                2 + target_name.count_bytes() as i32
            }, FrontendMessage::Execute { portal_name, num_rows } => {
                body.extend_from_slice(portal_name.as_bytes_with_nul());
                body.extend_from_slice((*num_rows).to_be_bytes().as_slice());
                (portal_name.count_bytes() + 1) as i32 + 4
            }, FrontendMessage::Flush => {
                0
            }, FrontendMessage::FunctionCall { oid, formats, arguments, result_format } => {
                body.extend_from_slice(oid.to_be_bytes().as_slice());
                body.extend_from_slice((formats.len() as i16).to_be_bytes().as_slice());
                for format in formats {
                    body.extend_from_slice((*format as i16).to_be_bytes().as_slice())
                }
                body.extend_from_slice((arguments.len() as i16).to_be_bytes().as_slice());
                let mut arguments_len = 0i32;
                for argument in arguments {
                    let al = argument.len() as i32;
                    body.extend_from_slice(al.to_be_bytes().as_slice());
                    body.extend_from_slice(argument.as_slice());
                    arguments_len += al + 4;
                }
                body.extend_from_slice((*result_format as i16).to_be_bytes().as_slice());
                10 + (formats.len() * 2) as i32 + arguments_len
            }, FrontendMessage::Parse { dest_stmt_name, query, param_data_type_oids } => {
                body.extend_from_slice(dest_stmt_name.as_bytes_with_nul());
                body.extend_from_slice(query.as_bytes_with_nul());
                body.extend_from_slice((param_data_type_oids.len() as i16).to_be_bytes().as_slice());
                for oid in param_data_type_oids {
                    body.extend_from_slice(oid.to_be_bytes().as_slice());
                }
                dest_stmt_name.count_bytes() as i32 + query.count_bytes() as i32 + 4 + (param_data_type_oids.len() * 4) as i32
            }, FrontendMessage::Query { query } => {
                body.extend_from_slice(query.as_bytes_with_nul());
                query.count_bytes() as i32 + 1
            }, FrontendMessage::Sync => {
                0
            }, FrontendMessage::Terminate => {
                0
            },
        };

        if let Some(type_byte) = self.type_byte() {
            buf.push(type_byte);
        }
        buf.extend_from_slice(&(length as i32).to_be_bytes());
        buf.extend_from_slice(body.as_slice());
        Ok(buf)
    }

    fn from_buffer(buf: &[u8], has_type_byte: bool, expecting_startup: bool, expecting: Option<FrontendMessageType>) -> Result<Option<(FrontendMessage,usize)>, MessageError> {
        if buf.len() < 4 { return Ok(None)}
        // Need the length field to read the header. On the typed path the type byte
        // precedes it, so we need length_offset + 4 bytes present.
        let (type_byte, length_offset) = match has_type_byte {
            true  => (Some(FrontendMessageTypeByte::try_from(*buf.get(0).ok_or(MessageError::MalformedMessage)?)?), 1),
            false => (None, 0),
        };
        
        let length = match buf.get(length_offset..length_offset + 4) {
            Some(l) => i32::from_be_bytes(l.try_into().map_err(|_| MessageError::MalformedMessage)?) as usize,
            None => return Ok(None), // header not fully arrived
        };
        
        // Length counts itself but not the type byte → minimum legal value is 4,
        // regardless of length_offset. (length_offset is buffer geometry, not length.)
        if length < 4 || length > MAX_MESSAGE_LEN {
            return Err(MessageError::MalformedMessage);
        }
        
        // Total frame occupies `length + length_offset` bytes; body starts after the
        // length field. None = frame not fully arrived = incomplete.
        let body = match buf.get(length_offset + 4 .. length + length_offset) {
            Some(b) => b,
            None => return Ok(None),
        };
        
        let message_type = match type_byte {
            Some(tb) => tb.to_type(expecting)?,
            None => {
                // Discriminant is the first 4 bytes of the body. Reading it here only
                // selects the type; the Startup/Cancel arms re-read from body as needed.
                let discrim = i32::from_be_bytes(
                    body.get(0..4).ok_or(MessageError::MalformedMessage)?
                        .try_into().map_err(|_| MessageError::MalformedMessage)?,
                );
                FrontendMessageType::from_discrim(discrim, expecting_startup)?
            },
        };

        let consumed = length + length_offset;
        
        let mut reader = BodyReader { buffer: body, pos: 0 };

        Ok(Some((match message_type {
            FrontendMessageType::Startup => {
                let version_major = reader.i16()?;
                let version_minor = reader.i16()?;

                let mut params = vec!();
                loop {
                    let name = reader.c_string()?;
                    if name.is_empty() { break; }
                    let value = reader.c_string()?;
                    params.push((name, value));
                }
                FrontendMessage::Startup { version_major, version_minor, params }
            }, FrontendMessageType::CancelRequest => {
                let _ = reader.i32()?;
                let pid = reader.i32()?;
                let key = reader.rest().to_owned();
                FrontendMessage::CancelRequest { pid, key }
            }, FrontendMessageType::SslRequest => FrontendMessage::SslRequest,
            FrontendMessageType::GssEnc => FrontendMessage::GssEnc,
            FrontendMessageType::GssResponse => FrontendMessage::GssResponse { data: reader.rest().to_owned() },
            FrontendMessageType::SaslInitialResponse => {
                let auth_mech = reader.c_string()?;
                let response_length = reader.i32()?;
                let sasl_initial = if response_length == -1 { None } 
                    else if response_length < -1 { return Err(MessageError::MalformedMessage) }
                    else { Some(reader.bytes(response_length as usize)?) };
                FrontendMessage::SaslInitialResponse { auth_mech, sasl_initial }
            }, FrontendMessageType::SaslResponse => FrontendMessage::SaslResponse { data: reader.rest().to_owned() },
            FrontendMessageType::PasswordMessage => FrontendMessage::PasswordMessage { password: reader.c_string()? },
            FrontendMessageType::Bind => {
                let dest_portal = reader.c_string()?;
                let src_prepared_stmt = reader.c_string()?;
                let num_formats = reader.i16()?;
                let mut formats = vec!();
                for _ in 0..num_formats {
                    formats.push(Format::try_from(reader.i16()?)?);
                }
                let num_params = reader.i16()?;
                let mut params = vec!();
                for _ in 0..num_params {
                    let param_len = reader.i32()?;
                    params.push( if param_len == -1 { None } 
                        else if param_len < -1 { return Err(MessageError::MalformedMessage)}
                        else { Some(reader.bytes(param_len as usize)?)});
                }
                let num_result_formats = reader.i16()?;
                let mut result_formats = vec!();
                for _ in 0..num_result_formats {
                    result_formats.push(Format::try_from(reader.i16()?)?);
                }
                FrontendMessage::Bind { dest_portal, src_prepared_stmt, formats, params, result_formats, }
            }, FrontendMessageType::Close => {
                let target = Target::try_from(reader.u8()?)?;
                let target_name = reader.c_string()?;
                FrontendMessage::Close { target, target_name }
            }, FrontendMessageType::CopyData => FrontendMessage::CopyData { data: reader.rest().to_owned() },
            FrontendMessageType::CopyDone => FrontendMessage::CopyDone,
            FrontendMessageType::CopyFail => FrontendMessage::CopyFail { error_message: reader.c_string()? },
            FrontendMessageType::Describe => {
                let target = Target::try_from(reader.u8()?)?;
                let target_name = reader.c_string()?;
                FrontendMessage::Describe { target, target_name }
            }, FrontendMessageType::Execute => {
                let portal_name = reader.c_string()?;
                let num_rows = reader.i32()?;
                FrontendMessage::Execute { portal_name, num_rows }
            }, FrontendMessageType::Flush => FrontendMessage::Flush,
            FrontendMessageType::FunctionCall => {
                let oid = reader.i32()?;
                let num_formats = reader.i16()?;
                let mut formats = vec!();
                for _ in 0..num_formats {
                    formats.push(Format::try_from(reader.i16()?)?);
                }
                let num_arguments = reader.i16()?;
                let mut arguments = vec!();
                for _ in 0..num_arguments {
                    let arg_len = reader.i32()?;
                    arguments.push(reader.bytes(arg_len as usize)?.to_owned());
                }
                let result_format = Format::try_from(reader.i16()?)?;
                FrontendMessage::FunctionCall { oid, formats, arguments, result_format }
            }, FrontendMessageType::Parse => {
                let dest_stmt_name = reader.c_string()?;
                let query = reader.c_string()?;
                let num_data_types = reader.i16()?;
                let mut param_data_type_oids = vec!();
                for _ in 0..num_data_types {
                    param_data_type_oids.push(reader.i32()?);
                }
                FrontendMessage::Parse { dest_stmt_name, query, param_data_type_oids }
            },
            FrontendMessageType::Query => {
                FrontendMessage::Query { query: reader.c_string()? }
            }, FrontendMessageType::Sync => FrontendMessage::Sync,
            FrontendMessageType::Terminate => FrontendMessage::Terminate,
        }, consumed)))
    }
}

#[repr(u8)]
#[derive(Copy,Clone)]
pub enum BackendMessageTypeByte {
    AuthenticationRequest = b'R',
    BackendKeyData = b'K',
    BindComplete = b'2',
    CloseComplete = b'3',
    CommandComplete = b'C',
    CopyData = b'd',
    CopyDone = b'c',
    CopyInResponse = b'G',
    CopyOutResponse = b'H',
    CopyBothResponse = b'W',
    DataRow = b'D',
    EmptyQueryResponse = b'I',
    ErrorResponse = b'E',
    FunctionCallResponse = b'V',
    NegotiateProtocolVersion = b'v',
    NoData = b'n',
    NoticeResponse = b'N',
    NotificationResponse = b'A',
    ParameterDescription = b't',
    ParameterStatus = b'S',
    ParseComplete = b'1',
    PortalSuspended = b's',
    ReadyForQuery = b'Z',
    RowDescription = b'T',
}

impl BackendMessageTypeByte {
    fn from_type(message_type: BackendMessageType) -> BackendMessageTypeByte {
        match message_type {
            BackendMessageType::BackendKeyData => BackendMessageTypeByte::BackendKeyData ,
            BackendMessageType::BindComplete => BackendMessageTypeByte::BindComplete ,
            BackendMessageType::CloseComplete => BackendMessageTypeByte::CloseComplete ,
            BackendMessageType::CommandComplete => BackendMessageTypeByte::CommandComplete ,
            BackendMessageType::CopyData => BackendMessageTypeByte::CopyData ,
            BackendMessageType::CopyDone => BackendMessageTypeByte::CopyDone ,
            BackendMessageType::CopyInResponse => BackendMessageTypeByte::CopyInResponse ,
            BackendMessageType::CopyOutResponse => BackendMessageTypeByte::CopyOutResponse ,
            BackendMessageType::CopyBothResponse => BackendMessageTypeByte::CopyBothResponse ,
            BackendMessageType::DataRow => BackendMessageTypeByte::DataRow ,
            BackendMessageType::EmptyQueryResponse => BackendMessageTypeByte::EmptyQueryResponse ,
            BackendMessageType::ErrorResponse => BackendMessageTypeByte::ErrorResponse ,
            BackendMessageType::FunctionCallResponse => BackendMessageTypeByte::FunctionCallResponse ,
            BackendMessageType::NegotiateProtocolVersion => BackendMessageTypeByte::NegotiateProtocolVersion ,
            BackendMessageType::NoData => BackendMessageTypeByte::NoData ,
            BackendMessageType::NoticeResponse => BackendMessageTypeByte::NoticeResponse ,
            BackendMessageType::NotificationResponse => BackendMessageTypeByte::NotificationResponse ,
            BackendMessageType::ParameterDescription => BackendMessageTypeByte::ParameterDescription ,
            BackendMessageType::ParameterStatus => BackendMessageTypeByte::ParameterStatus ,
            BackendMessageType::ParseComplete => BackendMessageTypeByte::ParseComplete ,
            BackendMessageType::PortalSuspended => BackendMessageTypeByte::PortalSuspended ,
            BackendMessageType::ReadyForQuery => BackendMessageTypeByte::ReadyForQuery ,
            BackendMessageType::RowDescription => BackendMessageTypeByte::RowDescription ,
            BackendMessageType::AuthenticationOk => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationKerberosV5 => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationCleartextPassword => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationMd5Password => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationGss => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationGssContinue => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationSppi => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationSasl => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationSaslContinue => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessageType::AuthenticationSaslFinal => BackendMessageTypeByte::AuthenticationRequest,
        }
    }
}


impl TryFrom<u8> for BackendMessageTypeByte {
    type Error = MessageError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            b'R' => BackendMessageTypeByte::AuthenticationRequest,
            b'K' => BackendMessageTypeByte::BackendKeyData,
            b'2' => BackendMessageTypeByte::BindComplete,
            b'3' => BackendMessageTypeByte::CloseComplete,
            b'C' => BackendMessageTypeByte::CommandComplete,
            b'd' => BackendMessageTypeByte::CopyData,
            b'c' => BackendMessageTypeByte::CopyDone,
            b'G' => BackendMessageTypeByte::CopyInResponse,
            b'H' => BackendMessageTypeByte::CopyOutResponse,
            b'W' => BackendMessageTypeByte::CopyBothResponse,
            b'D' => BackendMessageTypeByte::DataRow,
            b'I' => BackendMessageTypeByte::EmptyQueryResponse,
            b'E' => BackendMessageTypeByte::ErrorResponse,
            b'V' => BackendMessageTypeByte::FunctionCallResponse,
            b'v' => BackendMessageTypeByte::NegotiateProtocolVersion,
            b'n' => BackendMessageTypeByte::NoData,
            b'N' => BackendMessageTypeByte::NoticeResponse,
            b'A' => BackendMessageTypeByte::NotificationResponse,
            b't' => BackendMessageTypeByte::ParameterDescription,
            b'S' => BackendMessageTypeByte::ParameterStatus,
            b'1' => BackendMessageTypeByte::ParseComplete,
            b's' => BackendMessageTypeByte::PortalSuspended,
            b'Z' => BackendMessageTypeByte::ReadyForQuery,
            b'T' => BackendMessageTypeByte::RowDescription,
            _ => Err(MessageError::MalformedMessage)?,
        })
    }
}

pub enum BackendMessageType {
    // Types with 'R' type byte.
    AuthenticationOk,
    AuthenticationKerberosV5,
    AuthenticationCleartextPassword,
    AuthenticationMd5Password,
    AuthenticationGss,
    AuthenticationGssContinue,
    AuthenticationSppi,
    AuthenticationSasl,
    AuthenticationSaslContinue,
    AuthenticationSaslFinal,
    BackendKeyData, //'K'
    BindComplete, //'2'
    CloseComplete, //'3'
    CommandComplete, //'C'
    CopyData, //'d'
    CopyDone, //'c'
    CopyInResponse, //'G'
    CopyOutResponse, //'H'
    CopyBothResponse, //'W'
    DataRow, //'D'
    EmptyQueryResponse, //'I'
    ErrorResponse, //'E'
    FunctionCallResponse, //'V'
    NegotiateProtocolVersion, //'v'
    NoData, //'n'
    NoticeResponse, //'N'
    NotificationResponse, //'A'
    ParameterDescription, //'t'
    ParameterStatus, //'S'
    ParseComplete, //'1'
    PortalSuspended, //'s'
    ReadyForQuery, //'Z'
    RowDescription, //'T'
}

impl BackendMessageType {
    fn from_byte(byte: BackendMessageTypeByte, discrim: Option<i32>) -> Result<BackendMessageType, MessageError> {
        Ok(match byte {
            BackendMessageTypeByte::BackendKeyData => BackendMessageType::BackendKeyData ,
            BackendMessageTypeByte::BindComplete => BackendMessageType::BindComplete ,
            BackendMessageTypeByte::CloseComplete => BackendMessageType::CloseComplete ,
            BackendMessageTypeByte::CommandComplete => BackendMessageType::CommandComplete ,
            BackendMessageTypeByte::CopyData => BackendMessageType::CopyData ,
            BackendMessageTypeByte::CopyDone => BackendMessageType::CopyDone ,
            BackendMessageTypeByte::CopyInResponse => BackendMessageType::CopyInResponse ,
            BackendMessageTypeByte::CopyOutResponse => BackendMessageType::CopyOutResponse ,
            BackendMessageTypeByte::CopyBothResponse => BackendMessageType::CopyBothResponse ,
            BackendMessageTypeByte::DataRow => BackendMessageType::DataRow ,
            BackendMessageTypeByte::EmptyQueryResponse => BackendMessageType::EmptyQueryResponse ,
            BackendMessageTypeByte::ErrorResponse => BackendMessageType::ErrorResponse ,
            BackendMessageTypeByte::FunctionCallResponse => BackendMessageType::FunctionCallResponse ,
            BackendMessageTypeByte::NegotiateProtocolVersion => BackendMessageType::NegotiateProtocolVersion ,
            BackendMessageTypeByte::NoData => BackendMessageType::NoData ,
            BackendMessageTypeByte::NoticeResponse => BackendMessageType::NoticeResponse ,
            BackendMessageTypeByte::NotificationResponse => BackendMessageType::NotificationResponse ,
            BackendMessageTypeByte::ParameterDescription => BackendMessageType::ParameterDescription ,
            BackendMessageTypeByte::ParameterStatus => BackendMessageType::ParameterStatus ,
            BackendMessageTypeByte::ParseComplete => BackendMessageType::ParseComplete ,
            BackendMessageTypeByte::PortalSuspended => BackendMessageType::PortalSuspended ,
            BackendMessageTypeByte::ReadyForQuery => BackendMessageType::ReadyForQuery ,
            BackendMessageTypeByte::RowDescription => BackendMessageType::RowDescription ,
            BackendMessageTypeByte::AuthenticationRequest => match discrim {
                Some(0) => BackendMessageType::AuthenticationOk,
                Some(2) => BackendMessageType::AuthenticationKerberosV5,
                Some(3) => BackendMessageType::AuthenticationCleartextPassword,
                Some(5) => BackendMessageType::AuthenticationMd5Password,
                Some(7) => BackendMessageType::AuthenticationGss,
                Some(8) => BackendMessageType::AuthenticationGssContinue,
                Some(9) => BackendMessageType::AuthenticationSppi,
                Some(10) => BackendMessageType::AuthenticationSasl,
                Some(11) => BackendMessageType::AuthenticationSaslContinue,
                Some(12) => BackendMessageType::AuthenticationSaslFinal,
                _ => Err(MessageError::MalformedMessage)?,
            },
        })
    }
}

pub enum BackendMessage {
    AuthenticationOk,
    AuthenticationKerberosV5,
    AuthenticationCleartextPassword,
    AuthenticationMd5Password {
        salt: [u8; 4],
    },
    AuthenticationGss,
    AuthenticationGssContinue {
        data: Vec<u8>,
    },
    AuthenticationSppi,
    AuthenticationSasl {
        mechanism_names: Vec<CString>,
    },
    AuthenticationSaslContinue {
        data: Vec<u8>,
    },
    AuthenticationSaslFinal {
        data: Vec<u8>,
    },
    BackendKeyData {
        pid: i32,
        key: Vec<u8>,
    }, //'K'
    BindComplete, //'2'
    CloseComplete, //'3'
    CommandComplete {
           tag: CommandTag,
       }, //'C'
    CopyData {
        data: Vec<u8>,
    }, //'d'
    CopyDone, //'c'
    CopyInResponse {
        overall_format: Format,
        formats: Vec<Format>,
    }, //'G'
    CopyOutResponse {
        overall_format: Format,
        formats: Vec<Format>,
    }, //'H'
    CopyBothResponse {
        overall_format: Format,
        formats: Vec<Format>,
    }, //'W'
    DataRow {
        data: Vec<Option<Vec<u8>>>,
    }, //'D'
    EmptyQueryResponse, //'I'
    ErrorResponse {
        errors: Vec<(Option<ErrorType>, CString)>
    }, //'E'
    FunctionCallResponse {
        value: Option<Vec<u8>>,
    }, //'V'
    NegotiateProtocolVersion {
        minor_version: i32,
        unrecognized_options: Vec<CString>,
    }, //'v'
    NoData, //'n'
    NoticeResponse {
        notices: Vec<(Option<NoticeType>, CString)>,
    }, //'N'
    NotificationResponse {
        pid: i32,
        channel_name: CString,
        payload: CString,
    }, //'A'
    ParameterDescription {
        object_ids: Vec<i32>,
    }, //'t'
    ParameterStatus {
        param_name: CString,
        param_value: CString,
    }, //'S'
    ParseComplete, //'1'
    PortalSuspended, //'s'
    ReadyForQuery {
        transaction_status: TransactionStatus,
    }, //'Z'
    RowDescription {
        fields: Vec<RowDescription>,
    }, //'T'
}

impl BackendMessage {
    fn get_type_byte(&self) -> BackendMessageTypeByte {
        match self {
            BackendMessage::AuthenticationOk => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationKerberosV5 => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationCleartextPassword => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationMd5Password { .. } => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationGss => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationGssContinue { .. } => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationSppi => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationSasl { .. } => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationSaslContinue { .. } => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::AuthenticationSaslFinal { .. } => BackendMessageTypeByte::AuthenticationRequest,
            BackendMessage::BackendKeyData { .. } => BackendMessageTypeByte::BackendKeyData,
            BackendMessage::BindComplete => BackendMessageTypeByte::BindComplete,
            BackendMessage::CloseComplete => BackendMessageTypeByte::CloseComplete,
            BackendMessage::CommandComplete { .. } => BackendMessageTypeByte::CommandComplete,
            BackendMessage::CopyData { .. } => BackendMessageTypeByte::CopyData,
            BackendMessage::CopyDone => BackendMessageTypeByte::CopyDone,
            BackendMessage::CopyInResponse { .. } => BackendMessageTypeByte::CopyInResponse,
            BackendMessage::CopyOutResponse { .. } => BackendMessageTypeByte::CopyOutResponse,
            BackendMessage::CopyBothResponse { .. } => BackendMessageTypeByte::CopyBothResponse,
            BackendMessage::DataRow { .. } => BackendMessageTypeByte::DataRow,
            BackendMessage::EmptyQueryResponse => BackendMessageTypeByte::EmptyQueryResponse,
            BackendMessage::ErrorResponse { .. } => BackendMessageTypeByte::ErrorResponse,
            BackendMessage::FunctionCallResponse { .. } => BackendMessageTypeByte::FunctionCallResponse,
            BackendMessage::NegotiateProtocolVersion { .. } => BackendMessageTypeByte::NegotiateProtocolVersion,
            BackendMessage::NoData => BackendMessageTypeByte::NoData,
            BackendMessage::NoticeResponse { .. } => BackendMessageTypeByte::NoticeResponse,
            BackendMessage::NotificationResponse { .. } => BackendMessageTypeByte::NotificationResponse,
            BackendMessage::ParameterDescription { .. } => BackendMessageTypeByte::ParameterDescription,
            BackendMessage::ParameterStatus { .. } => BackendMessageTypeByte::ParameterStatus,
            BackendMessage::ParseComplete => BackendMessageTypeByte::ParseComplete,
            BackendMessage::PortalSuspended => BackendMessageTypeByte::PortalSuspended,
            BackendMessage::ReadyForQuery { .. } => BackendMessageTypeByte::ReadyForQuery,
            BackendMessage::RowDescription { .. } => BackendMessageTypeByte::RowDescription,
        }
    }

    fn to_buffer(&self) -> Result<Vec<u8>, MessageError> {
        let mut buf: Vec<u8> = vec!();
        let mut body: Vec<u8> = vec!();

        buf.push(self.get_type_byte() as u8);

        let length = match self {
            BackendMessage::AuthenticationOk => {
                body.extend_from_slice(0i32.to_be_bytes().as_slice()); 8
            }, BackendMessage::AuthenticationKerberosV5 =>  {
                body.extend_from_slice(2i32.to_be_bytes().as_slice()); 8
            }, BackendMessage::AuthenticationCleartextPassword => {
                body.extend_from_slice(3i32.to_be_bytes().as_slice()); 8
            }, BackendMessage::AuthenticationMd5Password { salt } => {
                body.extend_from_slice(5i32.to_be_bytes().as_slice());
                body.extend_from_slice(salt); 12
            }, BackendMessage::AuthenticationGss => {
                body.extend_from_slice(7i32.to_be_bytes().as_slice()); 8
            }, BackendMessage::AuthenticationGssContinue { data } => {
                body.extend_from_slice(8i32.to_be_bytes().as_slice());
                body.extend_from_slice(data.as_slice()); 8 + data.len() as i32
            }, BackendMessage::AuthenticationSppi => {
                body.extend_from_slice(9i32.to_be_bytes().as_slice()); 8
            }, BackendMessage::AuthenticationSasl { mechanism_names } => {
                body.extend_from_slice(10i32.to_be_bytes().as_slice());
                let length = mechanism_names.iter().fold(0, |acc,n| {
                    body.extend_from_slice(n.as_bytes_with_nul());
                    acc + n.count_bytes() + 1
                }) as i32 + 9; body.push(b'\0'); length
            }, BackendMessage::AuthenticationSaslContinue { data } => {
                body.extend_from_slice(11i32.to_be_bytes().as_slice());
                body.extend_from_slice(data.as_slice()); 8 + data.len() as i32
            }, BackendMessage::AuthenticationSaslFinal { data } => {
                body.extend_from_slice(12i32.to_be_bytes().as_slice());
                body.extend_from_slice(data.as_slice()); 8 + data.len() as i32
            }, BackendMessage::BackendKeyData { pid, key } => {
                body.extend_from_slice(pid.to_be_bytes().as_slice());
                body.extend_from_slice(key);
                8 + key.len() as i32
            }, BackendMessage::BindComplete => 4,
            BackendMessage::CloseComplete => 4,
            BackendMessage::CommandComplete { tag } => {
                let command_string: CString = tag.as_cstring();
                body.extend_from_slice(command_string.as_bytes_with_nul());
                4 + command_string.count_bytes() as i32 + 1 as i32
            }, BackendMessage::CopyData { data } => {
                body.extend_from_slice(data.as_slice());
                4 + data.len() as i32
            }, BackendMessage::CopyDone => 4,
            BackendMessage::CopyInResponse { overall_format, formats } => {
                body.push(*overall_format as u8);
                body.extend_from_slice((formats.len() as i16).to_be_bytes().as_slice());
                for f in formats { body.extend_from_slice((*f as i16).to_be_bytes().as_slice())};
                (7+ formats.len()*2) as i32
            }, BackendMessage::CopyOutResponse { overall_format, formats } => {
                body.push(*overall_format as u8);
                body.extend_from_slice((formats.len() as i16).to_be_bytes().as_slice());
                for f in formats { body.extend_from_slice((*f as i16).to_be_bytes().as_slice())};
                (7+ formats.len()*2) as i32
            }, BackendMessage::CopyBothResponse { overall_format, formats } => {
                body.push(*overall_format as u8);
                body.extend_from_slice((formats.len() as i16).to_be_bytes().as_slice());
                for f in formats { body.extend_from_slice((*f as i16).to_be_bytes().as_slice())};
                (7+ formats.len()*2) as i32
            }, BackendMessage::DataRow { data } => {
                let mut length = 6i32; // 4 (length field) + 2 (column count)
                body.extend_from_slice((data.len() as i16).to_be_bytes().as_slice());
                for col in data {
                    match col {
                        Some(c) => {
                            body.extend_from_slice((c.len() as i32).to_be_bytes().as_slice());
                            body.extend_from_slice(c.as_slice());
                            length += 4 + c.len() as i32;
                        }, None => {
                            body.extend_from_slice((-1i32).to_be_bytes().as_slice());
                            length += 4;
                        }
                    }
                }
                length
            }, BackendMessage::EmptyQueryResponse => 4,
            BackendMessage::ErrorResponse { errors } => {
                let mut length = 4i32;
                for error in errors {
                    // None = a field type we didn't recognise on parse; its original
                    // byte is gone, so we drop it rather than emit a garbage field.
                    if let Some(e) = error.0 {
                        body.push(e as u8);
                        body.extend_from_slice(error.1.as_bytes_with_nul());
                        length += 1 + error.1.count_bytes() as i32 + 1;
                    }
                }
                body.push(b'\0'); // the field list is terminated by a zero byte
                length + 1
            } BackendMessage::FunctionCallResponse { value } => {
                match value {
                    Some(v) => {
                        body.extend_from_slice((v.len() as i32).to_be_bytes().as_slice());
                        body.extend_from_slice(v.as_slice());
                        8 + v.len() as i32
                    }, None => {
                        body.extend_from_slice((-1i32).to_be_bytes().as_slice());
                        8
                    }
                }
            } BackendMessage::NegotiateProtocolVersion { minor_version, unrecognized_options } => {
                let mut length = 12i32;
                body.extend_from_slice(minor_version.to_be_bytes().as_slice());
                body.extend_from_slice((unrecognized_options.len() as i32).to_be_bytes().as_slice());
                for ur_opt in unrecognized_options {
                    body.extend_from_slice(ur_opt.as_bytes_with_nul());
                    length += ur_opt.count_bytes() as i32 + 1;
                }
                length
            }, BackendMessage::NoData => 4,
            BackendMessage::NoticeResponse { notices } => {
                let mut length = 4i32;
                for notice in notices {
                    if let Some(n) = notice.0 {
                        body.push(n as u8);
                        body.extend_from_slice(notice.1.as_bytes_with_nul());
                        length += 1 + notice.1.count_bytes() as i32 + 1;
                    }
                }
                body.push(b'\0'); // the field list is terminated by a zero byte
                length + 1
            }, BackendMessage::NotificationResponse { pid, channel_name, payload } => {
                body.extend_from_slice(pid.to_be_bytes().as_slice());
                body.extend_from_slice(channel_name.as_bytes_with_nul());
                body.extend_from_slice(payload.as_bytes_with_nul());
                8 + channel_name.count_bytes() as i32+ payload.count_bytes() as i32 + 2
            }, BackendMessage::ParameterDescription { object_ids } => {
                body.extend_from_slice((object_ids.len() as i16).to_be_bytes().as_slice());
                for o_id in object_ids {
                    body.extend_from_slice((*o_id).to_be_bytes().as_slice());
                }
                6 + object_ids.len() as i32 * 4
            }, BackendMessage::ParameterStatus { param_name, param_value } => {
                body.extend_from_slice(param_name.as_bytes_with_nul());
                body.extend_from_slice(param_value.as_bytes_with_nul());
                4 + param_name.count_bytes() as i32 + param_value.count_bytes() as i32 + 2
            }, BackendMessage::ParseComplete => 4,
            BackendMessage::PortalSuspended => 4,
            BackendMessage::ReadyForQuery { transaction_status } => {
                body.push(*transaction_status as u8);
                5
            }, BackendMessage::RowDescription { fields } => {
                let mut length = 6;
                body.extend_from_slice((fields.len() as i16).to_be_bytes().as_slice());
                for field in fields {
                    length += 19 + field.name.count_bytes() as i32;
                    body.extend_from_slice(field.name.as_bytes_with_nul());
                    match field.table_oid {
                        Some(oid) => body.extend_from_slice(oid.to_be_bytes().as_slice()),
                        None => body.extend_from_slice((0i32).to_be_bytes().as_slice()),
                    };
                    match field.attr_num {
                        Some(attr) => body.extend_from_slice(attr.to_be_bytes().as_slice()),
                        None => body.extend_from_slice((0i16).to_be_bytes().as_slice()),
                    };
                    body.extend_from_slice(field.type_oid.to_be_bytes().as_slice());
                    body.extend_from_slice(field.type_size.to_be_bytes().as_slice());
                    body.extend_from_slice(field.type_modifier.to_be_bytes().as_slice());
                    body.extend_from_slice((field.format as i16).to_be_bytes().as_slice());
                }
                length
            },
        };

        buf.extend_from_slice(&(length as i32).to_be_bytes());
        buf.extend_from_slice(body.as_slice());

        Ok(buf)
    }

    //fn from_buffer(buf: &[u8]) -> Result<Option<BackendMessage>, MessageError> {
    //    if buf.len() < 4 { return Ok(None) }

    //    let type_byte = BackendMessageTypeByte::try_from(buf[0])?;

    //    let length = i32::from_be_bytes(
    //        buf.get(1..5).ok_or(MessageError::MalformedMessage)?  // or `else return Ok(None)` — see below
    //            .try_into().map_err(|_| MessageError::MalformedMessage)?,
    //    ) as usize;
    //    if length < 4 { return Err(MessageError::MalformedMessage)}

    //    let (discrim, body_offset) = match type_byte {
    //        BackendMessageTypeByte::AuthenticationRequest => (
    //            Some(i32::from_be_bytes(buf[5..9].try_into().map_err(|_| MessageError::MalformedMessage)?)),
    //            9
    //        ), _ => (
    //            None,
    //            5
    //        ),
    //    };

    //    let body = match buf.get(body_offset..length+1) {
    //        Some(b) => b,
    //        None => return Ok(None),
    //    };

    //    let message_type = BackendMessageType::from_byte(type_byte, discrim)?;

    //    let mut reader = BodyReader { buffer: body, pos: 0 };
    fn from_buffer(buf: &[u8]) -> Result<Option<(BackendMessage,usize)>, MessageError> {
        // Need at least the type byte + 4-byte length to read the header at all.
        let header = match buf.get(0..5) {
            Some(h) => h,
            None => return Ok(None), // incomplete: header not fully arrived
        };
    
        let type_byte = BackendMessageTypeByte::try_from(header[0])?;
        let length = i32::from_be_bytes(
            header[1..5].try_into().map_err(|_| MessageError::MalformedMessage)?,
        ) as usize;
    
        // Length counts itself (the 4 length bytes) but not the type byte, so the
        // smallest legal value is 4. Below that the frame is malformed, not short;
        // above MAX_MESSAGE_LEN it's a corrupt/hostile length we refuse to wait for.
        if length < 4 || length > MAX_MESSAGE_LEN {
            return Err(MessageError::MalformedMessage);
        }
    
        // AuthenticationRequest carries a 4-byte sub-discriminant at the front of
        // its body; everything else starts its body right after the length field.
        let body_offset = match type_byte {
            BackendMessageTypeByte::AuthenticationRequest => 9,
            _ => 5,
        };
    
        // The declared length must be able to cover whatever header we consume past
        // the length field. Total frame occupies `length + 1` bytes (type byte is
        // the +1, not counted in length), so the body ends at `length + 1`.
        // If that end is before where the body starts, the length is lying → malformed.
        if length + 1 < body_offset {
            return Err(MessageError::MalformedMessage);
        }
    
        // For auth, read the discriminant — but only once we know the bytes are present.
        // Missing here means the frame isn't fully in the buffer yet (incomplete),
        // *not* malformed, because `length` already claims to cover these bytes.
        let discrim = match type_byte {
            BackendMessageTypeByte::AuthenticationRequest => {
                match buf.get(5..9) {
                    Some(d) => Some(i32::from_be_bytes(
                        d.try_into().map_err(|_| MessageError::MalformedMessage)?,
                    )),
                    None => return Ok(None),
                }
            }
            _ => None,
        };
    
        // The body is everything from body_offset to the end of the frame.
        // None here = frame not fully arrived = incomplete.
        let body = match buf.get(body_offset..length + 1) {
            Some(b) => b,
            None => return Ok(None),
        };
    
        let consumed = length + 1;
        let message_type = BackendMessageType::from_byte(type_byte, discrim)?;
        let mut reader = BodyReader { buffer: body, pos: 0 };
        Ok(Some((match message_type {
            BackendMessageType::AuthenticationOk => BackendMessage::AuthenticationOk,
            BackendMessageType::AuthenticationKerberosV5 => BackendMessage::AuthenticationKerberosV5,
            BackendMessageType::AuthenticationCleartextPassword => BackendMessage::AuthenticationCleartextPassword,
            BackendMessageType::AuthenticationMd5Password => BackendMessage::AuthenticationMd5Password { salt: reader.bytes(4)?.as_slice().try_into().map_err(|_|MessageError::MalformedMessage)? },
            BackendMessageType::AuthenticationGss => BackendMessage::AuthenticationGss,
            BackendMessageType::AuthenticationGssContinue => BackendMessage::AuthenticationGssContinue { data: reader.rest().to_owned() },
            BackendMessageType::AuthenticationSppi => BackendMessage::AuthenticationSppi,
            BackendMessageType::AuthenticationSasl => {
                let mut mechanism_names = vec!();
                loop {
                    if reader.peek()? == b'\0' { break }
                    mechanism_names.push(reader.c_string()?);
                }
                BackendMessage::AuthenticationSasl { mechanism_names }
            }, BackendMessageType::AuthenticationSaslContinue => BackendMessage::AuthenticationSaslContinue { data: reader.rest().to_owned() },
            BackendMessageType::AuthenticationSaslFinal => BackendMessage::AuthenticationSaslFinal { data: reader.rest().to_owned() },
            BackendMessageType::BackendKeyData => BackendMessage::BackendKeyData { pid: reader.i32()?, key: reader.rest().to_owned() },
            BackendMessageType::BindComplete => BackendMessage::BindComplete,
            BackendMessageType::CloseComplete => BackendMessage::CloseComplete,
            BackendMessageType::CommandComplete => BackendMessage::CommandComplete { tag: CommandTag::from_cstring(reader.c_string()?)? },
            BackendMessageType::CopyData => BackendMessage::CopyData { data: reader.rest().to_owned() },
            BackendMessageType::CopyDone => BackendMessage::CopyDone,
            BackendMessageType::CopyInResponse => {
                let overall_format = Format::try_from(reader.i8()?)?;
                let num_formats = reader.i16()?;
                let mut formats = vec!();
                for _ in 0..num_formats {
                    formats.push(Format::try_from(reader.i16()?)?);
                }
                BackendMessage::CopyInResponse { overall_format, formats }
            }, BackendMessageType::CopyOutResponse => {
                let overall_format = Format::try_from(reader.i8()?)?;
                let num_formats = reader.i16()?;
                let mut formats = vec!();
                for _ in 0..num_formats {
                    formats.push(Format::try_from(reader.i16()?)?);
                }
                BackendMessage::CopyOutResponse { overall_format, formats }
            }, BackendMessageType::CopyBothResponse => {
                let overall_format = Format::try_from(reader.i8()?)?;
                let num_formats = reader.i16()?;
                let mut formats = vec!();
                for _ in 0..num_formats {
                    formats.push(Format::try_from(reader.i16()?)?);
                }
                BackendMessage::CopyBothResponse { overall_format, formats }
            }, BackendMessageType::DataRow => {
                let num_values = reader.i16()?;
                let mut data = vec!();
                for _ in 0..num_values {
                    let value_len = reader.i32()?;
                    data.push( if value_len == -1 { None } 
                        else if value_len < -1 { return Err(MessageError::MalformedMessage) }
                        else { Some(reader.bytes(value_len as usize)?) });
                }
                BackendMessage::DataRow { data }
            }, BackendMessageType::EmptyQueryResponse => BackendMessage::EmptyQueryResponse,
            BackendMessageType::ErrorResponse => {
                let mut errors = vec!();
                loop {
                    let type_byte = reader.u8()?;
                    if type_byte == b'\0' { break }
                    let error_type = ErrorType::from_byte(type_byte);
                    let error_value = reader.c_string()?;
                    errors.push((error_type, error_value));
                }
                BackendMessage::ErrorResponse { errors }
            }, BackendMessageType::FunctionCallResponse => {
                let value_len = reader.i32()?;
                let value = if value_len == -1 { None } 
                    else if value_len < -1 { return Err(MessageError::MalformedMessage) }
                    else {Some(reader.bytes(value_len as usize)?)};
                BackendMessage::FunctionCallResponse { value }
            }, BackendMessageType::NegotiateProtocolVersion => {
                let minor_version = reader.i32()?;
                let num_unrecognized_options = reader.i32()?;
                let mut unrecognized_options = vec!();
                for _ in 0..num_unrecognized_options {
                    unrecognized_options.push(reader.c_string()?);
                }
                BackendMessage::NegotiateProtocolVersion { minor_version, unrecognized_options }
            }, BackendMessageType::NoData => BackendMessage::NoData,
            BackendMessageType::NoticeResponse => {
                let mut notices = vec!();
                loop {
                    let type_byte = reader.u8()?;
                    if type_byte == b'\0' { break }
                    let notice_type = NoticeType::from_byte(type_byte);
                    let notice_value = reader.c_string()?;
                    notices.push((notice_type, notice_value));
                }
                BackendMessage::NoticeResponse { notices }
            }, BackendMessageType::NotificationResponse => {
                let pid = reader.i32()?;
                let channel_name = reader.c_string()?;
                let payload = reader.c_string()?;
                BackendMessage::NotificationResponse { pid, channel_name, payload }
            }, BackendMessageType::ParameterDescription => {
                let num_oids = reader.i16()?;
                let mut object_ids = vec!();
                for _ in 0..num_oids {
                    object_ids.push(reader.i32()?);
                }
                BackendMessage::ParameterDescription { object_ids }
            }, BackendMessageType::ParameterStatus => BackendMessage::ParameterStatus { param_name: reader.c_string()?, param_value: reader.c_string()? },
            BackendMessageType::ParseComplete => BackendMessage::ParseComplete,
            BackendMessageType::PortalSuspended => BackendMessage::PortalSuspended,
            BackendMessageType::ReadyForQuery => BackendMessage::ReadyForQuery { transaction_status: TransactionStatus::try_from(reader.u8()?)? },
            BackendMessageType::RowDescription => {
                let num_fields = reader.i16()?;
                let mut fields = vec!();
                for _ in 0..num_fields {
                    // Wire order is name first, then table OID, then column attr number.
                    let name = reader.c_string()?;
                    let table_oid = match reader.i32()? {
                        0 => None,
                        s => Some(s),
                    };
                    let attr_num = match reader.i16()? {
                        0 => None,
                        s => Some(s),
                    };
                    fields.push(RowDescription {
                        name,
                        table_oid,
                        attr_num,
                        type_oid: reader.i32()?,
                        type_size: reader.i16()?,
                        type_modifier: reader.i32()?,
                        format: Format::try_from(reader.i16()?)?,
                    });
                }
                BackendMessage::RowDescription { fields }
            },
        }, consumed)))
    }
}

#[derive(Copy,Clone)]
#[repr(u8)]
pub enum Target {
    Statement = b'S',
    Portal = b'P',
}

impl TryFrom<u8> for Target {
    type Error = MessageError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            b'S' => Target::Statement,
            b'P' => Target::Portal,
            _ => Err(MessageError::MalformedMessage)?,
        })
    }
}

pub struct RowDescription {
    name: CString,
    table_oid: Option<i32>,
    attr_num: Option<i16>,
    type_oid: i32,
    type_size: i16,
    type_modifier: i32,
    format: Format,
}

#[repr(u8)]
#[derive(Copy,Clone)]
pub enum ErrorType {
    SeverityLocalized = b'S',
    Severity = b'V',
    Code = b'C',
    Message = b'M',
    Detail = b'D',
    Hint = b'H',
    Position = b'P',
    InternalPosition = b'p',
    InternalQuery = b'q',
    Where = b'W',
    SchemaName = b's',
    TableName = b't',
    ColumnName = b'c',
    DataTypeName = b'd',
    ConstraintName = b'n',
    FileName = b'F',
    Line = b'L',
    Routine = b'R',
}

impl TryFrom<u8> for ErrorType {
    type Error = MessageError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            b'S' => ErrorType::SeverityLocalized,
            b'V' => ErrorType::Severity,
            b'C' => ErrorType::Code,
            b'M' => ErrorType::Message,
            b'D' => ErrorType::Detail,
            b'H' => ErrorType::Hint,
            b'P' => ErrorType::Position,
            b'p' => ErrorType::InternalPosition,
            b'q' => ErrorType::InternalQuery,
            b'W' => ErrorType::Where,
            b's' => ErrorType::SchemaName,
            b't' => ErrorType::TableName,
            b'c' => ErrorType::ColumnName,
            b'd' => ErrorType::DataTypeName,
            b'n' => ErrorType::ConstraintName,
            b'F' => ErrorType::FileName,
            b'L' => ErrorType::Line,
            b'R' => ErrorType::Routine,
            _ => Err(MessageError::MalformedMessage)?,
        })
    }
}

impl ErrorType {
    fn from_byte(byte: u8) -> Option<ErrorType> {
        match byte {
            b'S' => Some(ErrorType::SeverityLocalized),
            b'V' => Some(ErrorType::Severity),
            b'C' => Some(ErrorType::Code),
            b'M' => Some(ErrorType::Message),
            b'D' => Some(ErrorType::Detail),
            b'H' => Some(ErrorType::Hint),
            b'P' => Some(ErrorType::Position),
            b'p' => Some(ErrorType::InternalPosition),
            b'q' => Some(ErrorType::InternalQuery),
            b'W' => Some(ErrorType::Where),
            b's' => Some(ErrorType::SchemaName),
            b't' => Some(ErrorType::TableName),
            b'c' => Some(ErrorType::ColumnName),
            b'd' => Some(ErrorType::DataTypeName),
            b'n' => Some(ErrorType::ConstraintName),
            b'F' => Some(ErrorType::FileName),
            b'L' => Some(ErrorType::Line),
            b'R' => Some(ErrorType::Routine),
            _ => None,
        }
    }
}



#[repr(u8)]
#[derive(Copy,Clone)]
pub enum NoticeType {
    SeverityLocalized = b'S',
    Severity = b'V',
    Code = b'C',
    Message = b'M',
    Detail = b'D',
    Hint = b'H',
    Position = b'P',
    InternalPosition = b'p',
    InternalQuery = b'q',
    Where = b'W',
    SchemaName = b's',
    TableName = b't',
    ColumnName = b'c',
    DataTypeName = b'd',
    ConstraintName = b'n',
    FileName = b'F',
    Line = b'L',
    Routine = b'R',
}

impl TryFrom<u8> for NoticeType {
    type Error = MessageError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            b'S' => NoticeType::SeverityLocalized,
            b'V' => NoticeType::Severity,
            b'C' => NoticeType::Code,
            b'M' => NoticeType::Message,
            b'D' => NoticeType::Detail,
            b'H' => NoticeType::Hint,
            b'P' => NoticeType::Position,
            b'p' => NoticeType::InternalPosition,
            b'q' => NoticeType::InternalQuery,
            b'W' => NoticeType::Where,
            b's' => NoticeType::SchemaName,
            b't' => NoticeType::TableName,
            b'c' => NoticeType::ColumnName,
            b'd' => NoticeType::DataTypeName,
            b'n' => NoticeType::ConstraintName,
            b'F' => NoticeType::FileName,
            b'L' => NoticeType::Line,
            b'R' => NoticeType::Routine,
            _ => Err(MessageError::MalformedMessage)?,
        })
    }
}

impl NoticeType {
    fn from_byte(byte: u8) -> Option<NoticeType> {
        match byte {
            b'S' => Some(NoticeType::SeverityLocalized),
            b'V' => Some(NoticeType::Severity),
            b'C' => Some(NoticeType::Code),
            b'M' => Some(NoticeType::Message),
            b'D' => Some(NoticeType::Detail),
            b'H' => Some(NoticeType::Hint),
            b'P' => Some(NoticeType::Position),
            b'p' => Some(NoticeType::InternalPosition),
            b'q' => Some(NoticeType::InternalQuery),
            b'W' => Some(NoticeType::Where),
            b's' => Some(NoticeType::SchemaName),
            b't' => Some(NoticeType::TableName),
            b'c' => Some(NoticeType::ColumnName),
            b'd' => Some(NoticeType::DataTypeName),
            b'n' => Some(NoticeType::ConstraintName),
            b'F' => Some(NoticeType::FileName),
            b'L' => Some(NoticeType::Line),
            b'R' => Some(NoticeType::Routine),
            _ => None,
        }
    }
}


#[repr(u8)]
#[derive(Copy,Clone)]
pub enum TransactionStatus {
    Idle = b'I',
    InTransaction = b'T',
    InFailedTransaction = b'E',
}

impl TryFrom<u8> for TransactionStatus {
    type Error = MessageError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        Ok(match value {
            b'I' => Self::Idle,
            b'T' => Self::InTransaction,
            b'E' => Self::InFailedTransaction,
            _ => Err(MessageError::MalformedMessage)?,
        })
    }
}

#[derive(Clone)]
pub enum CommandTag {
    Insert {
        oid: i32,
        rows: i32,
    },
    Delete {
        rows: i32,
    },
    Update {
        rows: i32,
    },
    Merge {
        rows: i32,
    },
    Select {
        rows: i32,
    },
    Move {
        rows: i32,
    },
    Fetch {
        rows: i32,
    },
    Copy {
        rows: Option<i32>,
    },
    Other {
        string: CString,
    }
}

impl CommandTag {
    fn as_cstring(&self) -> CString {
        match self {
            CommandTag::Insert { oid, rows } => CString::new(format!("INSERT 0 {}",rows)).unwrap(),
            CommandTag::Delete { rows } => CString::new(format!("DELETE {}", rows)).unwrap(),
            CommandTag::Update { rows } => CString::new(format!("UPDATE {}", rows)).unwrap(),
            CommandTag::Merge { rows } => CString::new(format!("MERGE {}", rows)).unwrap(),
            CommandTag::Select { rows } => CString::new(format!("SELECT {}", rows)).unwrap(),
            CommandTag::Move { rows } => CString::new(format!("MOVE {}", rows)).unwrap(),
            CommandTag::Fetch { rows } => CString::new(format!("FETCH {}", rows)).unwrap(),
            CommandTag::Copy { rows } => match rows {
                Some(r) => CString::new(format!("COPY {}", r)).unwrap(),
                None => c"COPY".to_owned(),
            },
            CommandTag::Other { string } => string.clone(),
        }
    }

    fn from_cstring(c_string: CString) -> Result<CommandTag, MessageError> {
        let s = c_string.to_str().map_err(|_| MessageError::MalformedMessage)?;

        let mut parts = s.split(' ');
        let verb = parts.next().ok_or(MessageError::MalformedMessage)?;
        let rest: Vec<&str> = parts.collect();

        fn num(s: &str) -> Result<i32, MessageError> {
            s.parse().map_err(|_| MessageError::MalformedMessage)
        }

        Ok(match (verb, rest.as_slice()) {
            ("INSERT", [oid, rows]) => CommandTag::Insert { oid: num(oid)?, rows: num(rows)? },
            ("DELETE", [rows])      => CommandTag::Delete { rows: num(rows)? },
            ("UPDATE", [rows])      => CommandTag::Update { rows: num(rows)? },
            ("MERGE",  [rows])      => CommandTag::Merge  { rows: num(rows)? },
            ("SELECT", [rows])      => CommandTag::Select { rows: num(rows)? },
            ("MOVE",   [rows])      => CommandTag::Move   { rows: num(rows)? },
            ("FETCH",  [rows])      => CommandTag::Fetch  { rows: num(rows)? },
            ("COPY",   [rows])      => CommandTag::Copy   { rows: Some(num(rows)?) },
            ("COPY",   [])          => CommandTag::Copy   { rows: None },
            _ => CommandTag::Other { string: c_string },
        })
    }
}

#[derive(Copy,Clone)]
#[repr(i16)]
pub enum Format {
    Text = 0,
    Binary = 1,
}

impl TryFrom<i16> for Format {
    type Error = MessageError;
    fn try_from(value: i16) -> Result<Self, Self::Error> {
        Ok( match value {
            0 => Format::Text,
            1 => Format::Binary,
            _ => Err(MessageError::MalformedMessage)?,
        })
    }
}

impl TryFrom<i8> for Format {
    type Error = MessageError;

    fn try_from(value: i8) -> Result<Self, Self::Error> {
        // The 1-byte overall_format in Copy responses; defer to the i16 impl so
        // there's a single place that decides what a valid format code is.
        Format::try_from(value as i16)
    }
}

pub struct BodyReader<'a> {
    buffer: &'a [u8],
    pos: usize,
}

impl<'a> BodyReader<'a> {
    fn i32(&mut self) -> Result<i32, MessageError> {
        let result = Ok(i32::from_be_bytes(self.buffer.get(self.pos..self.pos+4).ok_or(MessageError::MalformedMessage)?.try_into().map_err(|_| MessageError::MalformedMessage)?));
        self.pos += 4;
        result
    }

    fn i16(&mut self) -> Result<i16, MessageError> {
        let result = i16::from_be_bytes(self.buffer.get(self.pos..self.pos+2).ok_or(MessageError::MalformedMessage)?.try_into().map_err(|_| MessageError::MalformedMessage)?);
        self.pos += 2;
        Ok(result)
    }

    fn u8(&mut self) -> Result<u8, MessageError> {
        let result:u8 = *self.buffer.get(self.pos).ok_or(MessageError::MalformedMessage)?;
        self.pos += 1;
        Ok(result)
    }

    fn i8(&mut self) -> Result<i8, MessageError> {
        let result:i8 = *self.buffer.get(self.pos).ok_or(MessageError::MalformedMessage)? as i8;
        self.pos += 1;
        Ok(result)
    }

    fn c_string(&mut self) -> Result<CString, MessageError> {
        let result = CStr::from_bytes_until_nul(&self.buffer[self.pos..]).map_err(|_|MessageError::MalformedMessage)?.to_owned();
        self.pos += result.count_bytes() + 1;
        Ok(result)
    }

    fn bytes(&mut self, len: usize) -> Result<Vec<u8>, MessageError> {
        let result = self.buffer.get(self.pos..self.pos+len).ok_or(MessageError::MalformedMessage)?.to_owned();
        self.pos += len;
        Ok(result)
    }

    fn peek(&self) -> Result<u8, MessageError> {
        (self.buffer.get(self.pos)).ok_or(MessageError::MalformedMessage).map(|b| *b)
    }
    
    fn rest(&self) -> &'a [u8] { 
        &self.buffer[self.pos..] 
    }
}
