package strings

import "core:io"
import "core:unicode/utf8"

Reader :: struct {
	s:         string, // read-only buffer
	i:         i64,    // current reading index
	prev_rune: int,    // previous reading index of rune or < 0
}

reader_init :: proc(r: ^Reader, s: string) {
	r.s = s
	r.i = 0
	r.prev_rune = -1
}

reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
	s.stream_data = r
	s.stream_vtable = _reader_vtable
	return
}

to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
	reader_init(r, s)
	rr, _ := io.to_reader(reader_to_stream(r))
	return rr
}
to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
	reader_init(r, s)
	rr, _ := io.to_reader_at(reader_to_stream(r))
	return rr
}
to_byte_reader :: proc(r: ^Reader, s: string) -> io.Byte_Reader {
	reader_init(r, s)
	rr, _ := io.to_byte_reader(reader_to_stream(r))
	return rr
}
to_rune_reader :: proc(r: ^Reader, s: string) -> io.Rune_Reader {
	reader_init(r, s)
	rr, _ := io.to_rune_reader(reader_to_stream(r))
	return rr
}


reader_length :: proc(r: ^Reader) -> int {
	if r.i >= i64(len(r.s)) {
		return 0
	}
	return int(i64(len(r.s)) - r.i)
}

reader_size :: proc(r: ^Reader) -> i64 {
	return i64(len(r.s))
}

reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
	if r.i >= i64(len(r.s)) {
		return 0, .EOF
	}
	r.prev_rune = -1
	n = copy(p, r.s[r.i:])
	r.i += i64(n)
	return
}
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
	if off < 0 {
		return 0, .Invalid_Offset
	}
	if off >= i64(len(r.s)) {
		return 0, .EOF
	}
	n = copy(p, r.s[off:])
	if n < len(p) {
		err = .EOF
	}
	return
}
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
	r.prev_rune = -1
	if r.i >= i64(len(r.s)) {
		return 0, .EOF
	}
	b := r.s[r.i]
	r.i += 1
	return b, nil
}
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
	if r.i <= 0 {
		return .Invalid_Unread
	}
	r.prev_rune = -1
	r.i -= 1
	return nil
}
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
	if r.i >= i64(len(r.s)) {
		r.prev_rune = -1
		return 0, 0, .EOF
	}
	r.prev_rune = int(r.i)
	if c := r.s[r.i]; c < utf8.RUNE_SELF {
		r.i += 1
		return rune(c), 1, nil
	}
	ch, size = utf8.decode_rune_in_string(r.s[r.i:])
	r.i += i64(size)
	return
}
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
	if r.i <= 0 {
		return .Invalid_Unread
	}
	if r.prev_rune < 0 {
		return .Invalid_Unread
	}
	r.i = i64(r.prev_rune)
	r.prev_rune = -1
	return nil
}
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
	r.prev_rune = -1
	abs: i64
	switch whence {
	case .Start:
		abs = offset
	case .Current:
		abs = r.i + offset
	case .End:
		abs = i64(len(r.s)) + offset
	case:
		return 0, .Invalid_Whence
	}

	if abs < 0 {
		return 0, .Invalid_Offset
	}
	r.i = abs
	return abs, nil
}
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
	r.prev_rune = -1
	if r.i >= i64(len(r.s)) {
		return 0, nil
	}
	s := r.s[r.i:]
	m: int
	m, err = io.write_string(w, s)
	if m > len(s) {
		panic("bytes.Reader.write_to: invalid io.write_string count")
	}
	r.i += i64(m)
	n = i64(m)
	if m != len(s) && err == nil {
		err = .Short_Write
	}
	return
}


@(private)
_reader_vtable := &io.Stream_VTable{
	impl_size = proc(s: io.Stream) -> i64 {
		r := (^Reader)(s.stream_data)
		return reader_size(r)
	},
	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
		r := (^Reader)(s.stream_data)
		return reader_read(r, p)
	},
	impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
		r := (^Reader)(s.stream_data)
		return reader_read_at(r, p, off)
	},
	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
		r := (^Reader)(s.stream_data)
		return reader_read_byte(r)
	},
	impl_unread_byte = proc(s: io.Stream) -> io.Error {
		r := (^Reader)(s.stream_data)
		return reader_unread_byte(r)
	},
	impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
		r := (^Reader)(s.stream_data)
		return reader_read_rune(r)
	},
	impl_unread_rune = proc(s: io.Stream) -> io.Error {
		r := (^Reader)(s.stream_data)
		return reader_unread_rune(r)
	},
	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
		r := (^Reader)(s.stream_data)
		return reader_seek(r, offset, whence)
	},
	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
		r := (^Reader)(s.stream_data)
		return reader_write_to(r, w)
	},
}
