// Memory range for ref or box data. For boundary checking
pub struct FfiRefBounds {
    // Indicates how much data is above the pointer
    pub(crate) above: usize,
    // Indicates how much data is below the pointer
    pub(crate) below: usize,
}

pub const UNSIZED_BOUNDS: FfiRefBounds = FfiRefBounds {
    above: usize::MAX,
    below: usize::MAX,
};

impl FfiRefBounds {
    pub fn new(above: usize, below: usize) -> Self {
        Self { above, below }
    }

    pub fn is_unsized(&self) -> bool {
        self.above == usize::MAX && self.below == usize::MAX
    }

    // Check boundary
    pub fn check_boundary(&self, offset: isize) -> bool {
        if self.is_unsized() {
            return true;
        }
        let sign = offset.signum();
        let offset_abs = offset.unsigned_abs();
        if sign == -1 {
            self.above >= offset_abs
        } else if sign == 1 {
            self.below >= offset_abs
        } else {
            // sign == 0
            true
        }
    }

    // Check boundary
    // Check required here
    pub fn check_sized(&self, offset: isize, size: usize) -> bool {
        if self.is_unsized() {
            return true;
        }
        if offset < 0 && self.above < offset.unsigned_abs() {
            return true;
        }
        let end = offset + (size as isize) - 1;
        let end_sign = end.signum();
        let end_abs = end.unsigned_abs();
        if end_sign == -1 {
            self.above >= end_abs
        } else if end_sign == 1 {
            self.below >= end_abs
        } else {
            // sign == 0
            true
        }
    }

    // Calculate new bounds from bounds and offset
    // No boundary checking in here
    pub fn offset(&self, offset: isize) -> Self {
        let sign = offset.signum();
        let offset_abs = offset.unsigned_abs();

        let high: usize = if sign == -1 {
            self.above - offset_abs
        } else if sign == 1 {
            self.above + offset_abs
        } else {
            self.above
        };

        let low: usize = if sign == -1 {
            self.below + offset_abs
        } else if sign == 1 {
            self.below - offset_abs
        } else {
            self.below
        };

        Self {
            above: high,
            below: low,
        }
    }
}

impl Clone for FfiRefBounds {
    fn clone(&self) -> Self {
        Self {
            above: self.above,
            below: self.below,
        }
    }
}
