use std::any::Any;
use std::borrow::Cow;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::chunked_array::cast::CastOptions;
#[cfg(feature = "object")]
use crate::chunked_array::object::PolarsObjectSafe;
use crate::prelude::*;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum IsSorted {
    Ascending,
    Descending,
    Not,
}
impl IsSorted {
    pub fn reverse(self) -> Self {
        use IsSorted::*;
        match self {
            Ascending => Descending,
            Descending => Ascending,
            Not => Not,
        }
    }
}
macro_rules! invalid_operation_panic {
    ($op:ident, $s:expr) => {
        panic!(
            "`{}` operation not supported for dtype `{}`",
            stringify!($op),
            $s._dtype()
        )
    };
}
pub(crate) mod private {
    use ahash::RandomState;
    use super::*;
    use crate::chunked_array::metadata::MetadataFlags;
    use crate::chunked_array::ops::compare_inner::{TotalEqInner, TotalOrdInner};
    pub trait PrivateSeriesNumeric {
        fn bit_repr_is_large(&self) -> bool {
            false
        }
        fn bit_repr_large(&self) -> UInt64Chunked {
            unimplemented!()
        }
        fn bit_repr_small(&self) -> UInt32Chunked {
            unimplemented!()
        }
    }
    pub trait PrivateSeries {
        #[cfg(feature = "object")]
        fn get_list_builder(
            &self,
            _name: &str,
            _values_capacity: usize,
            _list_capacity: usize,
        ) -> Box<dyn ListBuilderTrait> {
            invalid_operation_panic!(get_list_builder, self)
        }
        fn _field(&self) -> Cow<Field>;
        fn _dtype(&self) -> &DataType;
        fn compute_len(&mut self);
        fn _get_flags(&self) -> MetadataFlags;
        fn _set_flags(&mut self, flags: MetadataFlags);
        fn explode_by_offsets(&self, _offsets: &[i64]) -> Series {
            invalid_operation_panic!(explode_by_offsets, self)
        }
        unsafe fn equal_element(
            &self,
            _idx_self: usize,
            _idx_other: usize,
            _other: &Series,
        ) -> bool {
            invalid_operation_panic!(equal_element, self)
        }
        #[allow(clippy::wrong_self_convention)]
        fn into_total_eq_inner<'a>(&'a self) -> Box<dyn TotalEqInner + 'a> {
            invalid_operation_panic!(into_total_eq_inner, self)
        }
        #[allow(clippy::wrong_self_convention)]
        fn into_total_ord_inner<'a>(&'a self) -> Box<dyn TotalOrdInner + 'a> {
            invalid_operation_panic!(into_total_ord_inner, self)
        }
        fn vec_hash(&self, _build_hasher: RandomState, _buf: &mut Vec<u64>) -> PolarsResult<()> {
            polars_bail!(opq = vec_hash, self._dtype());
        }
        fn vec_hash_combine(
            &self,
            _build_hasher: RandomState,
            _hashes: &mut [u64],
        ) -> PolarsResult<()> {
            polars_bail!(opq = vec_hash_combine, self._dtype());
        }
        #[cfg(feature = "algorithm_group_by")]
        unsafe fn agg_min(&self, groups: &GroupsProxy) -> Series {
            Series::full_null(self._field().name(), groups.len(), self._dtype())
        }
        #[cfg(feature = "algorithm_group_by")]
        unsafe fn agg_max(&self, groups: &GroupsProxy) -> Series {
            Series::full_null(self._field().name(), groups.len(), self._dtype())
        }
        #[cfg(feature = "algorithm_group_by")]
        unsafe fn agg_sum(&self, groups: &GroupsProxy) -> Series {
            Series::full_null(self._field().name(), groups.len(), self._dtype())
        }
        #[cfg(feature = "algorithm_group_by")]
        unsafe fn agg_std(&self, groups: &GroupsProxy, _ddof: u8) -> Series {
            Series::full_null(self._field().name(), groups.len(), self._dtype())
        }
        #[cfg(feature = "algorithm_group_by")]
        unsafe fn agg_var(&self, groups: &GroupsProxy, _ddof: u8) -> Series {
            Series::full_null(self._field().name(), groups.len(), self._dtype())
        }
        #[cfg(feature = "algorithm_group_by")]
        unsafe fn agg_list(&self, groups: &GroupsProxy) -> Series {
            Series::full_null(self._field().name(), groups.len(), self._dtype())
        }
        fn subtract(&self, _rhs: &Series) -> PolarsResult<Series> {
            polars_bail!(opq = subtract, self._dtype());
        }
        fn add_to(&self, _rhs: &Series) -> PolarsResult<Series> {
            polars_bail!(opq = add, self._dtype());
        }
        fn multiply(&self, _rhs: &Series) -> PolarsResult<Series> {
            polars_bail!(opq = multiply, self._dtype());
        }
        fn divide(&self, _rhs: &Series) -> PolarsResult<Series> {
            polars_bail!(opq = divide, self._dtype());
        }
        fn remainder(&self, _rhs: &Series) -> PolarsResult<Series> {
            polars_bail!(opq = remainder, self._dtype());
        }
        #[cfg(feature = "algorithm_group_by")]
        fn group_tuples(&self, _multithreaded: bool, _sorted: bool) -> PolarsResult<GroupsProxy> {
            polars_bail!(opq = group_tuples, self._dtype());
        }
        #[cfg(feature = "zip_with")]
        fn zip_with_same_type(
            &self,
            _mask: &BooleanChunked,
            _other: &Series,
        ) -> PolarsResult<Series> {
            polars_bail!(opq = zip_with_same_type, self._dtype());
        }
        #[allow(unused_variables)]
        fn arg_sort_multiple(
            &self,
            by: &[Series],
            _options: &SortMultipleOptions,
        ) -> PolarsResult<IdxCa> {
            polars_bail!(opq = arg_sort_multiple, self._dtype());
        }
    }
}
pub trait SeriesTrait:
    Send + Sync + private::PrivateSeries + private::PrivateSeriesNumeric
{
    fn rename(&mut self, name: &str);
    fn bitand(&self, _other: &Series) -> PolarsResult<Series> {
        polars_bail!(opq = bitand, self._dtype());
    }
    fn bitor(&self, _other: &Series) -> PolarsResult<Series> {
        polars_bail!(opq = bitor, self._dtype());
    }
    fn bitxor(&self, _other: &Series) -> PolarsResult<Series> {
        polars_bail!(opq = bitxor, self._dtype());
    }
    fn chunk_lengths(&self) -> ChunkLenIter;
    fn name(&self) -> &str;
    fn field(&self) -> Cow<Field> {
        self._field()
    }
    fn dtype(&self) -> &DataType {
        self._dtype()
    }
    fn chunks(&self) -> &Vec<ArrayRef>;
    unsafe fn chunks_mut(&mut self) -> &mut Vec<ArrayRef>;
    fn n_chunks(&self) -> usize {
        self.chunks().len()
    }
    fn shrink_to_fit(&mut self) {
        invalid_operation_panic!(shrink_to_fit, self);
    }
    fn limit(&self, num_elements: usize) -> Series {
        self.slice(0, num_elements)
    }
    fn slice(&self, _offset: i64, _length: usize) -> Series;
    #[doc(hidden)]
    fn append(&mut self, _other: &Series) -> PolarsResult<()>;
    #[doc(hidden)]
    fn extend(&mut self, _other: &Series) -> PolarsResult<()>;
    fn filter(&self, _filter: &BooleanChunked) -> PolarsResult<Series>;
    fn take(&self, _indices: &IdxCa) -> PolarsResult<Series>;
    unsafe fn take_unchecked(&self, _idx: &IdxCa) -> Series;
    fn take_slice(&self, _indices: &[IdxSize]) -> PolarsResult<Series>;
    unsafe fn take_slice_unchecked(&self, _idx: &[IdxSize]) -> Series;
    fn len(&self) -> usize;
    fn is_empty(&self) -> bool {
        self.len() == 0
    }
    fn rechunk(&self) -> Series;
    fn drop_nulls(&self) -> Series {
        if self.null_count() == 0 {
            Series(self.clone_inner())
        } else {
            self.filter(&self.is_not_null()).unwrap()
        }
    }
    fn mean(&self) -> Option<f64> {
        None
    }
    fn std(&self, _ddof: u8) -> Option<f64> {
        None
    }
    fn var(&self, _ddof: u8) -> Option<f64> {
        None
    }
    fn median(&self) -> Option<f64> {
        None
    }
    fn new_from_index(&self, _index: usize, _length: usize) -> Series;
    fn cast(&self, _data_type: &DataType, options: CastOptions) -> PolarsResult<Series>;
    fn get(&self, _index: usize) -> PolarsResult<AnyValue>;
    unsafe fn get_unchecked(&self, _index: usize) -> AnyValue {
        invalid_operation_panic!(get_unchecked, self)
    }
    fn sort_with(&self, _options: SortOptions) -> PolarsResult<Series> {
        polars_bail!(opq = sort_with, self._dtype());
    }
    #[allow(unused)]
    fn arg_sort(&self, options: SortOptions) -> IdxCa {
        invalid_operation_panic!(arg_sort, self)
    }
    fn null_count(&self) -> usize;
    fn has_validity(&self) -> bool;
    fn unique(&self) -> PolarsResult<Series> {
        polars_bail!(opq = unique, self._dtype());
    }
    fn n_unique(&self) -> PolarsResult<usize> {
        polars_bail!(opq = n_unique, self._dtype());
    }
    fn arg_unique(&self) -> PolarsResult<IdxCa> {
        polars_bail!(opq = arg_unique, self._dtype());
    }
    fn is_null(&self) -> BooleanChunked;
    fn is_not_null(&self) -> BooleanChunked;
    fn reverse(&self) -> Series;
    fn as_single_ptr(&mut self) -> PolarsResult<usize> {
        polars_bail!(opq = as_single_ptr, self._dtype());
    }
    fn shift(&self, _periods: i64) -> Series;
    fn sum_reduce(&self) -> PolarsResult<Scalar> {
        polars_bail!(opq = sum, self._dtype());
    }
    fn max_reduce(&self) -> PolarsResult<Scalar> {
        polars_bail!(opq = max, self._dtype());
    }
    fn min_reduce(&self) -> PolarsResult<Scalar> {
        polars_bail!(opq = min, self._dtype());
    }
    fn median_reduce(&self) -> PolarsResult<Scalar> {
        polars_bail!(opq = median, self._dtype());
    }
    fn var_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
        polars_bail!(opq = var, self._dtype());
    }
    fn std_reduce(&self, _ddof: u8) -> PolarsResult<Scalar> {
        polars_bail!(opq = std, self._dtype());
    }
    fn quantile_reduce(
        &self,
        _quantile: f64,
        _interpol: QuantileInterpolOptions,
    ) -> PolarsResult<Scalar> {
        polars_bail!(opq = quantile, self._dtype());
    }
    fn clone_inner(&self) -> Arc<dyn SeriesTrait>;
    #[cfg(feature = "object")]
    fn get_object(&self, _index: usize) -> Option<&dyn PolarsObjectSafe> {
        invalid_operation_panic!(get_object, self)
    }
    #[cfg(feature = "object")]
    unsafe fn get_object_chunked_unchecked(
        &self,
        _chunk: usize,
        _index: usize,
    ) -> Option<&dyn PolarsObjectSafe> {
        invalid_operation_panic!(get_object_chunked_unchecked, self)
    }
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any {
        invalid_operation_panic!(as_any_mut, self)
    }
    #[cfg(feature = "checked_arithmetic")]
    fn checked_div(&self, _rhs: &Series) -> PolarsResult<Series> {
        polars_bail!(opq = checked_div, self._dtype());
    }
    #[cfg(feature = "rolling_window")]
    fn rolling_map(
        &self,
        _f: &dyn Fn(&Series) -> Series,
        _options: RollingOptionsFixedWindow,
    ) -> PolarsResult<Series> {
        polars_bail!(opq = rolling_map, self._dtype());
    }
}
impl<'a> (dyn SeriesTrait + 'a) {
    pub fn unpack<N>(&self) -> PolarsResult<&ChunkedArray<N>>
    where
        N: 'static + PolarsDataType,
    {
        polars_ensure!(&N::get_dtype() == self.dtype(), unpack);
        Ok(self.as_ref())
    }
}