use std::sync::Arc;
use arrow_array::{Array, ArrayRef, FixedSizeListArray, StructArray};
use arrow_schema::DataType;
use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
use geoarrow_schema::{CoordType, Dimension};
use crate::array::{InterleavedCoordBuffer, SeparatedCoordBuffer};
use crate::builder::{InterleavedCoordBufferBuilder, SeparatedCoordBufferBuilder};
use crate::scalar::Coord;
#[derive(Debug, Clone)]
pub enum CoordBuffer {
Interleaved(InterleavedCoordBuffer),
Separated(SeparatedCoordBuffer),
}
impl CoordBuffer {
pub(crate) fn slice(&self, offset: usize, length: usize) -> Self {
match self {
CoordBuffer::Interleaved(c) => CoordBuffer::Interleaved(c.slice(offset, length)),
CoordBuffer::Separated(c) => CoordBuffer::Separated(c.slice(offset, length)),
}
}
pub fn coord_type(&self) -> CoordType {
match self {
CoordBuffer::Interleaved(_) => CoordType::Interleaved,
CoordBuffer::Separated(_) => CoordType::Separated,
}
}
pub(crate) fn storage_type(&self) -> DataType {
match self {
CoordBuffer::Interleaved(c) => c.storage_type(),
CoordBuffer::Separated(c) => c.storage_type(),
}
}
pub fn len(&self) -> usize {
match self {
CoordBuffer::Interleaved(c) => c.len(),
CoordBuffer::Separated(c) => c.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn value(&self, index: usize) -> Coord<'_> {
match self {
CoordBuffer::Interleaved(c) => Coord::Interleaved(c.value(index)),
CoordBuffer::Separated(c) => Coord::Separated(c.value(index)),
}
}
pub unsafe fn value_unchecked(&self, index: usize) -> Coord<'_> {
match self {
CoordBuffer::Interleaved(c) => Coord::Interleaved(unsafe { c.value_unchecked(index) }),
CoordBuffer::Separated(c) => Coord::Separated(unsafe { c.value_unchecked(index) }),
}
}
pub(crate) fn into_array_ref(self) -> ArrayRef {
self.into()
}
pub fn dim(&self) -> Dimension {
match self {
CoordBuffer::Interleaved(c) => c.dim(),
CoordBuffer::Separated(c) => c.dim(),
}
}
pub fn into_coord_type(self, coord_type: CoordType) -> Self {
let dim = self.dim();
match (self, coord_type) {
(CoordBuffer::Interleaved(cb), CoordType::Interleaved) => CoordBuffer::Interleaved(cb),
(CoordBuffer::Interleaved(cb), CoordType::Separated) => {
let mut new_buffer = SeparatedCoordBufferBuilder::with_capacity(cb.len(), dim);
for i in 0..cb.len() {
let coord = cb.value(i);
new_buffer.push_coord(&coord);
}
CoordBuffer::Separated(new_buffer.finish())
}
(CoordBuffer::Separated(cb), CoordType::Separated) => CoordBuffer::Separated(cb),
(CoordBuffer::Separated(cb), CoordType::Interleaved) => {
let mut new_buffer = InterleavedCoordBufferBuilder::with_capacity(cb.len(), dim);
for i in 0..cb.len() {
let coord = cb.value(i);
new_buffer.push_coord(&coord);
}
CoordBuffer::Interleaved(new_buffer.finish())
}
}
}
pub(crate) fn from_arrow(value: &dyn Array, dim: Dimension) -> GeoArrowResult<Self> {
match value.data_type() {
DataType::Struct(_) => {
let downcasted = value.as_any().downcast_ref::<StructArray>().unwrap();
Ok(CoordBuffer::Separated(SeparatedCoordBuffer::from_arrow(
downcasted, dim,
)?))
}
DataType::FixedSizeList(_, _) => {
let downcasted = value.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
Ok(CoordBuffer::Interleaved(
InterleavedCoordBuffer::from_arrow(downcasted, dim)?,
))
}
_ => Err(GeoArrowError::InvalidGeoArrow(format!(
"Unexpected coord buffer type: {:?}",
value.data_type()
))),
}
}
}
impl From<CoordBuffer> for ArrayRef {
fn from(value: CoordBuffer) -> Self {
match value {
CoordBuffer::Interleaved(c) => Arc::new(FixedSizeListArray::from(c)),
CoordBuffer::Separated(c) => Arc::new(StructArray::from(c)),
}
}
}
impl PartialEq for CoordBuffer {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(CoordBuffer::Interleaved(a), CoordBuffer::Interleaved(b)) => PartialEq::eq(a, b),
(CoordBuffer::Interleaved(left), CoordBuffer::Separated(right)) => {
if left.len() != right.len() {
return false;
}
for i in 0..left.len() {
let left_coord = left.value(i);
let right_coord = right.value(i);
if left_coord != right_coord {
return false;
}
}
true
}
(CoordBuffer::Separated(a), CoordBuffer::Separated(b)) => PartialEq::eq(a, b),
(CoordBuffer::Separated(left), CoordBuffer::Interleaved(right)) => {
if left.len() != right.len() {
return false;
}
for i in 0..left.len() {
let left_coord = left.value(i);
let right_coord = right.value(i);
if left_coord != right_coord {
return false;
}
}
true
}
}
}
}
impl From<InterleavedCoordBuffer> for CoordBuffer {
fn from(value: InterleavedCoordBuffer) -> Self {
Self::Interleaved(value)
}
}
impl From<SeparatedCoordBuffer> for CoordBuffer {
fn from(value: SeparatedCoordBuffer) -> Self {
Self::Separated(value)
}
}