package com.googlecode.totallylazy;
import com.googlecode.totallylazy.functions.Curried2;
import com.googlecode.totallylazy.functions.Function1;
import com.googlecode.totallylazy.functions.Unary;
import com.googlecode.totallylazy.iterators.SegmentIterator;
import com.googlecode.totallylazy.segments.AbstractSegment;
import com.googlecode.totallylazy.segments.CharacterSegment;
import java.io.Reader;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import static com.googlecode.totallylazy.Option.none;
import static com.googlecode.totallylazy.Option.some;
import static com.googlecode.totallylazy.Unchecked.cast;
public interface Segment<T> {
Option<T> headOption();
Segment<T> tail() throws NoSuchElementException;
default boolean isEmpty() {
return headOption().isEmpty();
}
default T head() throws NoSuchElementException {
return headOption().get();
}
default Segment<T> empty() {
return constructors.emptySegment();
}
default Segment<T> cons(T head) {
return constructors.cons(head, this);
}
default <C extends Segment<T>> C joinTo(C rest) {
return cast(tail().joinTo(rest).cons(head()));
}
class constructors {
public static <T> Segment<T> emptySegment(Class<T> aClass) {
return new EmptySegment<T>();
}
public static <T> Segment<T> emptySegment() {
return new EmptySegment<T>();
}
public static <T> Segment<T> segment(final T head) {
return segment(head, constructors.<T>emptySegment());
}
public static <T> Segment<T> segment(T head, Segment<T> tail) {
return ASegment.segment(head, tail);
}
public static <T> Segment<T> cons(T head, Segment<T> tail) {
return segment(head, tail);
}
public static <T> Segment<T> unique(T head, Segment<T> tail) {
return segment(head, tail.head().equals(head) ? tail.tail() : tail);
}
public static Segment<Character> characters(CharSequence charSequence) {
return CharacterSegment.characterSegment(charSequence);
}
public static Computation<Character> characters(final Reader reader) {
return Computation.computation1((Callable<Option<Character>>) () -> {
int read = reader.read();
if (read == -1) return none();
return some((char) read);
}, (Character character) -> characters(reader));
}
}
class methods {
public static <T, S extends Segment<T>> S cons(T head, S segment) {
return cast(segment.cons(head));
}
public static <T, A extends Segment<T>, B extends Segment<T>> B joinTo(A source, B destination) {
return cast(source.joinTo(destination));
}
public static <T> Sequence<T> sequence(final Segment<T> segments) {
return new Sequence<T>() {
@Override
public Iterator<T> iterator() {
return SegmentIterator.iterator(segments);
}
};
}
public static <T> Option<T> headOption(Segment<T> segment) {
return segment.isEmpty() ? Option.<T>none() : some(segment.head());
}
public static boolean equalTo(Segment<?> a, Segment<?> b) {
return Sequences.equalTo(methods.sequence(a), methods.sequence(b));
}
public static String toString(Segment<?> segment, String separator) {
return Iterators.toString(SegmentIterator.iterator(segment), separator);
}
}
class functions {
public static <T, Self extends Segment<T>> Curried2<Self, T, Self> cons() {
return (set, t) -> cast(set.cons(t));
}
public static <T, Self extends Segment<T>> Unary<Self> tail() {
return segment -> cast(segment.tail());
}
public static <T, Self extends Segment<T>> Function1<Self, Self> cons(T t) {
return functions.<T, Self>cons().flip().apply(t);
}
public static Function1<String, Segment<Character>> characters() {
return constructors::characters;
}
public static <T> Function1<Segment<T>, Option<T>> headOption() {
return Segment<T>::headOption;
}
}
class ASegment<T> extends AbstractSegment<T> {
private final T head;
private final Segment<T> tail;
private ASegment(T head, Segment<T> tail) {
this.head = head;
this.tail = tail;
}
static <T> ASegment<T> segment(T head, Segment<T> tail) {
return new ASegment<T>(head, tail);
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public T head() throws NoSuchElementException {
return head;
}
@Override
public Segment<T> tail() throws NoSuchElementException {
return tail;
}
}
class EmptySegment<T> extends AbstractSegment<T> {
@Override
public boolean isEmpty() {
return true;
}
@Override
public T head() throws NoSuchElementException {
throw new NoSuchElementException();
}
@Override
public Segment<T> tail() throws NoSuchElementException {
throw new NoSuchElementException();
}
@Override
public <C extends Segment<T>> C joinTo(C rest) {
return rest;
}
}
}