# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hooks for ctypes module.

Inside the ctypes module, the value class is defined inside
the C coded module _ctypes.
Thus astroid doesn't know that the value member is a builtin type
among float, int, bytes or str.
"""

import sys

from astroid import nodes
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def enrich_ctypes_redefined_types() -> nodes.Module:
    """
    For each ctypes redefined types, overload 'value' and '_type_' members
    definition.

    Overloading 'value' is mandatory otherwise astroid cannot infer the correct type for it.
    Overloading '_type_' is necessary because the class definition made here replaces the original
    one, in which '_type_' member is defined. Luckily those original class definitions are very short
    and contain only the '_type_' member definition.
    """
    c_class_to_type = (
        ("c_byte", "int", "b"),
        ("c_char", "bytes", "c"),
        ("c_double", "float", "d"),
        ("c_float", "float", "f"),
        ("c_int", "int", "i"),
        ("c_int16", "int", "h"),
        ("c_int32", "int", "i"),
        ("c_int64", "int", "l"),
        ("c_int8", "int", "b"),
        ("c_long", "int", "l"),
        ("c_longdouble", "float", "g"),
        ("c_longlong", "int", "l"),
        ("c_short", "int", "h"),
        ("c_size_t", "int", "L"),
        ("c_ssize_t", "int", "l"),
        ("c_ubyte", "int", "B"),
        ("c_uint", "int", "I"),
        ("c_uint16", "int", "H"),
        ("c_uint32", "int", "I"),
        ("c_uint64", "int", "L"),
        ("c_uint8", "int", "B"),
        ("c_ulong", "int", "L"),
        ("c_ulonglong", "int", "L"),
        ("c_ushort", "int", "H"),
        ("c_wchar", "str", "u"),
    )

    src = ["""
from _ctypes import _SimpleCData

class c_bool(_SimpleCData):
    def __init__(self, value):
        self.value = True
        self._type_ = '?'
    """]

    for c_type, builtin_type, type_code in c_class_to_type:
        src.append(f"""
class {c_type}(_SimpleCData):
    def __init__(self, value):
        self.value = {builtin_type}(value)
        self._type_ = '{type_code}'
        """)

    return parse("\n".join(src))


def register(manager: AstroidManager) -> None:
    if not hasattr(sys, "pypy_version_info"):
        # No need of this module in pypy where everything is written in python
        register_module_extender(manager, "ctypes", enrich_ctypes_redefined_types)
