# Copyright 2021 Nikita Melekhin. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

from Lexer.lexer import Lexer
from token import Token
from type_file import Type
from connection import Connection


class Parser:

    def set_code_lines(self, code: [str]):
        self.lexer = Lexer(code)
        self.token = Token()
        self.current_token_id = -1
        self.read_tokens = 0
        self.lexer_rich_the_end = 0
        self.tokens = []
        self.next_token()

    def next_token(self):
        self.current_token_id += 1
        self.token = self.get_token_at(self.current_token_id)

    def get_token_at(self, pos):
        if pos >= self.read_tokens and not self.lexer_rich_the_end:
            for i in range(pos - self.read_tokens + 1):
                self.tokens.append(self.lexer.next_token())
                self.read_tokens += 1
                if self.tokens[-1].type == Type.Special.EOF:
                    self.lexer_rich_the_end = True
                    break

        return self.tokens[min(pos, self.read_tokens - 1)]

    def is_nth(self, type_of_token, n):
        if isinstance(type_of_token, list):
            for type_of_cur_token in type_of_token:
                if self.get_token_at(self.current_token_id+n).type == type_of_cur_token:
                    return True
            return False
        return self.get_token_at(self.current_token_id+n).type == type_of_token

    def is_next(self, type_of_token):
        if isinstance(type_of_token, list):
            for type_of_cur_token in type_of_token:
                if self.token.type == type_of_cur_token:
                    return True
            return False
        return self.token.type == type_of_token

    def must_next(self, type_of_token):
        if not self.is_next(type_of_token):
            print("{0} is not {1}".format(self.token, type_of_token))
            exit(1)

    def __init__(self):
        pass

    def eat_name(self):
        self.must_next(Type.Reserved.Name)
        self.next_token()
        self.must_next(Type.Lang.Colon)
        self.next_token()
        self.must_next(Type.Word)
        res = self.token.value
        self.next_token()
        return res

    def eat_protected(self):
        if (self.is_next(Type.Reserved.KeyProtected)):
            self.next_token()
            return True
        return False

    def eat_magic(self):
        self.must_next(Type.Reserved.Magic)
        self.next_token()
        self.must_next(Type.Lang.Colon)
        self.next_token()
        self.must_next(Type.Number.Integer)
        res = self.token.value
        self.next_token()
        return res

    def eat_params(self):
        params = []
        self.must_next(Type.Lang.LeftBracket)
        self.next_token()
        while not self.is_next(Type.Lang.RightBracket):
            self.must_next(Type.Word)
            typ = self.token.value
            self.next_token()

            self.must_next(Type.Word)
            nam = self.token.value
            self.next_token()

            params.append([typ, nam])

            if self.is_next(Type.Lang.Comma):
                self.must_next(Type.Lang.Comma)
                self.next_token()

        self.must_next(Type.Lang.RightBracket)
        self.next_token()
        return params

    def eat_message(self, decoder):
        self.must_next(Type.Word)
        msgname = self.token.value
        self.next_token()
        decoder.add_message(msgname, self.eat_params())
        return msgname

    def eat_function(self, decoder):
        ms1 = self.eat_message(decoder)
        ms2 = None

        if self.is_next(Type.Reserved.Return):
            self.must_next(Type.Reserved.Return)
            self.next_token()
            ms2 = self.eat_message(decoder)

        decoder.add_function(ms1, ms2)

    def eat_decoder(self):
        self.must_next(Type.Reserved.Begin)
        self.next_token()
        is_protected = self.eat_protected()
        decoder = Connection(self.eat_name(), self.eat_magic(), is_protected)
        while not self.is_next(Type.Reserved.End):
            self.eat_function(decoder)
        self.must_next(Type.Reserved.End)
        self.next_token()
        return decoder

    def parse(self):
        decoders = []
        while self.is_next(Type.Reserved.Begin):
            decoders.append(self.eat_decoder())
        print("connc: {0} decoders parsed!".format(len(decoders)))
        return decoders
