package lexer

import (
	"fmt"
	"strings"

	"github.com/jorenchik/mdemory/src/compiler/comperror"
)

var buffer            []rune
var	row                 int32
var	column              int32
var	previousRow         int32
var	previousColumn      int32
var textStarted         bool  = false
var identifierStarted   bool  = false

type TokenType int
const (
	TextFragment TokenType = iota
	QuestionEnd
	ElementDashStart
	ElementPlusStart
	Identifier              
	IdentifierStart
	IdentifierEnd
	SectionIdentifierStart
	SectionStart
	SectionEnd
	SOF
	EOF
)

type Token struct {
	TokenType TokenType;
	Content   string;
	Row		  int32;
	Column    int32;
} 

func (token Token) ToString() string {
	content := token.Content
	if (token.TokenType == TextFragment) {
		content = strings.Replace(
			strings.Trim(content, " "),
			"\n",
			"\\n",
			-1,
		)
	}
	return fmt.Sprintf(
		"%20s: %35s %3d:%3d\n",
		ToString(&token.TokenType),
		content,
		token.Row,
		token.Column,
	)
}

var tokens []Token

func makeTokenWithTokenBuffer(
    ttype    TokenType,
    tokenLen int32,
    textType TokenType,
) {
	if (len(strings.Trim(string(buffer), " \n\t")) - 1 > 0) {
		textFragment := []rune{}
		for i := 0; i < len(buffer) - int(tokenLen); i++ {
			element := buffer[i]
			textFragment = append(textFragment, element)
		}
		tokens = append(
			tokens,
			Token{
				TokenType: textType,
				Content: string(textFragment),
				Row: int32(previousRow),
				Column: int32(previousColumn),
			},
		)
	}
	tokens = append(
		tokens,
		Token{
			TokenType: ttype,
			Content: string(buffer[len(buffer)-int(tokenLen):]),
			Row: int32(row),
			Column: int32(column),
		},
	)
	previousRow = row
	previousColumn = column
	buffer = []rune{}
}

func TokenizeMdem(fileRunes []rune) ([]Token, error) {
	tokens         = []Token{} 
	buffer         = []rune{} 
    row            = 1
    column         = 1
    previousRow    = -1
    previousColumn = -1
    textStarted    = false

    if (len(strings.Trim(string(fileRunes), " \n\t")) <= 0) {
        return []Token{}, nil
    }

	for i := 0; i < len(fileRunes); i++ {
		c := fileRunes[i]

        // AdvancePointer
		if (c == '\n') {
			row += 1
			column = 1
		}
		buffer = append(buffer, c)

        // SkipWhitetext
		if !textStarted {
			if c == '\n' {
				previousRow += 1
				previousColumn = 1
			} else if (c == ' ') {
				previousColumn += 1
			} else {
				textStarted = true 
			}
		}

        // EmitTokens
		switch c {
		case '[':
			makeTokenWithTokenBuffer(IdentifierStart, 1, TextFragment)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
            identifierStarted = true
		case ']':
            if !identifierStarted {
                return []Token{}, comperror.PositionErr{
                    Message: "Cannot end identifier if it is not started",
                    Row:     row,
                    Column:  column,
                } 

            }
			makeTokenWithTokenBuffer(IdentifierEnd, 1, Identifier)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
            identifierStarted = false
		case '#':
			makeTokenWithTokenBuffer(SectionIdentifierStart, 1, TextFragment)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
		case '{':
			makeTokenWithTokenBuffer(SectionStart, 1, Identifier)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
		case '}':
			makeTokenWithTokenBuffer(SectionEnd, 1, TextFragment)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
		case '-':
			makeTokenWithTokenBuffer(ElementDashStart, 1, TextFragment)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
		case '>':
			makeTokenWithTokenBuffer(QuestionEnd, 1, TextFragment)
			previousRow = row
			previousColumn = column
		case '+':
			makeTokenWithTokenBuffer(ElementPlusStart, 1, TextFragment)
			previousRow       = row
			previousColumn    = column
			textStarted       = false
		}
		column += 1
	}

    // EmitEOF
	makeTokenWithTokenBuffer(EOF, 0, TextFragment)

	if true {
		fmt.Printf("Lexer output:\n")
		for _, el := range tokens {
			fmt.Print(el.ToString())
		}
		fmt.Printf("End Lexer output\n")
	}
	return tokens, nil
}

func ToString (ttype *TokenType) string {
	switch *ttype {
        case TextFragment:           return "text fragment"
        case QuestionEnd:            return "question end symbol"
        case ElementDashStart:       return "dash element start"
        case ElementPlusStart:       return "plus element start"
        case Identifier:             return "identifier"
        case IdentifierStart:        return "start of identifier"
        case IdentifierEnd:          return "end of identifier"
        case SectionIdentifierStart: return "identifier"
        case SectionStart:           return "start of a section"
        case SectionEnd:             return "end of a section"
        case EOF:                    return "end of a file"
        default:                     return "unrecognized token"
	}
}
