"""
Séquence 1, Séance 2 — Les piles (implémentation avec un tableau et une
position de sommet)
=============================================================================

Ce fichier correspond à la section « Les piles » de la fiche
« Interfaces, implémentation bas niveau et POO ».

Il contient :
  - le code du cours (creer_pile, est_vide_pile) ;
  - le code de l'Exercice 3 (sommet_pile, empiler, depiler) ;
  - les tests correspondants.

Pour l'exécuter : télécharger ce fichier, puis dans un terminal, dans le
même dossier :
    python3 piles_tableau.py

Si tout est correct, le programme affiche "Tests des piles : OK".
"""


LEN_PILE = 10


def creer_pile():
    """Construit une pile vide, de taille fixe LEN_PILE."""
    return {"tableau": ['_'] * LEN_PILE,
            "position_sommet": -1}


def est_vide_pile(pile):
    """Renvoie True si la pile ne contient aucun élément."""
    return pile["position_sommet"] == -1


def sommet_pile(pile):
    """Renvoie l'élément au sommet de la pile, sans le retirer."""
    assert not est_vide_pile(pile), "la pile est vide"
    return pile["tableau"][pile["position_sommet"]]


def empiler(pile, element):
    """Retourne la pile mise à jour après l'empilement d'un élément au sommet."""
    assert pile["position_sommet"] < LEN_PILE - 1, "la pile est pleine"
    pile["tableau"][pile["position_sommet"] + 1] = element
    pile["position_sommet"] += 1
    return pile


def depiler(pile):
    """Retourne la pile mise à jour après le dépilement de son sommet."""
    assert not est_vide_pile(pile), "la pile est vide"
    pile["tableau"][pile["position_sommet"]] = '_'
    pile["position_sommet"] -= 1
    return pile


def test_exercice_3():
    """Exercice 3 : écrire sommet_pile, empiler, depiler."""
    p = creer_pile()
    assert est_vide_pile(p), "une pile tout juste créée doit être vide"

    for assiette in ['a1', 'a2', 'a3', 'a4', 'a5']:
        empiler(p, assiette)
    assert p["position_sommet"] == 4
    assert sommet_pile(p) == 'a5'

    empiler(p, 'a6')
    assert p["position_sommet"] == 5
    assert sommet_pile(p) == 'a6'

    retiree = sommet_pile(p)
    depiler(p)
    assert retiree == 'a6'
    assert p["position_sommet"] == 4
    assert sommet_pile(p) == 'a5'

    # Une pile pleine doit refuser un nouvel empilement
    p_pleine = creer_pile()
    for i in range(LEN_PILE):
        empiler(p_pleine, f"a{i}")
    try:
        empiler(p_pleine, "a_de_trop")
        raise AssertionError("empiler aurait dû lever une erreur sur une pile pleine")
    except AssertionError as erreur:
        assert "pleine" in str(erreur)

    # Une pile vide ne doit pas pouvoir être dépilée
    p_vide = creer_pile()
    try:
        depiler(p_vide)
        raise AssertionError("depiler aurait dû lever une erreur sur une pile vide")
    except AssertionError as erreur:
        assert "vide" in str(erreur)

    print("Exercice 3 : OK")


if __name__ == "__main__":
    test_exercice_3()
    print("Tests des piles : OK")
