use cyberex::void::HyVoidConst;

use crate::sys::*;
use std::{ffi::c_void, ops::Deref};

pub trait ICVecImpl<T> {
    fn fn_free(ctx: *const c_void);
    fn fn_data(ctx: *const c_void) -> *const T;
    fn fn_size(ctx: *const c_void) -> usize;
}
impl ICVecImpl<u8> for u8 {
    fn fn_free(ctx: *const c_void) {
        unsafe { codec_vec_uint8_free(ctx) }
    }
    fn fn_data(ctx: *const c_void) -> *const u8 {
        unsafe { codec_vec_uint8_data(ctx) }
    }
    fn fn_size(ctx: *const c_void) -> usize {
        unsafe { codec_vec_uint8_size(ctx) }
    }
}

pub struct CVecView<T>
where
    T: ICVecImpl<T>,
{
    ctx: HyVoidConst<()>,
    _mark: std::marker::PhantomData<T>,
}

impl<T> CVecView<T>
where
    T: ICVecImpl<T>,
{
    pub fn new(ptr: *const c_void) -> Self {
        let ctx = HyVoidConst::from_ptr(ptr);

        Self {
            ctx,
            _mark: std::marker::PhantomData,
        }
    }
    fn len(&self) -> usize {
        let ctx = self.ctx.as_ptr();
        T::fn_size(ctx)
    }
    fn ptr(&self) -> *const T {
        let ctx = self.ctx.as_ptr();
        T::fn_data(ctx).cast()
    }
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    pub fn as_slice(&self) -> &[T] {
        if self.is_empty() {
            return &[];
        }
        unsafe { std::slice::from_raw_parts(self.ptr(), self.len()) }
    }
}

impl<T> From<*const c_void> for CVecView<T>
where
    T: ICVecImpl<T>,
{
    fn from(ptr: *const c_void) -> Self {
        CVecView::new(ptr)
    }
}
impl<T> Deref for CVecView<T>
where
    T: ICVecImpl<T>,
{
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        self.as_slice()
    }
}

impl<T> Drop for CVecView<T>
where
    T: ICVecImpl<T>,
{
    fn drop(&mut self) {
        let ptr = self.ctx.as_ptr();
        T::fn_free(ptr);
    }
}