use core::convert::{TryFrom, TryInto};
use super::{ManagedBuffer, ManagedType};
use crate::{
    abi::TypeAbi,
    api::{Handle, ManagedTypeApi},
    hex_util::encode_bytes_as_hex,
};
use alloc::string::String;
use dharitri_codec::{
    DecodeError, EncodeError, NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput,
    TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast,
};
const DECODE_ERROR_BAD_LENGTH: &[u8] = b"bad array length";
#[repr(transparent)]
#[derive(Clone)]
pub struct ManagedByteArray<M, const N: usize>
where
    M: ManagedTypeApi,
{
    pub(crate) buffer: ManagedBuffer<M>,
}
impl<M, const N: usize> ManagedType<M> for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    fn from_raw_handle(handle: Handle) -> Self {
        ManagedByteArray {
            buffer: ManagedBuffer::from_raw_handle(handle),
        }
    }
    #[doc(hidden)]
    fn get_raw_handle(&self) -> Handle {
        self.buffer.get_raw_handle()
    }
    fn transmute_from_handle_ref(handle_ref: &Handle) -> &Self {
        unsafe { core::mem::transmute(handle_ref) }
    }
}
impl<M, const N: usize> Default for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    fn default() -> Self {
        Self::new_from_bytes(&[0u8; N])
    }
}
impl<M, const N: usize> From<&[u8; N]> for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    fn from(bytes: &[u8; N]) -> Self {
        Self::new_from_bytes(bytes)
    }
}
impl<M, const N: usize> ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    pub fn new_from_bytes(bytes: &[u8; N]) -> Self {
        ManagedByteArray {
            buffer: ManagedBuffer::new_from_bytes(&bytes[..]),
        }
    }
    #[inline]
    pub fn len(&self) -> usize {
        self.buffer.len()
    }
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
    #[inline]
    pub fn as_managed_buffer(&self) -> &ManagedBuffer<M> {
        &self.buffer
    }
    #[inline]
    pub fn to_byte_array(&self) -> [u8; N] {
        let mut result = [0u8; N];
        let _ = self.buffer.load_slice(0, &mut result[..]);
        result
    }
}
impl<M, const N: usize> PartialEq for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.buffer == other.buffer
    }
}
impl<M, const N: usize> Eq for ManagedByteArray<M, N> where M: ManagedTypeApi {}
impl<M, const N: usize> TryFrom<ManagedBuffer<M>> for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    type Error = DecodeError;
    fn try_from(value: ManagedBuffer<M>) -> Result<Self, Self::Error> {
        if value.len() != N {
            return Err(DecodeError::from(DECODE_ERROR_BAD_LENGTH));
        }
        Ok(ManagedByteArray { buffer: value })
    }
}
impl<M, const N: usize> TopEncode for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    fn top_encode<O: TopEncodeOutput>(&self, output: O) -> Result<(), EncodeError> {
        self.buffer.top_encode(output)
    }
}
impl<M, const N: usize> TopDecode for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
        let buffer = ManagedBuffer::top_decode(input)?;
        buffer.try_into()
    }
}
#[derive(Clone)]
pub(crate) struct ManagedBufferSizeContext(pub usize);
impl TryStaticCast for ManagedBufferSizeContext {}
impl<M, const N: usize> NestedEncode for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    #[inline]
    fn dep_encode<O: NestedEncodeOutput>(&self, dest: &mut O) -> Result<(), EncodeError> {
        dest.push_specialized(ManagedBufferSizeContext(N), &self.buffer, |else_output| {
            else_output.write(self.buffer.to_boxed_bytes().as_slice());
            Ok(())
        })
    }
}
impl<M, const N: usize> NestedDecode for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    fn dep_decode<I: NestedDecodeInput>(input: &mut I) -> Result<Self, DecodeError> {
        let buffer: ManagedBuffer<M> = input
            .read_specialized(ManagedBufferSizeContext(N), |_| {
                Err(DecodeError::UNSUPPORTED_OPERATION)
            })?;
        Ok(ManagedByteArray { buffer })
    }
}
impl<M, const N: usize> TypeAbi for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    fn type_name() -> String {
        <&[u8; N] as TypeAbi>::type_name()
    }
}
impl<M, const N: usize> core::fmt::Debug for ManagedByteArray<M, N>
where
    M: ManagedTypeApi,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("ManagedByteArray")
            .field("handle", &self.buffer.handle)
            .field("size", &N)
            .field("hex-value", &encode_bytes_as_hex(&self.to_byte_array()))
            .finish()
    }
}