use std::{collections::HashSet, ffi::c_void};

use odbc_sys::{CDataType, Date, Numeric, Time, Timestamp};

use crate::{
    Bit, Error,
    buffers::columnar::{Resize, Slice},
    columnar_bulk_inserter::BoundInputSlice,
    error::TooLargeBufferSize,
    handles::{CData, CDataMut, StatementRef},
};

use super::{
    BinColumn, BinColumnSlice, BufferDesc, CharColumn, ColumnarBuffer, Indicator, Item,
    NullableSlice, NullableSliceMut, TextColumn, TextColumnSlice, WCharColumn,
    bin_column::BinColumnSliceMut,
    column_with_indicator::{
        OptBitColumn, OptDateColumn, OptF32Column, OptF64Column, OptI8Column, OptI16Column,
        OptI32Column, OptI64Column, OptTimeColumn, OptTimestampColumn, OptU8Column,
    },
    columnar::ColumnBuffer,
    text_column::TextColumnSliceMut,
};

/// Buffer holding a single column of either a result set or parameter
#[derive(Debug)]
pub enum AnyBuffer {
    /// A buffer for holding both nullable and required binary data.
    Binary(BinColumn),
    /// A buffer for holding both nullable and required text data. Uses the system encoding for
    /// character data.
    Text(CharColumn),
    /// A buffer for holding both nullable and required text data. Uses UTF-16 encoding
    WText(WCharColumn),
    Date(Vec<Date>),
    Time(Vec<Time>),
    Timestamp(Vec<Timestamp>),
    F64(Vec<f64>),
    F32(Vec<f32>),
    I8(Vec<i8>),
    I16(Vec<i16>),
    I32(Vec<i32>),
    I64(Vec<i64>),
    U8(Vec<u8>),
    Bit(Vec<Bit>),
    Numeric(Vec<Numeric>),
    NullableDate(OptDateColumn),
    NullableTime(OptTimeColumn),
    NullableTimestamp(OptTimestampColumn),
    NullableF64(OptF64Column),
    NullableF32(OptF32Column),
    NullableI8(OptI8Column),
    NullableI16(OptI16Column),
    NullableI32(OptI32Column),
    NullableI64(OptI64Column),
    NullableU8(OptU8Column),
    NullableBit(OptBitColumn),
}

impl AnyBuffer {
    /// Map buffer description to actual buffer.
    pub fn try_from_desc(max_rows: usize, desc: BufferDesc) -> Result<Self, TooLargeBufferSize> {
        let fallible_allocations = true;
        Self::impl_from_desc(max_rows, desc, fallible_allocations)
    }

    /// Map buffer description to actual buffer.
    pub fn from_desc(max_rows: usize, desc: BufferDesc) -> Self {
        let fallible_allocations = false;
        Self::impl_from_desc(max_rows, desc, fallible_allocations).unwrap()
    }

    /// Map buffer description to actual buffer.
    fn impl_from_desc(
        max_rows: usize,
        desc: BufferDesc,
        fallible_allocations: bool,
    ) -> Result<Self, TooLargeBufferSize> {
        let buffer = match desc {
            BufferDesc::Binary { max_bytes } => {
                if fallible_allocations {
                    AnyBuffer::Binary(BinColumn::try_new(max_rows, max_bytes)?)
                } else {
                    AnyBuffer::Binary(BinColumn::new(max_rows, max_bytes))
                }
            }
            BufferDesc::Text { max_str_len } => {
                if fallible_allocations {
                    AnyBuffer::Text(TextColumn::try_new(max_rows, max_str_len)?)
                } else {
                    AnyBuffer::Text(TextColumn::new(max_rows, max_str_len))
                }
            }
            BufferDesc::WText { max_str_len } => {
                if fallible_allocations {
                    AnyBuffer::WText(TextColumn::try_new(max_rows, max_str_len)?)
                } else {
                    AnyBuffer::WText(TextColumn::new(max_rows, max_str_len))
                }
            }
            BufferDesc::Date { nullable: false } => {
                AnyBuffer::Date(vec![Date::default(); max_rows])
            }
            BufferDesc::Time { nullable: false } => {
                AnyBuffer::Time(vec![Time::default(); max_rows])
            }
            BufferDesc::Timestamp { nullable: false } => {
                AnyBuffer::Timestamp(vec![Timestamp::default(); max_rows])
            }
            BufferDesc::F64 { nullable: false } => AnyBuffer::F64(vec![f64::default(); max_rows]),
            BufferDesc::F32 { nullable: false } => AnyBuffer::F32(vec![f32::default(); max_rows]),
            BufferDesc::I8 { nullable: false } => AnyBuffer::I8(vec![i8::default(); max_rows]),
            BufferDesc::I16 { nullable: false } => AnyBuffer::I16(vec![i16::default(); max_rows]),
            BufferDesc::I32 { nullable: false } => AnyBuffer::I32(vec![i32::default(); max_rows]),
            BufferDesc::I64 { nullable: false } => AnyBuffer::I64(vec![i64::default(); max_rows]),
            BufferDesc::U8 { nullable: false } => AnyBuffer::U8(vec![u8::default(); max_rows]),
            BufferDesc::Bit { nullable: false } => AnyBuffer::Bit(vec![Bit::default(); max_rows]),
            BufferDesc::Numeric => AnyBuffer::Numeric(vec![Numeric::default(); max_rows]),
            BufferDesc::Date { nullable: true } => {
                AnyBuffer::NullableDate(OptDateColumn::new(max_rows))
            }
            BufferDesc::Time { nullable: true } => {
                AnyBuffer::NullableTime(OptTimeColumn::new(max_rows))
            }
            BufferDesc::Timestamp { nullable: true } => {
                AnyBuffer::NullableTimestamp(OptTimestampColumn::new(max_rows))
            }
            BufferDesc::F64 { nullable: true } => {
                AnyBuffer::NullableF64(OptF64Column::new(max_rows))
            }
            BufferDesc::F32 { nullable: true } => {
                AnyBuffer::NullableF32(OptF32Column::new(max_rows))
            }
            BufferDesc::I8 { nullable: true } => AnyBuffer::NullableI8(OptI8Column::new(max_rows)),
            BufferDesc::I16 { nullable: true } => {
                AnyBuffer::NullableI16(OptI16Column::new(max_rows))
            }
            BufferDesc::I32 { nullable: true } => {
                AnyBuffer::NullableI32(OptI32Column::new(max_rows))
            }
            BufferDesc::I64 { nullable: true } => {
                AnyBuffer::NullableI64(OptI64Column::new(max_rows))
            }
            BufferDesc::U8 { nullable: true } => AnyBuffer::NullableU8(OptU8Column::new(max_rows)),
            BufferDesc::Bit { nullable: true } => {
                AnyBuffer::NullableBit(OptBitColumn::new(max_rows))
            }
        };
        Ok(buffer)
    }

    fn inner(&self) -> &dyn CData {
        match self {
            AnyBuffer::Binary(col) => col,
            AnyBuffer::Text(col) => col,
            AnyBuffer::WText(col) => col,
            AnyBuffer::F64(col) => col,
            AnyBuffer::F32(col) => col,
            AnyBuffer::Date(col) => col,
            AnyBuffer::Time(col) => col,
            AnyBuffer::Timestamp(col) => col,
            AnyBuffer::I8(col) => col,
            AnyBuffer::I16(col) => col,
            AnyBuffer::I32(col) => col,
            AnyBuffer::I64(col) => col,
            AnyBuffer::Bit(col) => col,
            AnyBuffer::U8(col) => col,
            AnyBuffer::Numeric(col) => col,
            AnyBuffer::NullableF64(col) => col,
            AnyBuffer::NullableF32(col) => col,
            AnyBuffer::NullableDate(col) => col,
            AnyBuffer::NullableTime(col) => col,
            AnyBuffer::NullableTimestamp(col) => col,
            AnyBuffer::NullableI8(col) => col,
            AnyBuffer::NullableI16(col) => col,
            AnyBuffer::NullableI32(col) => col,
            AnyBuffer::NullableI64(col) => col,
            AnyBuffer::NullableBit(col) => col,
            AnyBuffer::NullableU8(col) => col,
        }
    }

    fn inner_mut(&mut self) -> &mut dyn AnyBufferVariantMut {
        match self {
            AnyBuffer::Binary(col) => col,
            AnyBuffer::Text(col) => col,
            AnyBuffer::WText(col) => col,
            AnyBuffer::F64(col) => col,
            AnyBuffer::F32(col) => col,
            AnyBuffer::Date(col) => col,
            AnyBuffer::Time(col) => col,
            AnyBuffer::Timestamp(col) => col,
            AnyBuffer::I8(col) => col,
            AnyBuffer::I16(col) => col,
            AnyBuffer::I32(col) => col,
            AnyBuffer::I64(col) => col,
            AnyBuffer::Bit(col) => col,
            AnyBuffer::U8(col) => col,
            AnyBuffer::Numeric(col) => col,
            AnyBuffer::NullableF64(col) => col,
            AnyBuffer::NullableF32(col) => col,
            AnyBuffer::NullableDate(col) => col,
            AnyBuffer::NullableTime(col) => col,
            AnyBuffer::NullableTimestamp(col) => col,
            AnyBuffer::NullableI8(col) => col,
            AnyBuffer::NullableI16(col) => col,
            AnyBuffer::NullableI32(col) => col,
            AnyBuffer::NullableI64(col) => col,
            AnyBuffer::NullableBit(col) => col,
            AnyBuffer::NullableU8(col) => col,
        }
    }
}

/// A trait implemented by all variants of [`AnyBuffer`]. This allows us to reduce the number of
/// match statements for methods mutating [`AnyBuffer`], as we only need to implement
/// [`AnyBuffer::inner_mut`].
trait AnyBufferVariantMut: CDataMut + Resize {}

impl<T> AnyBufferVariantMut for T where T: CDataMut + Resize {}

unsafe impl CData for AnyBuffer {
    fn cdata_type(&self) -> CDataType {
        self.inner().cdata_type()
    }

    fn indicator_ptr(&self) -> *const isize {
        self.inner().indicator_ptr()
    }

    fn value_ptr(&self) -> *const c_void {
        self.inner().value_ptr()
    }

    fn buffer_length(&self) -> isize {
        self.inner().buffer_length()
    }
}

unsafe impl CDataMut for AnyBuffer {
    fn mut_indicator_ptr(&mut self) -> *mut isize {
        self.inner_mut().mut_indicator_ptr()
    }

    fn mut_value_ptr(&mut self) -> *mut c_void {
        self.inner_mut().mut_value_ptr()
    }
}

/// Flexible columnar buffer implementation. Bind this to a cursor to fetch values in bulk, or pass
/// this as a parameter to a statement, to submit many parameters at once.
pub type ColumnarAnyBuffer = ColumnarBuffer<AnyBuffer>;

impl ColumnarAnyBuffer {
    /// Allocates a [`ColumnarBuffer`] fitting the buffer descriptions.
    pub fn from_descs(capacity: usize, descs: impl IntoIterator<Item = BufferDesc>) -> Self {
        let mut column_index = 0;
        let columns = descs
            .into_iter()
            .map(move |desc| {
                let buffer = AnyBuffer::from_desc(capacity, desc);
                column_index += 1;
                (column_index, buffer)
            })
            .collect();
        unsafe { ColumnarBuffer::new_unchecked(capacity, columns) }
    }

    /// Allocates a [`ColumnarBuffer`] fitting the buffer descriptions. If not enough memory is
    /// available to allocate the buffers this function fails with
    /// [`Error::TooLargeColumnBufferSize`]. This function is slower than [`Self::from_descs`]
    /// which would just panic if not enough memory is available for allocation.
    pub fn try_from_descs(
        capacity: usize,
        descs: impl IntoIterator<Item = BufferDesc>,
    ) -> Result<Self, Error> {
        let mut column_index = 0;
        let columns = descs
            .into_iter()
            .map(move |desc| {
                let buffer = AnyBuffer::try_from_desc(capacity, desc)
                    .map_err(|source| source.add_context(column_index))?;
                column_index += 1;
                Ok::<_, Error>((column_index, buffer))
            })
            .collect::<Result<_, _>>()?;
        Ok(unsafe { ColumnarBuffer::new_unchecked(capacity, columns) })
    }

    /// Allows you to pass the buffer descriptions together with a one based column index referring
    /// the column, the buffer is supposed to bind to. This allows you also to ignore columns in a
    /// result set, by not binding them at all. There is no restriction on the order of column
    /// indices passed, but the function will panic, if the indices are not unique.
    pub fn from_descs_and_indices(
        max_rows: usize,
        description: impl Iterator<Item = (u16, BufferDesc)>,
    ) -> ColumnarBuffer<AnyBuffer> {
        let columns: Vec<_> = description
            .map(|(col_index, buffer_desc)| {
                (col_index, AnyBuffer::from_desc(max_rows, buffer_desc))
            })
            .collect();

        // Assert uniqueness of indices
        let mut indices = HashSet::new();
        if columns
            .iter()
            .any(move |&(col_index, _)| !indices.insert(col_index))
        {
            panic!("Column indices must be unique.")
        }

        ColumnarBuffer::new(columns)
    }
}

/// A borrowed view on the valid rows in a column of a [`crate::buffers::ColumnarBuffer`].
///
/// For columns of fixed size types, which are guaranteed to not contain null, a direct access to
/// the slice is offered. Buffers over nullable columns can be accessed via an iterator over
/// options.
#[derive(Debug, Clone, Copy)]
pub enum AnySlice<'a> {
    /// Nullable character data in the system encoding.
    Text(TextColumnSlice<'a, u8>),
    /// Nullable character data encoded in UTF-16.
    WText(TextColumnSlice<'a, u16>),
    Binary(BinColumnSlice<'a>),
    Date(&'a [Date]),
    Time(&'a [Time]),
    Timestamp(&'a [Timestamp]),
    F64(&'a [f64]),
    F32(&'a [f32]),
    I8(&'a [i8]),
    I16(&'a [i16]),
    I32(&'a [i32]),
    I64(&'a [i64]),
    U8(&'a [u8]),
    Bit(&'a [Bit]),
    Numeric(&'a [Numeric]),
    NullableDate(NullableSlice<'a, Date>),
    NullableTime(NullableSlice<'a, Time>),
    NullableTimestamp(NullableSlice<'a, Timestamp>),
    NullableF64(NullableSlice<'a, f64>),
    NullableF32(NullableSlice<'a, f32>),
    NullableI8(NullableSlice<'a, i8>),
    NullableI16(NullableSlice<'a, i16>),
    NullableI32(NullableSlice<'a, i32>),
    NullableI64(NullableSlice<'a, i64>),
    NullableU8(NullableSlice<'a, u8>),
    NullableBit(NullableSlice<'a, Bit>),
    NullableNumeric(NullableSlice<'a, Numeric>),
}

impl<'a> AnySlice<'a> {
    /// This method is useful if you expect the variant to be [`AnySlice::Text`]. It allows you to
    /// unwrap the inner column view without explictly matching it.
    pub fn as_text_view(self) -> Option<TextColumnSlice<'a, u8>> {
        if let Self::Text(view) = self {
            Some(view)
        } else {
            None
        }
    }

    /// This method is useful if you expect the variant to be [`AnySlice::WText`]. It allows you to
    /// unwrap the inner column view without explictly matching it.
    pub fn as_w_text_view(self) -> Option<TextColumnSlice<'a, u16>> {
        if let Self::WText(view) = self {
            Some(view)
        } else {
            None
        }
    }

    /// This method is useful if you expect the variant to be [`AnySlice::Binary`]. It allows you to
    /// unwrap the inner column view without explictly matching it.
    pub fn as_bin_view(self) -> Option<BinColumnSlice<'a>> {
        if let Self::Binary(view) = self {
            Some(view)
        } else {
            None
        }
    }

    /// Extract the array type from an [`AnySlice`].
    pub fn as_slice<I: Item>(self) -> Option<&'a [I]> {
        I::as_slice(self)
    }

    /// Extract the typed nullable buffer from an [`AnySlice`].
    pub fn as_nullable_slice<I: Item>(self) -> Option<NullableSlice<'a, I>> {
        I::as_nullable_slice(self)
    }
}

unsafe impl<'a> BoundInputSlice<'a> for AnyBuffer {
    type SliceMut = AnySliceMut<'a>;

    unsafe fn as_view_mut(
        &'a mut self,
        parameter_index: u16,
        stmt: StatementRef<'a>,
    ) -> Self::SliceMut {
        let num_rows = self.capacity();
        unsafe {
            match self {
                AnyBuffer::Binary(column) => {
                    AnySliceMut::Binary(column.as_view_mut(parameter_index, stmt))
                }
                AnyBuffer::Text(column) => {
                    AnySliceMut::Text(column.as_view_mut(parameter_index, stmt))
                }
                AnyBuffer::WText(column) => {
                    AnySliceMut::WText(column.as_view_mut(parameter_index, stmt))
                }
                AnyBuffer::Date(column) => AnySliceMut::Date(column),
                AnyBuffer::Time(column) => AnySliceMut::Time(column),
                AnyBuffer::Timestamp(column) => AnySliceMut::Timestamp(column),
                AnyBuffer::F64(column) => AnySliceMut::F64(column),
                AnyBuffer::F32(column) => AnySliceMut::F32(column),
                AnyBuffer::I8(column) => AnySliceMut::I8(column),
                AnyBuffer::I16(column) => AnySliceMut::I16(column),
                AnyBuffer::I32(column) => AnySliceMut::I32(column),
                AnyBuffer::I64(column) => AnySliceMut::I64(column),
                AnyBuffer::U8(column) => AnySliceMut::U8(column),
                AnyBuffer::Bit(column) => AnySliceMut::Bit(column),
                AnyBuffer::Numeric(column) => AnySliceMut::Numeric(column),
                AnyBuffer::NullableDate(column) => {
                    AnySliceMut::NullableDate(column.writer_n(num_rows))
                }
                AnyBuffer::NullableTime(column) => {
                    AnySliceMut::NullableTime(column.writer_n(num_rows))
                }
                AnyBuffer::NullableTimestamp(column) => {
                    AnySliceMut::NullableTimestamp(column.writer_n(num_rows))
                }
                AnyBuffer::NullableF64(column) => {
                    AnySliceMut::NullableF64(column.writer_n(num_rows))
                }
                AnyBuffer::NullableF32(column) => {
                    AnySliceMut::NullableF32(column.writer_n(num_rows))
                }
                AnyBuffer::NullableI8(column) => AnySliceMut::NullableI8(column.writer_n(num_rows)),
                AnyBuffer::NullableI16(column) => {
                    AnySliceMut::NullableI16(column.writer_n(num_rows))
                }
                AnyBuffer::NullableI32(column) => {
                    AnySliceMut::NullableI32(column.writer_n(num_rows))
                }
                AnyBuffer::NullableI64(column) => {
                    AnySliceMut::NullableI64(column.writer_n(num_rows))
                }
                AnyBuffer::NullableU8(column) => AnySliceMut::NullableU8(column.writer_n(num_rows)),
                AnyBuffer::NullableBit(column) => {
                    AnySliceMut::NullableBit(column.writer_n(num_rows))
                }
            }
        }
    }
}

/// A mutable slice of an input buffer, with runtime type information. Edit values in this slice in
/// order to send parameters in bulk to a database.
pub enum AnySliceMut<'a> {
    Text(TextColumnSliceMut<'a, u8>),
    /// Nullable character data encoded in UTF-16.
    WText(TextColumnSliceMut<'a, u16>),
    Binary(BinColumnSliceMut<'a>),
    Date(&'a mut [Date]),
    Time(&'a mut [Time]),
    Timestamp(&'a mut [Timestamp]),
    F64(&'a mut [f64]),
    F32(&'a mut [f32]),
    I8(&'a mut [i8]),
    I16(&'a mut [i16]),
    I32(&'a mut [i32]),
    I64(&'a mut [i64]),
    U8(&'a mut [u8]),
    Bit(&'a mut [Bit]),
    Numeric(&'a mut [Numeric]),
    NullableDate(NullableSliceMut<'a, Date>),
    NullableTime(NullableSliceMut<'a, Time>),
    NullableTimestamp(NullableSliceMut<'a, Timestamp>),
    NullableF64(NullableSliceMut<'a, f64>),
    NullableF32(NullableSliceMut<'a, f32>),
    NullableI8(NullableSliceMut<'a, i8>),
    NullableI16(NullableSliceMut<'a, i16>),
    NullableI32(NullableSliceMut<'a, i32>),
    NullableI64(NullableSliceMut<'a, i64>),
    NullableU8(NullableSliceMut<'a, u8>),
    NullableBit(NullableSliceMut<'a, Bit>),
}

impl<'a> AnySliceMut<'a> {
    /// This method is useful if you expect the variant to be [`AnySliceMut::Binary`]. It allows you
    /// to unwrap the inner column view without explictly matching it.
    pub fn as_bin_view(self) -> Option<BinColumnSliceMut<'a>> {
        if let Self::Binary(view) = self {
            Some(view)
        } else {
            None
        }
    }

    /// This method is useful if you expect the variant to be [`AnySliceMut::Text`]. It allows you
    /// to unwrap the inner column view without explictly matching it.
    pub fn as_text_view(self) -> Option<TextColumnSliceMut<'a, u8>> {
        if let Self::Text(view) = self {
            Some(view)
        } else {
            None
        }
    }

    /// This method is useful if you expect the variant to be [`AnySliceMut::WText`]. It allows you
    /// to unwrap the inner column view without explictly matching it.
    pub fn as_w_text_view(self) -> Option<TextColumnSliceMut<'a, u16>> {
        if let Self::WText(view) = self {
            Some(view)
        } else {
            None
        }
    }

    /// Extract the array type from an [`AnySliceMut`].
    pub fn as_slice<I: Item>(self) -> Option<&'a mut [I]> {
        I::as_slice_mut(self)
    }

    /// Extract the typed nullable buffer from an [`AnySliceMut`].
    pub fn as_nullable_slice<I: Item>(self) -> Option<NullableSliceMut<'a, I>> {
        I::as_nullable_slice_mut(self)
    }
}

unsafe impl ColumnBuffer for AnyBuffer {
    fn capacity(&self) -> usize {
        match self {
            AnyBuffer::Binary(col) => col.capacity(),
            AnyBuffer::Text(col) => col.capacity(),
            AnyBuffer::WText(col) => col.capacity(),
            AnyBuffer::Date(col) => col.capacity(),
            AnyBuffer::Time(col) => col.capacity(),
            AnyBuffer::Timestamp(col) => col.capacity(),
            AnyBuffer::F64(col) => col.capacity(),
            AnyBuffer::F32(col) => col.capacity(),
            AnyBuffer::I8(col) => col.capacity(),
            AnyBuffer::I16(col) => col.capacity(),
            AnyBuffer::I32(col) => col.capacity(),
            AnyBuffer::I64(col) => col.capacity(),
            AnyBuffer::U8(col) => col.capacity(),
            AnyBuffer::Bit(col) => col.capacity(),
            AnyBuffer::Numeric(col) => col.capacity(),
            AnyBuffer::NullableDate(col) => col.capacity(),
            AnyBuffer::NullableTime(col) => col.capacity(),
            AnyBuffer::NullableTimestamp(col) => col.capacity(),
            AnyBuffer::NullableF64(col) => col.capacity(),
            AnyBuffer::NullableF32(col) => col.capacity(),
            AnyBuffer::NullableI8(col) => col.capacity(),
            AnyBuffer::NullableI16(col) => col.capacity(),
            AnyBuffer::NullableI32(col) => col.capacity(),
            AnyBuffer::NullableI64(col) => col.capacity(),
            AnyBuffer::NullableU8(col) => col.capacity(),
            AnyBuffer::NullableBit(col) => col.capacity(),
        }
    }

    fn has_truncated_values(&self, num_rows: usize) -> Option<Indicator> {
        match self {
            AnyBuffer::Binary(col) => col.has_truncated_values(num_rows),
            AnyBuffer::Text(col) => col.has_truncated_values(num_rows),
            AnyBuffer::WText(col) => col.has_truncated_values(num_rows),
            _ => None,
        }
    }
}

unsafe impl Slice for AnyBuffer {
    type Slice<'a> = AnySlice<'a>;

    fn slice(&self, valid_rows: usize) -> AnySlice<'_> {
        match self {
            AnyBuffer::Binary(col) => AnySlice::Binary(col.view(valid_rows)),
            AnyBuffer::Text(col) => AnySlice::Text(col.slice(valid_rows)),
            AnyBuffer::WText(col) => AnySlice::WText(col.slice(valid_rows)),
            AnyBuffer::Date(col) => AnySlice::Date(&col[0..valid_rows]),
            AnyBuffer::Time(col) => AnySlice::Time(&col[0..valid_rows]),
            AnyBuffer::Timestamp(col) => AnySlice::Timestamp(&col[0..valid_rows]),
            AnyBuffer::F64(col) => AnySlice::F64(&col[0..valid_rows]),
            AnyBuffer::F32(col) => AnySlice::F32(&col[0..valid_rows]),
            AnyBuffer::I8(col) => AnySlice::I8(&col[0..valid_rows]),
            AnyBuffer::I16(col) => AnySlice::I16(&col[0..valid_rows]),
            AnyBuffer::I32(col) => AnySlice::I32(&col[0..valid_rows]),
            AnyBuffer::I64(col) => AnySlice::I64(&col[0..valid_rows]),
            AnyBuffer::U8(col) => AnySlice::U8(&col[0..valid_rows]),
            AnyBuffer::Bit(col) => AnySlice::Bit(&col[0..valid_rows]),
            AnyBuffer::Numeric(col) => AnySlice::Numeric(&col[0..valid_rows]),
            AnyBuffer::NullableDate(col) => AnySlice::NullableDate(col.iter(valid_rows)),
            AnyBuffer::NullableTime(col) => AnySlice::NullableTime(col.iter(valid_rows)),
            AnyBuffer::NullableTimestamp(col) => AnySlice::NullableTimestamp(col.iter(valid_rows)),
            AnyBuffer::NullableF64(col) => AnySlice::NullableF64(col.iter(valid_rows)),
            AnyBuffer::NullableF32(col) => AnySlice::NullableF32(col.iter(valid_rows)),
            AnyBuffer::NullableI8(col) => AnySlice::NullableI8(col.iter(valid_rows)),
            AnyBuffer::NullableI16(col) => AnySlice::NullableI16(col.iter(valid_rows)),
            AnyBuffer::NullableI32(col) => AnySlice::NullableI32(col.iter(valid_rows)),
            AnyBuffer::NullableI64(col) => AnySlice::NullableI64(col.iter(valid_rows)),
            AnyBuffer::NullableU8(col) => AnySlice::NullableU8(col.iter(valid_rows)),
            AnyBuffer::NullableBit(col) => AnySlice::NullableBit(col.iter(valid_rows)),
        }
    }
}

impl Resize for AnyBuffer {
    fn resize(&mut self, new_capacity: usize) {
        self.inner_mut().resize(new_capacity);
    }
}

#[cfg(test)]
mod tests {
    use crate::buffers::{AnySlice, AnySliceMut, ColumnBuffer as _, Resize, columnar::Slice as _};

    use super::AnyBuffer;

    #[test]
    fn any_buffer_is_resize() {
        // Given an `AnyBuffer` with a capacity of 2 and values [1, 2]
        let mut buffer = AnyBuffer::I32(vec![1, 2]);

        // When we resize it to a capacity of 4
        buffer.resize(4);

        // Then the buffer should still have the same values in the first two positions and the
        // remaining positions should be filled with default values (0 for i32)
        assert_eq!(buffer.slice(4).as_slice(), Some([1i32, 2, 0, 0].as_slice()));
        // And the capacity should be 4
        assert_eq!(buffer.capacity(), 4);
    }

    #[test]
    fn slice_should_only_contain_part_of_the_buffer() {
        let buffer = AnyBuffer::I32(vec![1, 2, 3]);

        let view = buffer.slice(2);

        assert_eq!(Some([1, 2].as_slice()), view.as_slice::<i32>());
    }

    #[test]
    fn slice_should_be_none_if_types_mismatch() {
        let buffer = [1, 2, 3];
        let view = AnySlice::I32(&buffer);
        assert_eq!(None, view.as_slice::<i16>());
    }

    #[test]
    fn slice_mut_should_be_none_if_types_mismatch() {
        let mut buffer = [1, 2, 3];
        let view = AnySliceMut::I32(&mut buffer);
        assert_eq!(None, view.as_slice::<i16>());
    }

    #[test]
    fn nullable_slice_should_be_none_if_buffer_is_non_nullable() {
        let buffer = [1, 2, 3];
        let view = AnySlice::I32(&buffer);
        assert!(view.as_nullable_slice::<i32>().is_none());
    }

    #[test]
    fn nullable_slice_mut_should_be_none_if_buffer_is_non_nullable() {
        let mut buffer = [1, 2, 3];
        let view = AnySliceMut::I32(&mut buffer);
        assert!(view.as_nullable_slice::<i32>().is_none());
    }
}
