"""
Lint Utilities
==============

Provides some help to pylint so static analysis can be
made aware of some constants and functions that we define
in headers.
"""

import re
from functools import partial
from os.path import dirname, abspath, join

try:
    from astroid import MANAGER, scoped_nodes

# The astroid is only needed by pylint.  We ignore ImportError
# here because the functions below are not called directory and
# we don't want this to be an extra dependency for the whole
# project.
except ImportError:
    pass

HEADERS_DIR = join(
    dirname(dirname(abspath(__file__))), "core", "cdefs", "headers")
CONSTANTS_HEADER = join(HEADERS_DIR, "constants.h")
FUNCTIONS_HEADER = join(HEADERS_DIR, "functions.h")
REGEX_FUNCTION = re.compile("^[A-Z]+ ([A-Z][a-z]*[A-Z].*)[(].*$")
REGEX_CONSTANT = re.compile("^#define ([A-Z]*[_]*[A-Z]*[_]*[A-Z]*) ...$")


def transform(cls, constants=None, functions=None):
    """
    Transforms class objects from pylint so they're aware of extra
    attributes that are not present when being statically analyzed.
    """
    assert isinstance(constants, set)
    assert isinstance(functions, set)

    for value in constants:
        cls.locals[value] = [scoped_nodes.Class(value, None)]

    for value in functions:
        cls.locals[value] = [scoped_nodes.Function(value, None)]


def register(linter):  # pylint: disable=unused-argument
    """
    An entrypoint that pylint uses to search for and register
    plugins with the given ``linter``
    """
    # Load constants
    constants = set()
    with open(CONSTANTS_HEADER, "r") as constants_file:
        for line in constants_file:
            match = REGEX_CONSTANT.match(line)
            if match:
                constants.add(match.group(1))

    # Load functions
    functions = set()
    with open(FUNCTIONS_HEADER, "r") as functions_file:
        for line in functions_file:
            # special case that does not match the regex
            if line.startswith("HANDLE handle_from_fd(int);"):
                functions.add("handle_from_fd")
                continue

            match = REGEX_FUNCTION.match(line)
            if match:
                functions.add(match.group(1))

    # TODO: needs to change once we FFILibrary returns the prebuilt lib
    MANAGER.register_transform(
        scoped_nodes.Class,
        partial(transform, constants=constants, functions=functions),
        predicate=lambda node: node.name == "FFILibrary")

@@ -612,16 +612,105 @@
 aders%22)%0A
+SOURCES_DIR = join(%0A    dirname(dirname(abspath(__file__))), %22core%22, %22cdefs%22, %22sources%22)%0A
 CONSTANT
@@ -805,16 +805,58 @@
 ons.h%22)%0A
+SOURCE_MAIN = join(SOURCES_DIR, %22main.c%22)%0A
 REGEX_FU
@@ -889,30 +889,13 @@
 %5D+ (
-%5BA-Z%5D%5Ba-z%5D*%5BA-Z%5D.*)%5B(%5D
+.*)%5C(
 .*$%22
@@ -1615,29 +1615,8 @@
 %22%22%22%0A
-    # Load constants%0A
     
@@ -1625,32 +1625,88 @@
 nstants = set()%0A
+    functions = set()%0A%0A    # Load constants from header%0A
     with open(CO
@@ -1917,38 +1917,28 @@
 unctions
-%0A    functions = set()
+ from header
 %0A    wit
@@ -2038,189 +2038,235 @@
     
-# special case that does not match the regex%0A            if line.startswith(%22HANDLE handle_from_fd(int);%22):%0A                functions.add(%22handle_from_fd%22)%0A                continue%0A
+match = REGEX_FUNCTION.match(line)%0A            if match:%0A                functions.add(match.group(1))%0A%0A    # Load functions from source%0A    with open(SOURCE_MAIN, %22r%22) as source_main_file:%0A        for line in source_main_file:
 %0A   

fix function naming, don't special case handle_from_fd, fix regex