
import sequences.util.*;
import sequences.handle.*;



//
// Sliced
//

record Sliced[A](
    seq : A,
    first : SequenceCoordinateType(A),
    last : SequenceCoordinateType(A),
);

[A | LightweightSequence?(A)]
overload LightweightSequence?(static Sliced[A]) = true;

[A | ContiguousSequence?(A)]
overload ContiguousSequence?(static Sliced[A]) = true;

[A | CallDefined?(UnderlyingContainerConstructor, Static[A])]
overload UnderlyingContainerConstructor(static Sliced[A]) =
    UnderlyingContainerConstructor(A);



//
// sliced, slicedFrom, slicedUpto
//

define sliced;

[A, C | CoordinateSequence?(A) and (C == SequenceCoordinateType(A))]
inline overload sliced(forward a:A, first:C, last:C) {
    alias T = Type(wrapSequence(a));
    return Sliced[T](wrapSequence(a), first, last);
}

[A, C | C == SequenceCoordinateType(A)]
inline overload sliced(ref a:Sliced[A], first:C, last:C) {
    alias T = Type(wrapSequence(a.seq));
    return Sliced[T](wrapSequence(a.seq), first, last);
}

[A, C | C == SequenceCoordinateType(A)]
inline overload sliced(rvalue a:Sliced[A], first:C, last:C) {
    alias T = Type(move(a.seq));
    Sliced[T](move(a.seq), first, last);
}

[A, I, J | RandomAccessSequence?(A) and Integer?(I) and Integer?(J)]
inline overload sliced(forward a:A, first:I, last:J) {
    var b = begin(a);
    assert[#boundsCheck](first >= 0 and first <= size(a) and last >= 0 and last <= size(a),
        A, " sliced indices out of bounds");
    return sliced(a, b+first, b+last);
}

define slicedFrom;

[A, C | CoordinateSequence?(A) and (C == SequenceCoordinateType(A))]
inline overload slicedFrom(forward a:A, first:C) =
    sliced(a, first, end(a));

[A, I | RandomAccessSequence?(A) and Integer?(I)]
inline overload slicedFrom(forward a:A, first:I) {
    assert[#boundsCheck](first >= 0 and first <= size(a),
        A, " slicedFrom index out out of bounds");
    return slicedFrom(a, begin(a)+first);
}

define slicedUpto;

[A, C | CoordinateSequence?(A) and (C == SequenceCoordinateType(A))]
inline overload slicedUpto(forward a:A, last:C) =
    sliced(a, begin(a), last);

[A, I | RandomAccessSequence?(A) and Integer?(I)]
inline overload slicedUpto(forward a:A, last:I) {
    assert[#boundsCheck](last >= 0 and last <= size(a),
        A, " slicedUpto index out out of bounds");
    return slicedUpto(a, begin(a)+last);
}



//
// sequence operations
//

[A]
inline overload iterator(a:Sliced[A]) = coordinateRange(a.first, a.last);

[A | RandomAccessSequence?(A)]
inline overload reverseIterator(a:Sliced[A]) =
    reverseCoordinateRange(a.last, a.first);

[A | RandomAccessSequence?(A)]
inline overload size(a:Sliced[A]) =
    if (a.last < a.first) SizeT(0) else SizeT(a.last - a.first);

[A, I | RandomAccessSequence?(A) and Integer?(I)]
inline overload index(a:Sliced[A], i:I) = forward ..(a.first + i)^;

[A]
inline overload begin(a:Sliced[A]) = a.first;

[A]
inline overload end(a:Sliced[A]) = a.last;

[A]
inline overload front(a:Sliced[A]) = forward ..a.first^;

[A | RandomAccessSequence?(A)]
inline overload back(a:Sliced[A]) = forward ..a[size(a)-1]^;
