
import sequences.util.*;


//
// mapped
//

inline mapped(forward mapper, forward ..seqs) = Mapped(mapper, zipped(..seqs));

inline overload mapped(forward mapper, forward seq) = Mapped(mapper, wrapSequence(seq));


private record Mapped[F, A] (
    mapper : F,
    seq : A,
);

[F,A | LightweightSequence?(A)]
overload LightweightSequence?(static Mapped[F,A]) = true;

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



//
// MappedIterator
//

[F,A]
inline overload iterator(a:Mapped[F,A]) =
    MappedIterator(a.mapper, iterator(a.seq));

[F,A | CallDefined?(reverseIterator, A)]
inline overload reverseIterator(a:Mapped[F,A]) =
    MappedIterator(a.mapper, reverseIterator(a.seq));

private record MappedIterator[F, ITER] (
    mapper : F,
    iter : ITER,
);

[F, ITER]
inline overload hasNext?(iter:MappedIterator[F, ITER]) = hasNext?(iter.iter);

[F, ITER]
inline overload next(iter:MappedIterator[F, ITER]) =
    forward ..iter.mapper(..next(iter.iter));



//
// MappedCoordinate, mappedInnerCoordinate
//

private record MappedCoordinate[F, COORD] (
    mapper : F,
    coord : COORD,
);

[F,COORD]
mappedInnerCoordinate(x:MappedCoordinate[F,COORD]) = ref x.coord;

[F, A | CallDefined?(begin, A)]
inline overload begin(a:Mapped[F, A]) = MappedCoordinate(a.mapper, begin(a.seq));

[F, A | CallDefined?(end, A)]
inline overload end(a:Mapped[F, A]) = MappedCoordinate(a.mapper, end(a.seq));

[F, COORD]
inline overload dereference(x:MappedCoordinate[F, COORD]) =
    forward ..x.mapper(..dereference(x.coord));

[F, COORD, I | CallDefined?(add, COORD, I)]
inline overload add(x:MappedCoordinate[F, COORD], i:I) =
    MappedCoordinate(x.mapper, x.coord + i);

[F, COORD, I | CallDefined?(subtract, COORD, I)]
inline overload subtract(x:MappedCoordinate[F, COORD], i:I) =
    MappedCoordinate(x.mapper, x.coord - i);

[F, COORD | CallDefined?(subtract, COORD, COORD)]
inline overload subtract(x1:MappedCoordinate[F, COORD],
                         x2:MappedCoordinate[F, COORD]) =
    x1.coord - x2.coord;

[F, COORD | CallDefined?(inc, COORD)]
inline overload inc(x:MappedCoordinate[F, COORD]) {
    inc(x.coord);
}

[F, COORD | CallDefined?(dec, COORD)]
inline overload dec(x:MappedCoordinate[F, COORD]) {
    dec(x.coord);
}



//
// size, StaticSize, index
//

[F, A | CallDefined?(size, A)]
inline overload size(a:Mapped[F, A]) = size(a.seq);

[F, A | CallDefined?(StaticSize, Static[A])]
overload StaticSize(static Mapped[F,A]) = StaticSize(A);

[F, A, I | CallDefined?(index, A, I)]
inline overload index(a:Mapped[F, A], i:I) =
    forward ..a.mapper(..index(a.seq, i));
