use crate::{
    traits::{Reveal, ToMontgomery},
    utils::{
        elliptic_curve::AffineEdwardsPoint,
        field::{BaseField, ScalarField},
    },
};
use curve25519_dalek_arcium_fork::{
    field::FieldElement,
    EdwardsPoint,
    RistrettoPoint as ArcisRistrettoPoint,
};
use primitives::algebra::elliptic_curve::{Curve25519Ristretto, Point, Scalar};
use std::ops::Mul;

pub trait Curve: Clone + Copy + Reveal {
    fn generator() -> Self;
}

pub type CurvePoint = Point<Curve25519Ristretto>;

impl Mul<CurvePoint> for ScalarField {
    type Output = CurvePoint;

    fn mul(self, rhs: CurvePoint) -> Self::Output {
        rhs * Scalar::<Curve25519Ristretto>::from_le_bytes(&self.to_le_bytes()).unwrap()
    }
}

impl Reveal for CurvePoint {
    fn reveal(self) -> Self {
        self
    }
}

impl ToMontgomery for CurvePoint {
    type Output = BaseField;

    fn to_montgomery(self, _is_expected_non_identity: bool) -> (BaseField, BaseField) {
        // on plaintext points we simply set is_expected_non_identity = true
        AffineEdwardsPoint::from(self).to_montgomery(true)
    }
}

impl Curve for CurvePoint {
    fn generator() -> Self {
        Self::generator()
    }
}

impl From<AffineEdwardsPoint<BaseField>> for CurvePoint {
    fn from(value: AffineEdwardsPoint<BaseField>) -> Self {
        let x = FieldElement::from_bytes(&value.x.to_le_bytes());
        let y = FieldElement::from_bytes(&value.y.to_le_bytes());
        let xy = FieldElement::from_bytes(&(value.x * value.y).to_le_bytes());
        Self::new(ArcisRistrettoPoint::new_unchecked(
            EdwardsPoint::new_unchecked(x, y, FieldElement::ONE, xy),
        ))
    }
}
