from typing import List

from lark import Transformer
from lark import Token
from source.models import *
from dataclasses import dataclass
from .exception import DevicetreeException

def flatten_token_array(tokens: List[Token], name: str):
    result_list = list()
    for token in tokens:
        result_list.append(token.value)
    return Token(name, result_list)

@dataclass
class NodeNameAndAlias:
    name: str
    alias: str

class DtsTransformer(Transformer):
    # Flatten the start node into a list
    def start(self, tokens):
        return tokens
    def dts_version(self, tokens: List[Token]):
        version = tokens[0].value
        if version != "dts-v1":
            raise DevicetreeException(f"Unsupported DTS version: {version}")
        return DtsVersion(version)
    def device(self, tokens: list):
        node_name = None
        node_alias = None
        node_address = None
        status = None
        properties = list()
        child_devices = list()
        for index, item in enumerate(tokens):
            if type(item) is Token and item.type == 'NODE_NAME':
                node_name = item.value
            elif type(item) is Token and item.type == 'NODE_ALIAS':
                node_alias = item.value
            elif type(item) is Token and item.type == 'NODE_ADDRESS':
                node_address = item.value
            elif type(item) is DeviceProperty:
                if item.name == "status":
                    status = item.value
                else:
                    properties.append(item)
            elif type(item) is Device:
                child_devices.append(item)
        return Device(node_name, node_alias, node_address, status, properties, child_devices)
    def device_property(self, objects: List[object]):
        name = objects[0]
        # Boolean property has no value as the value is implied to be true
        if (len(objects) == 1) or (objects[1] is None):
            return DeviceProperty(name, "boolean", True)
        if type(objects[1]) is not PropertyValue:
            raise DevicetreeException(f"Object was not converted to PropertyValue: {objects[1]}")
        return DeviceProperty(name, objects[1].type, objects[1].value)
    def property_value(self, tokens: List):
        token = tokens[0]
        if type(token) is Token:
            raise DevicetreeException(f"Failed to convert token to PropertyValue: {token}")
        return token
    def PHANDLE(self, token: Token):
        return PropertyValue(type="phandle", value=token.value[1:])
    def values(self, object):
        return PropertyValue(type="values", value=object)
    def value(self, object):
        # PHANDLE is already converted to PropertyValue
        if isinstance(object[0], PropertyValue):
            return object[0]
        return PropertyValue(type="value", value=object[0])
    def phandle_array_entry(self, tokens: list):
        return tokens[0]
    def phandle_array(self, tokens: list):
        return PropertyValue(type="phandle-array", value=tokens)
    def array(self, object):
        return PropertyValue(type="array", value=object)
    def VALUE(self, token: Token):
        if token.value.startswith("&"):
            return PropertyValue(type="phandle", value=token.value[1:])
        return token.value
    def NUMBER(self, token: Token):
        return token.value
    def PROPERTY_NAME(self, token: Token):
        return token.value
    def QUOTED_TEXT(self, token: Token):
        return PropertyValue("text", token.value[1:-1])
    def quoted_text_array(self, tokens: List[Token]):
        result_list = list()
        for token in tokens:
            result_list.append(token.value)
        return PropertyValue("text_array", result_list)
    def INCLUDE_C(self, token: Token):
        return IncludeC(token.value)
    def DEFINE_C(self, token: Token):
        return DefineC(token.value)