use std::ops::Deref;
use std::ops::Range;

use crate::*;

/// Arbitrary immutable text as span from some shared string
#[derive(Debug)]
pub struct SubStr {
    string: CowStr,
    range: Range<usize>,
}

/// Tried to use a range larger than the string it should cover.
#[derive(Debug)]
pub struct RangeError;

impl SubStr {
    /// Create a `SubStr` that spans a complete `CowStr`. This function consumes its input,
    /// when this is not desired then the input must be cloned first.
    #[must_use]
    pub fn new(from: CowStr) -> SubStr {
        let len = from.len();
        SubStr {
            string: from,
            range: 0..len,
        }
    }

    /// Create and `SubStr` as span of a `CowStr`. This function consumes its input `CowStr`,
    /// when this is not desired then the input must be cloned first.
    #[must_use]
    pub fn new_range(from: CowStr, start: usize, end: usize) -> Result<SubStr, RangeError> {
        if from.get(start..end).is_some() {
            Ok(SubStr {
                string: from,
                range: start..end,
            })
        } else {
            Err(RangeError)
        }
    }

    /// Create a `SubStr` as range from an existing `SubStr`.  This function consumes its
    /// input `CowStr`, when this is not desired then the input must be cloned first.
    #[must_use]
    pub fn sub_range(&self, start: usize, end: usize) -> Result<SubStr, RangeError> {
        Self::new_range(
            self.string.clone(),
            self.range.start + start,
            self.range.start + end,
        )
    }

    /// Create a `SubStr` that spans a complete `'static str`,
    #[must_use]
    pub fn from_static(from: &'static str) -> SubStr {
        SubStr::new(CowStr::from_static(from))
    }

    /// Returns `Ok(&'static str)` when the underlying string has static lifetime and
    /// `Err(&'self str)` when it is dynamically allocated.
    pub fn deref_static(&self) -> Result<&'static str, &str> {
        match self.string.deref_static() {
            Ok(s) => Ok(&s[self.range.clone()]),
            Err(s) => Err(&s[self.range.clone()]),
        }
    }
}

impl Deref for SubStr {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.string[self.range.clone()]
    }
}

#[test]
fn from() {
    let origin = CowStr::from("test string");
    let substr = SubStr::new_range(origin, 0, 4).unwrap();
    assert_eq!(&*substr, "test");

    let subsubstr = substr.sub_range(1, 3).unwrap();
    assert_eq!(&*subsubstr, "es");
}

#[test]
#[should_panic]
fn from_panic() {
    let origin = CowStr::from("test string");
    let _substr = SubStr::new_range(origin, 10, 100).unwrap();
}
