from PIL import Image, ImageDraw, ImageFont
import random
import math
from pathlib import Path


def get_font(size):
    """Recherche une police disponible sur Windows ou Linux."""
    candidates = [
        "C:/Windows/Fonts/arial.ttf",
        "C:/Windows/Fonts/calibri.ttf",
        "C:/Windows/Fonts/segoeui.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf",
    ]

    for path in candidates:
        if Path(path).exists():
            return ImageFont.truetype(path, size)

    return ImageFont.load_default()


def generate_email_image(
    email,
    output="email.png",
    seed=None
):
    """
    Génère une image PNG de l'adresse e-mail avec
    des perturbations destinées à compliquer l'OCR.

    Niveau 3 : perturbations fortes.
    """

    if seed is not None:
        random.seed(seed)

    # --------------------------------------------------------
    # PARAMÈTRES DU NIVEAU 3
    # --------------------------------------------------------

    font_size = 46
    rotation = 8.0
    noise = 110
    lines = 8
    spacing = 4
    wobble = 3
    background_noise = 10

    font = get_font(font_size)

    # --------------------------------------------------------
    # CALCUL DE LA TAILLE DE L'IMAGE
    # --------------------------------------------------------

    dummy = Image.new("RGB", (10, 10), "white")
    draw = ImageDraw.Draw(dummy)

    character_widths = []

    for character in email:
        bbox = draw.textbbox(
            (0, 0),
            character,
            font=font
        )

        width = max(1, bbox[2] - bbox[0])
        character_widths.append(width)

    padding = 25

    width = (
        sum(character_widths)
        + spacing * (len(email) - 1)
        + 2 * padding
        + 10
    )

    height = font_size + 2 * padding

    # --------------------------------------------------------
    # FOND LÉGÈREMENT BRUITÉ
    # --------------------------------------------------------

    image = Image.new(
        "RGB",
        (width, height),
        (248, 248, 248)
    )

    pixels = image.load()

    for y in range(height):
        for x in range(width):

            variation = random.randint(
                -background_noise,
                background_noise
            )

            value = max(
                225,
                min(255, 248 + variation)
            )

            pixels[x, y] = (
                value,
                value,
                value
            )

    draw = ImageDraw.Draw(image)

    # --------------------------------------------------------
    # LIGNES PARASITES
    # --------------------------------------------------------

    for _ in range(lines):

        y = random.randint(
            5,
            height - 5
        )

        points = []

        amplitude = random.uniform(
            1,
            3 + 3
        )

        phase = random.uniform(
            0,
            math.pi * 2
        )

        period = random.uniform(
            25,
            60
        )

        for x in range(width):

            yy = (
                y
                + amplitude
                * math.sin(
                    x / period + phase
                )
            )

            points.append(
                (x, yy)
            )

        draw.line(
            points,
            fill=(190, 190, 190),
            width=1
        )

    # --------------------------------------------------------
    # RENDU DES CARACTÈRES UN PAR UN
    # --------------------------------------------------------

    x = padding

    for i, character in enumerate(email):

        # Variation verticale
        y = (
            padding
            - 5
            + random.randint(
                -wobble,
                wobble
            )
        )

        bbox = font.getbbox(character)

        char_width = max(
            20,
            bbox[2] - bbox[0] + 20
        )

        char_height = max(
            60,
            bbox[3] - bbox[1] + 30
        )

        # Image temporaire pour le caractère
        char_image = Image.new(
            "RGBA",
            (char_width, char_height),
            (0, 0, 0, 0)
        )

        char_draw = ImageDraw.Draw(
            char_image
        )

        # Légère variation de couleur
        gray = random.randint(
            15,
            45
        )

        char_draw.text(
            (
                char_width // 2,
                char_height // 2
            ),
            character,
            font=font,
            fill=(
                gray,
                gray,
                gray,
                255
            ),
            anchor="mm"
        )

        # Rotation aléatoire
        angle = random.uniform(
            -rotation,
            rotation
        )

        char_image = char_image.rotate(
            angle,
            resample=Image.Resampling.BICUBIC,
            expand=True
        )

        # Positionnement
        image.paste(
            char_image,
            (
                x - 8,
                y - 8
            ),
            char_image
        )

        x += (
            character_widths[i]
            + spacing
        )

    # --------------------------------------------------------
    # BRUIT SUPPLÉMENTAIRE
    # --------------------------------------------------------

    draw = ImageDraw.Draw(image)

    for _ in range(noise):

        x1 = random.randint(
            0,
            width - 1
        )

        y1 = random.randint(
            0,
            height - 1
        )

        shade = random.randint(
            120,
            210
        )

        # Petits points
        if random.random() < 0.65:

            radius = random.choice(
                [1, 1, 1, 2]
            )

            draw.ellipse(
                (
                    x1 - radius,
                    y1 - radius,
                    x1 + radius,
                    y1 + radius
                ),
                fill=(
                    shade,
                    shade,
                    shade
                )
            )

        # Petits traits
        else:

            x2 = min(
                width - 1,
                x1 + random.randint(
                    3,
                    12
                )
            )

            y2 = max(
                0,
                min(
                    height - 1,
                    y1 + random.randint(
                        -2,
                        2
                    )
                )
            )

            draw.line(
                (
                    x1,
                    y1,
                    x2,
                    y2
                ),
                fill=(
                    shade,
                    shade,
                    shade
                ),
                width=1
            )

    # --------------------------------------------------------
    # BORDURE
    # --------------------------------------------------------

    draw.rectangle(
        (
            0,
            0,
            width - 1,
            height - 1
        ),
        outline=(
            210,
            210,
            210
        ),
        width=1
    )

    # --------------------------------------------------------
    # SAUVEGARDE
    # --------------------------------------------------------

    output_path = Path(output).resolve()

    # On s'assure que le dossier de destination existe bien avant
    # de sauvegarder, sinon PIL renvoie un FileNotFoundError.
    output_path.parent.mkdir(parents=True, exist_ok=True)

    image.save(str(output_path), format="PNG")


# ============================================================
# UTILISATION
# ============================================================

if __name__ == "__main__":

    dossier_programme = Path(__file__).resolve().parent

    fichier_sortie = dossier_programme / "email.png"

    generate_email_image(
        email="courspasquet@mathweb.fr",
        output=str(fichier_sortie)
    )