package json5

import (
	"bytes"
	"io"
)

type TrimNodeReader struct {
	r  io.Reader
	br *bytes.Reader
}

func isNL(c byte) bool {
	return c == '\n' || c == '\r'
}

func isWS(c byte) bool {
	return c == ' ' || c == '\t' || isNL(c)
}

func consumeComment(s []byte, i int) int {
	if i < len(s) && s[i] == '/' {
		s[i-1] = ' '
		for ; i < len(s) && !isNL(s[i]); i += 1 {
			s[i] = ' '
		}
	}
	if i < len(s) && s[i] == '*' {
		s[i-1] = ' '
		s[i] = ' '
		for ; i < len(s); i += 1 {
			if s[i] != '*' {
				s[i] = ' '
			} else {
				s[i] = ' '
				i++
				if i < len(s) {
					if s[i] == '/' {
						s[i] = ' '
						break
					}
				}
			}
		}
	}
	return i
}

func prep(r io.Reader) (s []byte, err error) {
	buf := &bytes.Buffer{}
	_, err = io.Copy(buf, r)
	s = buf.Bytes()
	if err != nil {
		return
	}

	i := 0
	for i < len(s) {
		switch s[i] {
		case '"':
			i += 1
			for i < len(s) {
				if s[i] == '"' {
					i += 1
					break
				} else if s[i] == '\\' {
					i += 1
				}
				i += 1
			}
		case '/':
			i = consumeComment(s, i+1)
		case ',':
			j := i
			for {
				i += 1
				if i >= len(s) {
					break
				} else if s[i] == '}' || s[i] == ']' {
					s[j] = ' '
					break
				} else if s[i] == '/' {
					i = consumeComment(s, i+1)
				} else if !isWS(s[i]) {
					break
				}
			}
		default:
			i += 1
		}
	}
	return
}

// Read acts as a proxy for the underlying reader and cleans p
// of comments and trailing commas preceeding ] and }
// comments are delimitted by // up until the end the line
func (st *TrimNodeReader) Read(p []byte) (n int, err error) {
	if st.br == nil {
		var s []byte
		if s, err = prep(st.r); err != nil {
			return
		}
		st.br = bytes.NewReader(s)
	}
	return st.br.Read(p)
}

// NewTrimNodeReader New returns an io.Reader acting as proxy to r
func NewTrimNodeReader(r io.Reader) io.Reader {
	return &TrimNodeReader{r: r}
}
