package lexer

import "strconv"
import "git.tebibyte.media/arf/arf/infoerr"

// tokenizeSymbolBeginning lexes a token that starts with a number.
func (lexer *lexingOperation) tokenizeNumberBeginning (negative bool) (err error) {
	var intNumber   uint64
	var floatNumber float64
	var isFloat     bool
	var amountRead  int
	var totalRead   int

	token := lexer.newToken()

	if lexer.char == '0' {
		lexer.nextRune()
		totalRead ++

		if lexer.char == 'x' {
			lexer.nextRune()
			totalRead ++
			intNumber, floatNumber,
			isFloat, amountRead,
			err = lexer.tokenizeNumber(16)
			
		} else if lexer.char == 'b' {
			lexer.nextRune()
			totalRead ++
			intNumber, floatNumber,
			isFloat, amountRead,
			err = lexer.tokenizeNumber(2)
			
		} else if lexer.char == '.' {
			intNumber, floatNumber,
			isFloat, amountRead,
			err = lexer.tokenizeNumber(10)
			
		} else if lexer.char >= '0' && lexer.char <= '7' {
			intNumber, floatNumber,
			isFloat, amountRead,
			err = lexer.tokenizeNumber(8)
		} else if lexer.char >= '8' && lexer.char <= '9' {
			err = infoerr.NewError (
				lexer.file.Location(1),
				"unexpected number '" + string(lexer.char) +
				"' in octal literal",
				infoerr.ErrorKindError)
			return
		}
	} else {
		intNumber, floatNumber,
		isFloat, amountRead,
		err = lexer.tokenizeNumber(10)
	}

	totalRead += amountRead
	if negative {
		totalRead += 1
	}

	if err != nil { return }

	if isFloat {
		token.kind  = TokenKindFloat
		if negative {
			token.value = floatNumber * -1
		} else {
			token.value = floatNumber
		}
	} else {
		if negative {
			token.kind  = TokenKindInt
			token.value = int64(intNumber) * -1
		} else {
			token.kind  = TokenKindUInt
			token.value = uint64(intNumber)
		}		
	}

	token.location.SetWidth(totalRead)
	lexer.addToken(token)
	return
}

// runeIsDigit checks to see if the rune is a valid digit within the given
// radix, up to 16. A '.' rune will also be treated as valid.
func runeIsDigit (char rune, radix uint64) (isDigit bool) {
	isDigit = true

	var digit uint64
	if char >= '0' && char <= '9' {
		digit = uint64(char - '0')
	} else if char >= 'A' && char <= 'F' {
		digit = uint64(char - 'A' + 10)
	} else if char >= 'a' && char <= 'f' {
		digit = uint64(char - 'a' + 10)
	} else if char != '.' {
		isDigit = false
	}

	if digit >= radix {
		isDigit = false
	}

	return
}

// tokenizeNumber reads and tokenizes a number with the specified radix.
func (lexer *lexingOperation) tokenizeNumber (
	radix uint64,
) (
	intNumber   uint64,
	floatNumber float64,
	isFloat     bool,
	amountRead  int,
	err         error,
) {
	got := ""
	for {
		if !runeIsDigit(lexer.char, radix) { break }
		if lexer.char == '.' {
			if radix != 10 {
				err = infoerr.NewError (
					lexer.file.Location(1),
					"floats must have radix of 10",
					infoerr.ErrorKindError)
				return
			}
			isFloat = true
		}

		got += string(lexer.char)
		err = lexer.nextRune()
		if err != nil { return }
	}

	amountRead = len(got)

	if isFloat {
		floatNumber, err = strconv.ParseFloat(got, 64)
	} else {
		intNumber, err = strconv.ParseUint(got, int(radix), 64)
	}
	
	if err != nil {
		err = infoerr.NewError (
			lexer.file.Location(1),
			"could not parse number: " + err.Error(),
			infoerr.ErrorKindError)
		return
	}

	return
}
