package csvparser

import (
	"strings"
)
type EncloserId int

const (
	DoubleQuotes EncloserId = iota
	SingleQuotes
	RoundBrackets
	SquareBrackets
	CurlyBrackets
)

type Encloser struct {
	Open  rune
	Close rune
}

var EnclosersRunes = map[EncloserId]Encloser{
	DoubleQuotes:   Encloser{'"', '"'},
	SingleQuotes:   Encloser{'\'', '\''},
	RoundBrackets:  Encloser{'(', ')'},
	SquareBrackets: Encloser{'[', ']'},
	CurlyBrackets:  Encloser{'{', '}'},
}

type CsvParser struct {
	Enclosers []EncloserId
	Delimiter rune
	DelimiterString string
}

func (parser *CsvParser) Init() {
	parser.Enclosers = []EncloserId{DoubleQuotes, SquareBrackets}
	parser.Delimiter = ' '
	parser.DelimiterString = string(parser.Delimiter)
}
func (parser *CsvParser) Parse(line string) error {
	for len(line) > 0 {
		line = strings.TrimLeft(line, parser.DelimiterString)
		line = strings.TrimRight(line, parser.DelimiterString)

		if len(line) == 0 {
			break
		}
		// Search for an encloser
		for id := range parser.Enclosers {
			if line[0] == EnclosersRunes[id].Open {
				endChar := EnclosersRunes[id].Close
				endOfFieldFound := false
				l := len(line)
				for i := 1; i < l; i++ {
					if line[i] == endChar && line[i-1] != '\\' {
						fields = append(fields, line[1:i])
						line = line[i+1:]
						endOfFieldFound = true
						break
					}
				}
				if !endOfFieldFound {
					return nil, errors.New("#ERR: bad format")
				}
				break;
			}
		}
		if line[0] == '"' || line[0] == '[' {
			var endChar byte = '"'
			if line[0] == '[' {
				endChar = ']'
			}
			endOfFieldFound := false
			l := len(line)
			for i := 1; i < l; i++ {
				if line[i] == endChar && line[i-1] != '\\' {
					fields = append(fields, line[1:i])
					line = line[i+1:]
					endOfFieldFound = true
					break
				}
			}
			if !endOfFieldFound {
				return nil, errors.New("#ERR: bad format")
			}
		} else {
			nextSpace := strings.IndexAny(line, " \t")
			if nextSpace != -1 {
				fields = append(fields, line[:nextSpace])
				line = line[nextSpace:]
			} else {
				fields = append(fields, line)
				line = ""
				break
			}
		}
	}
	return fields, nil

}