"""
Mini-projet — Historique de navigateur avec piles et files (collections.deque)
================================================================================

Ce fichier correspond à la fiche « Piles et files avec deque (mini-projet
historique) ».

Il contient :
  - le code du cours (reculer, avancer, nouvelle_url) ;
  - les tests des deux séquences de l'énoncé ;
  - les tests du cas où back devient vide, et du cas où forward devient vide.

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

Si tout est correct, le programme affiche "Tous les tests de l'historique
de navigateur : OK".
"""

from collections import deque


def reculer(historique):
    """Retourne l'historique après un clic sur 'précédent'.

    La page courante est ajoutée en tête de forward, et le sommet de back
    (la dernière page visitée avant la page courante) devient la nouvelle
    page courante. Si back est vide, l'historique est renvoyé inchangé."""
    if len(historique["back"]) == 0:
        return historique
    historique["forward"].appendleft(historique["current"])
    historique["current"] = historique["back"].pop()
    return historique


def avancer(historique):
    """Retourne l'historique après un clic sur 'suivant'.

    La page courante est ajoutée en queue de back, et le sommet de forward
    (la page la plus proche de la page courante) devient la nouvelle page
    courante. Si forward est vide, l'historique est renvoyé inchangé."""
    if len(historique["forward"]) == 0:
        return historique
    historique["back"].append(historique["current"])
    historique["current"] = historique["forward"].popleft()
    return historique


def nouvelle_url(historique, url):
    """Retourne l'historique après la visite d'une nouvelle page url.

    La page courante est ajoutée en queue de back, url devient la nouvelle
    page courante, et forward est entièrement vidé."""
    historique["back"].append(historique["current"])
    historique["current"] = url
    historique["forward"].clear()
    return historique


def etat_initial():
    """Reconstruit l'historique de l'exemple du cours, à chaque appel
    (pour que chaque test reparte d'un historique neuf)."""
    return {"back": deque(['url_D3', 'url_C1', 'url_A8', 'url_A5']),
            "current": 'url_F',
            "forward": deque(['url_Z', 'url_alpha1'])}


def test_sequence_1():
    """Séquence de l'énoncé : reculer, reculer, nouvelle_url, reculer,
    reculer, avancer."""
    h = etat_initial()
    reculer(h)
    reculer(h)
    nouvelle_url(h, 'url_B2')
    reculer(h)
    reculer(h)
    avancer(h)
    assert list(h["back"]) == ['url_D3', 'url_C1'], "back incorrect après la séquence 1"
    assert h["current"] == 'url_A8', "current incorrect après la séquence 1"
    assert list(h["forward"]) == ['url_B2'], "forward incorrect après la séquence 1"
    print("Séquence 1 : OK")


def test_sequence_2():
    """Séquence de l'énoncé : avancer, avancer, reculer, nouvelle_url,
    reculer."""
    h = etat_initial()
    avancer(h)
    avancer(h)
    reculer(h)
    nouvelle_url(h, 'url_G7')
    reculer(h)
    assert list(h["back"]) == ['url_D3', 'url_C1', 'url_A8', 'url_A5', 'url_F'], \
        "back incorrect après la séquence 2"
    assert h["current"] == 'url_Z', "current incorrect après la séquence 2"
    assert list(h["forward"]) == ['url_G7'], "forward incorrect après la séquence 2"
    print("Séquence 2 : OK")


def test_reculer_quand_back_devient_vide():
    """back ne contient que 4 éléments : il devient vide après 4 appels à
    reculer, et un 5e appel ne doit rien changer."""
    h = etat_initial()
    for _ in range(4):
        reculer(h)
    assert len(h["back"]) == 0, "back devrait être vide après 4 appels à reculer"
    assert h["current"] == 'url_D3', "current incorrect une fois back vide"

    # Un 5e appel, sur back déjà vide, ne doit rien changer
    forward_avant = list(h["forward"])
    reculer(h)
    assert len(h["back"]) == 0, "back devrait rester vide"
    assert h["current"] == 'url_D3', "current ne doit pas changer quand back est vide"
    assert list(h["forward"]) == forward_avant, "forward ne doit pas changer quand back est vide"
    print("reculer avec back vide : OK")


def test_avancer_quand_forward_devient_vide():
    """forward ne contient que 2 éléments : il devient vide après 2 appels
    à avancer, et un 3e appel ne doit rien changer."""
    h = etat_initial()
    for _ in range(2):
        avancer(h)
    assert len(h["forward"]) == 0, "forward devrait être vide après 2 appels à avancer"
    assert h["current"] == 'url_alpha1', "current incorrect une fois forward vide"

    # Un 3e appel, sur forward déjà vide, ne doit rien changer
    back_avant = list(h["back"])
    avancer(h)
    assert len(h["forward"]) == 0, "forward devrait rester vide"
    assert h["current"] == 'url_alpha1', "current ne doit pas changer quand forward est vide"
    assert list(h["back"]) == back_avant, "back ne doit pas changer quand forward est vide"
    print("avancer avec forward vide : OK")


if __name__ == "__main__":
    test_sequence_1()
    test_sequence_2()
    test_reculer_quand_back_devient_vide()
    test_avancer_quand_forward_devient_vide()
    print("Tous les tests de l'historique de navigateur : OK")
