/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::cmp::{max, min};
use std::iter;
use std::fmt;
use std::num;
use std::num::{Bounded, Zero};

/// An index type to be used by a `Range`
pub trait RangeIndex: Copy
                    + Clone
                    + fmt::Show
                    + PartialEq
                    + PartialOrd
                    + Eq
                    + Ord
                    + Add<Self, Self>
                    + Sub<Self, Self>
                    + Neg<Self>
                    + Zero {}

pub trait IntRangeIndex<T>: RangeIndex + Copy {
    fn new(x: T) -> Self;
    fn get(self) -> T;
}

impl RangeIndex for int {}

impl IntRangeIndex<int> for int {
    #[inline]
    fn new(x: int) -> int { x }

    #[inline]
    fn get(self) -> int { self }
}

/// Implements a range index type with operator overloads
#[macro_export]
macro_rules! int_range_index {
    ($(#[$attr:meta])* struct $Self:ident($T:ty)) => (
        #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Show, Zero)]
        $(#[$attr])*
        pub struct $Self(pub $T);

        impl $Self {
            #[inline]
            pub fn to_uint(self) -> uint {
                self.get() as uint
            }
        }

        impl RangeIndex for $Self {}

        impl IntRangeIndex<$T> for $Self  {
            #[inline]
            fn new(x: $T) -> $Self {
                $Self(x)
            }

            #[inline]
            fn get(self) -> $T {
                match self { $Self(x) => x }
            }
        }

        impl Add<$Self, $Self> for $Self {
            #[inline]
            fn add(&self, other: &$Self) -> $Self {
                $Self(self.get() + other.get())
            }
        }

        impl Sub<$Self, $Self> for $Self {
            #[inline]
            fn sub(&self, other: &$Self) -> $Self {
                $Self(self.get() - other.get())
            }
        }

        impl Mul<$Self, $Self> for $Self {
            #[inline]
            fn mul(&self, other: &$Self) -> $Self {
                $Self(self.get() * other.get())
            }
        }

        impl Neg<$Self> for $Self {
            #[inline]
            fn neg(&self) -> $Self {
                $Self(-self.get())
            }
        }

        impl ::std::num::One for $Self {
            fn one() -> $Self {
                $Self(1)
            }
        }

        impl ToPrimitive for $Self {
            fn to_i64(&self) -> Option<i64> {
                Some(self.get() as i64)
            }

            fn to_u64(&self) -> Option<u64> {
                Some(self.get() as u64)
            }
        }
    )
}

/// A range of indices
#[deriving(Clone, Encodable)]
pub struct Range<I> {
    begin: I,
    length: I,
}

impl<I: RangeIndex> fmt::Show for Range<I> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{} .. {})", self.begin(), self.end())
    }
}

/// An iterator over each index in a range
pub struct EachIndex<T, I> {
    it: iter::Range<T>,
}

pub fn each_index<T: Int, I: IntRangeIndex<T>>(start: I, stop: I) -> EachIndex<T, I> {
    EachIndex { it: iter::range(start.get(), stop.get()) }
}

impl<T: Int, I: IntRangeIndex<T>> Iterator<I> for EachIndex<T, I> {
    #[inline]
    fn next(&mut self) -> Option<I> {
        self.it.next().map(|i| IntRangeIndex::new(i))
    }

    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        self.it.size_hint()
    }
}

impl<I: RangeIndex> Range<I> {
    /// Create a new range from beginning and length offsets. This could be
    /// denoted as `[begin, begin + length)`.
    ///
    /// ~~~ignore
    ///    |-- begin ->|-- length ->|
    ///    |           |            |
    /// <- o - - - - - +============+ - - - ->
    /// ~~~
    #[inline]
    pub fn new(begin: I, length: I) -> Range<I> {
        Range { begin: begin, length: length }
    }

    #[inline]
    pub fn empty() -> Range<I> {
        Range::new(num::zero(), num::zero())
    }

    /// The index offset to the beginning of the range.
    ///
    /// ~~~ignore
    ///    |-- begin ->|
    ///    |           |
    /// <- o - - - - - +============+ - - - ->
    /// ~~~
    #[inline]
    pub fn begin(&self) -> I { self.begin  }

    /// The index offset from the beginning to the end of the range.
    ///
    /// ~~~ignore
    ///                |-- length ->|
    ///                |            |
    /// <- o - - - - - +============+ - - - ->
    /// ~~~
    #[inline]
    pub fn length(&self) -> I { self.length }

    /// The index offset to the end of the range.
    ///
    /// ~~~ignore
    ///    |--------- end --------->|
    ///    |                        |
    /// <- o - - - - - +============+ - - - ->
    /// ~~~
    #[inline]
    pub fn end(&self) -> I { self.begin + self.length }

    /// `true` if the index is between the beginning and the end of the range.
    ///
    /// ~~~ignore
    ///        false        true      false
    ///          |           |          |
    /// <- o - - + - - +=====+======+ - + - ->
    /// ~~~
    #[inline]
    pub fn contains(&self, i: I) -> bool {
        i >= self.begin() && i < self.end()
    }

    /// `true` if the offset from the beginning to the end of the range is zero.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.length().is_zero()
    }

    /// Shift the entire range by the supplied index delta.
    ///
    /// ~~~ignore
    ///                     |-- delta ->|
    ///                     |           |
    /// <- o - +============+ - - - - - | - - - ->
    ///                                 |
    /// <- o - - - - - - - +============+ - - - ->
    /// ~~~
    #[inline]
    pub fn shift_by(&mut self, delta: I) {
        self.begin = self.begin + delta;
    }

    /// Extend the end of the range by the supplied index delta.
    ///
    /// ~~~ignore
    ///                     |-- delta ->|
    ///                     |           |
    /// <- o - - - - - +====+ - - - - - | - - - ->
    ///                                 |
    /// <- o - - - - - +================+ - - - ->
    /// ~~~
    #[inline]
    pub fn extend_by(&mut self, delta: I) {
        self.length = self.length + delta;
    }

    /// Move the end of the range to the target index.
    ///
    /// ~~~ignore
    ///                               target
    ///                                 |
    /// <- o - - - - - +====+ - - - - - | - - - ->
    ///                                 |
    /// <- o - - - - - +================+ - - - ->
    /// ~~~
    #[inline]
    pub fn extend_to(&mut self, target: I) {
        self.length = target - self.begin;
    }

    /// Adjust the beginning offset and the length by the supplied deltas.
    #[inline]
    pub fn adjust_by(&mut self, begin_delta: I, length_delta: I) {
        self.begin = self.begin + begin_delta;
        self.length = self.length + length_delta;
    }

    /// Set the begin and length values.
    #[inline]
    pub fn reset(&mut self, begin: I, length: I) {
        self.begin = begin;
        self.length = length;
    }

    #[inline]
    pub fn intersect(&self, other: &Range<I>) -> Range<I> {
        let begin = max(self.begin(), other.begin());
        let end = min(self.end(), other.end());

        if end < begin {
            Range::empty()
        } else {
            Range::new(begin, end - begin)
        }
    }
}

/// Methods for `Range`s with indices based on integer values
impl<T: Int, I: IntRangeIndex<T>> Range<I> {
    /// Returns an iterater that increments over `[begin, end)`.
    #[inline]
    pub fn each_index(&self) -> EachIndex<T, I> {
        each_index(self.begin(), self.end())
    }

    #[inline]
    pub fn is_valid_for_string(&self, s: &str) -> bool {
        let s_len = s.len();
        match num::cast::<uint, T>(s_len) {
            Some(len) => {
                let len = IntRangeIndex::new(len);
                self.begin() < len
                && self.end() <= len
                && self.length() <= len
            },
            None => {
                debug!("Range<T>::is_valid_for_string: string length (len={}) is longer than the \
                        max value for the range index (max={})", s_len,
                        {
                            let max: T = Bounded::max_value();
                            let val: I = IntRangeIndex::new(max);
                            val
                        });
                false
            },
        }
    }
}