import numpy as np
from scipy.spatial import ConvexHull


def convex_hull(x, y):
    points = np.column_stack((x, y))
    hull = ConvexHull(points)
    hull_points = points[hull.vertices]
    return hull_points[:, 0], hull_points[:, 1]


def upper_convex_hull(x, y):
    points = sorted(zip(x, y))

    # Build the upper hull
    upper = []
    for p in points:
        while len(upper) >= 2:
            (x1, y1), (x2, y2) = upper[-2], upper[-1]
            cross = (x2 - x1) * (p[1] - y1) - (y2 - y1) * (p[0] - x1)
            if cross >= 0:
                upper.pop()
            else:
                break
        upper.append(p)

    upper = np.array(upper)
    return upper[:, 0], upper[:, 1]  # Return X and Y of upper hull


def _cvx(min, max, lambd):
    return (1 - lambd) * min + lambd * max


def decorate(ax):
    """Setup the Jaccard-RMSE plot."""
    ax.set_title("Jaccard - RMSE")
    ax.set_xlabel("RMSE")
    ax.set_ylabel("Jaccard")

    x_min, x_max = ax.get_xlim()
    y_min, y_max = ax.get_ylim()

    # flip y axis
    x_min, x_max = x_max, x_min

    x_0 = _cvx(x_min, x_max, lambd=0.05)
    y_0 = _cvx(y_min, y_max, lambd=0.05)
    x_1 = _cvx(x_min, x_max, lambd=0.35)
    y_1 = _cvx(y_min, y_max, lambd=0.35)

    ax.annotate(
        "",
        xy=(x_1, y_0),
        xytext=(x_0, y_0),
        arrowprops=dict(facecolor="black", arrowstyle="->"),
    )

    ax.annotate(
        "",
        xy=(x_0, y_1),
        xytext=(x_0, y_0),
        arrowprops=dict(facecolor="black", arrowstyle="->"),
    )

    ax.text(
        _cvx(x_0, x_1, lambd=0.5),
        _cvx(y_min, y_0, lambd=0.5),
        "better",
        ha="center",
        va="center",
        fontsize=10,
    )
    ax.text(
        _cvx(x_min, x_0, lambd=0.5),
        _cvx(y_0, y_1, lambd=0.5),
        "better",
        ha="center",
        va="center",
        rotation=90,
        fontsize=10,
    )


def scatter(jac, rmse, ax):
    ax.scatter(rmse, jac, marker="x", color="red", zorder=1)


def plot(jac, rmse, ax, cvx_hull=False, **kwargs):
    """Add a Jaccard-RMSE curve to the plot."""
    if cvx_hull:
        rmse_sorted, jac_sorted = upper_convex_hull(rmse, jac)
    else:
        sorted_indices = np.argsort(rmse)
        jac_sorted = jac[sorted_indices]
        rmse_sorted = rmse[sorted_indices]

    ax.plot(rmse_sorted, jac_sorted, **kwargs)
    ax.legend()
