import numpy as np
import matplotlib.pyplot as plt
import numpy.random as npr

longueur_aiguille = 1

def tracer_parquet(nbRainures, largeur, longueur):
    """
    Dessine le systeme des coordonnes et les rainures.
    Renvoie les objets axe et fig.
    """
    fig = plt.figure(figsize=(9, 6))
    axe = fig.add_subplot(1, 1, 1, aspect="equal")
    axe.axis('off')

    # les axes de coordonnees
    Oxs = [-longueur/2 - 1, longueur/2 + 1]
    Oys = [-nbRainures/2 * largeur - .4, nbRainures/2 * largeur + .4]
    axe.plot(Oxs, [0, 0], c='r', lw=.3)
    axe.plot([0, 0], Oys, c='r', lw=.3)

    # les rainures
    for k in range(nbRainures):
        a = longueur/2
        b = (k - nbRainures//2) * largeur
        axe.plot([-a, a], [b, b], c='k', lw=.5)
    return axe, fig


def aiguille(a, b, theta, longueurA=longueur_aiguille):
    """
    Renvoie les vecteurs des points necessaires pour dessiner l'aiguille
    dont le millieu est (a, b) est l'angle forme avec l'abscisse est
    theta.
    """
    h = 55
    x = np.array([0, 0, longueurA, 0]) - longueurA/2
    y = np.array([-longueurA/h, longueurA/h, 0, -longueurA/h])
    X = np.cos(theta) * x - np.sin(theta) * y + a
    Y = np.sin(theta) * x + np.cos(theta) * y + b    
    return X, Y


def lancer_aiguille(longueurA, nbRainures, largeur, longueur):  # figure, axes):
    """
    Renvoie les x, y et theta aleatoires dans certains intervalles.
    Le calcul est base sur numpy.random.uniform
    """
    x_low = -longueur/2 + longueurA/2
    x_high = -x_low
    y_low = -(nbRainures//2) * largeur
    y_high = (nbRainures - 1 - (nbRainures//2)) * largeur
    x_middle = npr.uniform(low=x_low, high=x_high)
    y_middle = npr.uniform(low=y_low, high=y_high)
    angle = 2 * np.pi * npr.rand()
    return x_middle, y_middle, angle


def experiment_graphique(nbLancees, longueurA, \
                         nbRainures, largeur, longueur, delta_t=.1):
    axe, fig = tracer_parquet(N, largeur, longueur)
    for k in range(nbLancees):
        lancee = lancer_aiguille(longueurA, N, largeur, longueur)
        X, Y = aiguille(*lancee)
        axe.plot(X, Y, color='b', lw=.8)
        plt.pause(delta_t)


def y_lancee_normalisee(yInitial, largeur):
    """
    Renvoie le y obtenu par translations avec + ou - largeur
    dans [-largeur/2, largeur/2].
    """
    sgn = np.sign(yInitial)
    y = abs(yInitial)
    while y > -largeur/2:
        y -= largeur
    return sgn * (y + largeur)
    

def experiment_graphique_avec_intersection(nbLancees, longueurA, \
                         nbRainures, largeur, longueur, delta_t=.1):
    axe, fig = tracer_parquet(N, largeur, longueur)
    for k in range(nbLancees):
        x, y, theta = lancer_aiguille(longueurA, N, largeur, longueur)
        yNormalise = y_lancee_normalisee(y, largeur)
        if abs(yNormalise) > longueurA/2 * abs(np.sin(theta)):
            color = 'red'
        else:
            color = 'blue'
        X, Y = aiguille(x, y, theta)
        axe.plot(X, Y, color=color, lw=.8)
        # axe.annotate(k, xy=(x, y), ha='center', va='center')
        plt.pause(delta_t)


def experiment(nbLancees, longueurA, largeur):
    """
    Renvoie la liste de lingueur nbLancees avec, sur la position k-1
    le nombre de lancees sur la rainure des k lancees.
    """
    BL = []
    b = 0
    for k in range(nbLancees):
        y = npr.uniform(low=-largeur/2, high=largeur/2)
        theta = npr.uniform(low=0, high=2*np.pi)
        if abs(y) <= longueurA/2 * abs(np.sin(theta)):
            b += 1
        BL.append(b)
    return BL


def limInf_et_limSup_pour_experiment(nbLancees, longueurA, largeur):
    """
    Renvoie les vecteurs (frequence), (frequence_inf) et
    (frequence_sup).

    Ces deux derniers permettent le calcul des limites inf et sup
    pour la frequence (donc donnent une valeur approchee de cette
    limite).

    Les constructions se font en allant jusqu'a nbLancees-100.
    """
    if nbLancees < 110:
        print("Il faut un nombre de lnacees plus grand que 110.")
        return None
    L = np.array(experiment(nbLancees, longueurA, largeur))
    N_vector = np.array([k for k in range(1, nbLancees + 1)])
    F = L/N_vector
    bound = nbLancees - 100
    FInf = []
    FSup = []
    for k in range(bound):
        FInf.append(min(F[k:]))
        FSup.append(max(F[k:]))
    return F, FInf, FSup, bound
    

N = 5
largeur = 1.3
longueur = 6
nbLancees = 50

np.random.seed(2)  # fixe les nombres aleatoires utilises
experiment_graphique_avec_intersection(nbLancees, longueur_aiguille, N, \
                                       largeur, longueur)

N = 5000

F, FInf, FSup, bound = limInf_et_limSup_pour_experiment(N, longueur_aiguille, largeur)
f_lim = (FInf[-1] + FSup[-1])/2
f_lim = int(f_lim*10000)/10000
N_vector = np.array([k for k in range(1, N + 1)])

fig2 = plt.figure(figsize=(10, 4))
axe2 = fig2.add_subplot(1, 1, 1)
titre = f"Frequences des bonnes lancees avec valeur limite ~{f_lim}"
axe2.set_title(titre)

axe2.plot(N_vector[3:], F[3:], c='b', lw=.7, label='f')
axe2.plot(N_vector[3: bound], FInf[3:], c='r', lw=.4, label='f inf')
axe2.plot(N_vector[3: bound], FSup[3:], c='g', lw=.4, label='f sup')

axe2.legend()

plt.show()

