from __future__ import annotations
import pathlib
import importlib.util
import io


def load_from_source(file: str, mod_name: str | None = None):
    mod_name = mod_name or f"__source__.{file}"
    spec = importlib.util.spec_from_file_location(mod_name, file)
    if not spec:
        raise IOError(f"{file} not found")
    module = importlib.util.module_from_spec(spec)
    if not spec.loader:
        raise IOError(f"{file} is not a valid python module")
    spec.loader.exec_module(module)
    return module


def create_syntax_error(msg: str, text: str, line: int, offset: int, file: str):
    e = SyntaxError(msg)
    e.lineno = line + 1
    e.filename = file
    e.text = text
    e.offset = offset + 1
    return e


def get_relative_path(file: str | pathlib.Path, rootdir: str | pathlib.Path):
    if isinstance(file, str):
        file = pathlib.Path(file)
    if isinstance(rootdir, str):
        rootdir = pathlib.Path(rootdir)
    project_based_path = str(file.absolute().relative_to(rootdir.absolute()))
    return project_based_path


_cnt_ref = [0]


def global_gensym(name: str):
    """Generate a name that never collides with other names generated by this function."""
    try:
        return f"{name}_{_cnt_ref[0]}"
    finally:
        _cnt_ref[0] += 1


def make_valid_identifier(string: str):
    """Transform a name to a valid Python identifier."""
    buf = io.StringIO()
    for c in string:
        if c in {"_", "."} or c.isidentifier():
            buf.write("c")
        else:
            buf.write("0x{:02x}".format(ord(c)))
    return buf.getvalue()
