package speiger.src.coreengine.utils.misc;

import java.util.Objects;

public interface CodepointSequence extends CharSequence {
	public int codePointAt(int index);
	public int codePointBefore(int index);
	public int codePointCount(int beginIndex, int endIndex);
	public int offsetByCodePoints(int index, int codePointOffset);
	public default boolean startsWith(String value) { return startsWith(value, 0);}
	public boolean startsWith(String value, int offset);
	
	@Override
	public CodepointSequence subSequence(int start, int end);
	
	public static CodepointSequence of(String value, int from) {
		return new OfString(value, from);
	}
	
	public static CodepointSequence of(String value, int from, int to) {
		return new OfString(value, from, to);
	}
	
	public static CodepointSequence of(StringBuilder value, int from) {
		return new OfBuilder(value, from);
	}
	
	public static CodepointSequence of(StringBuilder value, int from, int to) {
		return new OfBuilder(value, from, to);
	}
	
	record OfBuilder(StringBuilder parent, int from, int to) implements CodepointSequence {
		public OfBuilder {
			if(from < 0 || from > to || to > parent.length()) {
				 throw new IndexOutOfBoundsException("Index Out of Bounds: From="+from+", To="+to+" Length="+parent.length());
			}
		}
		
		public OfBuilder(StringBuilder parent, int from) {
			this(parent, from, parent.length());
		}
		
		@Override
		public char charAt(int index) {
			Objects.checkIndex(index, length());
			return parent.charAt(index+from);
		}
		
		@Override
		public int length() {
			return to-from;
		}
		
		@Override
		public CodepointSequence subSequence(int start, int end) {
			Objects.checkFromToIndex(start, end, length());
	        return new OfBuilder(parent, from+start, from+end);
		}
		
		@Override
		public int codePointAt(int index) {
			Objects.checkIndex(index, length());
			return parent.codePointAt(index+from);
		}
		
		@Override
		public int codePointBefore(int index) {
			Objects.checkIndex(index-1, length());
			return parent.codePointBefore(index+from);
		}
		
		@Override
		public int codePointCount(int beginIndex, int endIndex) {
			Objects.checkFromToIndex(beginIndex, endIndex, length());
			return parent.codePointCount(beginIndex+from, endIndex+from);
		}
		
		@Override
		public int offsetByCodePoints(int index, int codePointOffset) {
			Objects.checkFromIndexSize(index, codePointOffset, length());
			return parent.offsetByCodePoints(index+from, codePointOffset);
		}
		
		@Override
		public final String toString() {
			return parent.substring(from, to);
		}
		
		@Override
		public boolean startsWith(String value, int offset) {
			if(offset < 0 || length() - offset < value.length()) return false;
			for(int i = 0,m=value.length();i<m;i++) {
				if(parent.codePointAt(i+from+offset) != value.codePointAt(i)) return false;
			}
			return true;
		}
	}
	
	record OfString(String parent, int from, int to) implements CodepointSequence {
		public OfString {
			if(from < 0 || from > to || to > parent.length()) {
				 throw new IndexOutOfBoundsException("Index Out of Bounds: From="+from+", To="+to+" Length="+parent.length());
			}
		}
		
		public OfString(String parent, int from) {
			this(parent, from, parent.length());
		}
		
		@Override
		public int length() {
			return to-from;
		}
		
		@Override
		public char charAt(int index) {
			Objects.checkIndex(index, length());
			return parent.charAt(index+from);
		}
		
		@Override
		public CodepointSequence subSequence(int start, int end) {
			Objects.checkFromToIndex(start, end, length());
	        return new OfString(parent, from+start, from+end);
		}

		@Override
		public boolean startsWith(String value, int offset) {
			if(offset < 0 || value.length() > length()-offset) return false;
			return parent.startsWith(value, offset+from);
		}
		
		@Override
		public int codePointAt(int index) {
			Objects.checkIndex(index, length());
			return parent.codePointAt(index+from);
		}
		
		@Override
		public int codePointBefore(int index) {
			Objects.checkIndex(index-1, length());
			return parent.codePointBefore(index+from);
		}
		
		@Override
		public int codePointCount(int beginIndex, int endIndex) {
			Objects.checkFromToIndex(beginIndex, endIndex, length());
			return parent.codePointCount(beginIndex+from, endIndex+from);
		}
		
		@Override
		public int offsetByCodePoints(int index, int codePointOffset) {
			Objects.checkFromIndexSize(index, codePointOffset, length());
			return parent.offsetByCodePoints(index+from, codePointOffset);
		}
		
		@Override
		public final String toString() {
			return parent.substring(from, to);
		}
	}
}
