import ast

import astcheck
import astsearch

from ..code import Parameter

__all__ = ['extract_definitions', 'build_definitions']

def check_fillable_node(node, path):
    if isinstance(node, (ast.Num, ast.Str)):
        return
    elif isinstance(node, ast.NameConstant) and (node.value in (True, False)):
        return
    elif isinstance(node, ast.List):
        for n in node.elts:
            check_fillable_node(n, path)
        return
    elif isinstance(node, ast.Dict):
        for n in node.keys:
            check_fillable_node(n, path)
        for n in node.values:
            check_fillable_node(n, path)
        return
    
    raise astcheck.ASTMismatch(path, node, 'number, string or boolean')

definition_pattern = ast.Assign(targets=[ast.Name()], value=check_fillable_node)

def type_and_value(node):
    if isinstance(node, ast.Num):
        # int or float
        return type(node.n), node.n
    elif isinstance(node, ast.Str):
        return str, node.s
    elif isinstance(node, ast.NameConstant) and (node.value in (True, False)):
        return (bool, node.value)
    elif isinstance(node, ast.List):
        return (list, [type_and_value(n)[1] for n in node.elts])
    elif isinstance(node, ast.Dict):
        return (dict, {type_and_value(node.keys[i])[1]: type_and_value(node.values[i])[1] for i in range(len(node.keys))})

    raise NotImplementedError()

def extract_definitions(cell):
    cell_ast = ast.parse(cell)
    for assign in astsearch.ASTPatternFinder(definition_pattern).scan_ast(cell_ast):
        yield Parameter(assign.targets[0].id, *type_and_value(assign.value))

def build_definitions(inputs):
    return "\n".join("{0.name} = {0.value!r}".format(i) for i in inputs)