package parser

import (
	"bytes"
	"strconv"

	"github.com/go-pg/pg/internal"
)

type Parser struct {
	b []byte
}

func New(b []byte) *Parser {
	return &Parser{
		b: b,
	}
}

func NewString(s string) *Parser {
	return New(internal.StringToBytes(s))
}

func (p *Parser) Bytes() []byte {
	return p.b
}

func (p *Parser) Valid() bool {
	return len(p.b) > 0
}

func (p *Parser) Read() byte {
	if p.Valid() {
		c := p.b[0]
		p.Skip(c)
		return c
	}
	return 0
}

func (p *Parser) Peek() byte {
	if p.Valid() {
		return p.b[0]
	}
	return 0
}

func (p *Parser) Advance() {
	p.b = p.b[1:]
}

func (p *Parser) Skip(c byte) bool {
	if p.Peek() == c {
		p.Advance()
		return true
	}
	return false
}

func (p *Parser) SkipBytes(b []byte) bool {
	if len(b) > len(p.b) {
		return false
	}
	if !bytes.Equal(p.b[:len(b)], b) {
		return false
	}
	p.b = p.b[len(b):]
	return true
}

func (p *Parser) ReadSep(c byte) ([]byte, bool) {
	ind := bytes.IndexByte(p.b, c)
	if ind == -1 {
		b := p.b
		p.b = p.b[len(p.b):]
		return b, false
	}

	b := p.b[:ind]
	p.b = p.b[ind+1:]
	return b, true
}

func (p *Parser) ReadIdentifier() (s string, numeric bool) {
	end := len(p.b)
	numeric = true
	for i, ch := range p.b {
		if isNum(ch) {
			continue
		}
		if isAlpha(ch) || ch == '_' {
			numeric = false
			continue
		}
		end = i
		break
	}
	if end <= 0 {
		return "", false
	}
	b := p.b[:end]
	p.b = p.b[end:]
	return internal.BytesToString(b), numeric
}

func (p *Parser) ReadNumber() int {
	end := len(p.b)
	for i, ch := range p.b {
		if !isNum(ch) {
			end = i
			break
		}
	}
	if end <= 0 {
		return 0
	}
	n, _ := strconv.Atoi(string(p.b[:end]))
	p.b = p.b[end:]
	return n
}

func (p *Parser) readSubstring() []byte {
	var b []byte
	for p.Valid() {
		c := p.Read()
		switch c {
		case '\\':
			switch p.Peek() {
			case '\\':
				b = append(b, '\\')
				p.Advance()
			case '"':
				b = append(b, '"')
				p.Advance()
			default:
				b = append(b, c)
			}
		case '\'':
			switch p.Peek() {
			case '\'':
				b = append(b, '\'')
				p.Skip(c)
			default:
				b = append(b, c)
			}
		case '"':
			return b
		default:
			b = append(b, c)
		}
	}
	return b
}
