# Rohan Sangani
# 11/3/2025
# Simple Example Illustrating Elliptic Curve Cryptography
# Adapted from https://www.cs.ucf.edu/~dmarino/ucf/cis3362/progs/ECC.java
import random

from EllipticCurve import EllipticCurve
from Point import Point

class ECC:
    def __init__(self, c, x, y, n_a):
        self.curve = c
        self.generator = Point(c, x, y)
        self.private_key = n_a
        self.public_key = self.generator * self.private_key

    # Encrypts a single point, returning a list of points [c1,c2] representing the ciphertext
    def encrypt(self, plain):
        bits = self.curve.p.bit_length() # same as this.curve.getP().bitLength()
        k = random.getrandbits(bits) # same as new BigInteger(bits, new Random())
        print(f"Picked {k=} for encryption")

        # return both parts of the cipher text
        ans = [self.generator * k, plain + (self.public_key * k)]
        return ans

    # Similar to El-Gamal
    def decrypt(self, cipher):
        sub = cipher[0] * self.private_key

        print(f"Decryption result is: {cipher[1]} - {sub}")
        return cipher[1] - sub

    # toString()
    def __str__(self):
        return (f"Generator: {self.generator}\n"
                f"Private Key: {self.private_key}\n"
                f"Public Key: {self.public_key}")


def main():
    curve = EllipticCurve(23, 1, 1) # 23 is prime, a = b = 1
    x = 6
    y = 19
    n_a = 10
    alice = ECC(curve, x, y, n_a)

    # Points starts with just the origin, then repeatedly adds (3, 13)
    points = [Point(curve, 0, 0)]
    temp = Point(curve, 3, 13)
    for i in range (1, 28):
        points.append(points[i-1] + temp)

    # For each of those points, we can encrypt and decrypt it
    for plain in points:
        print(f"Encrypting {plain}")

        cipher = alice.encrypt(plain)
        print(f"Cipher first part: {cipher[0]}")
        print(f"Cipher second part: {cipher[1]}")

        recover = alice.decrypt(cipher)
        print(f"Recovered: {recover}")

if __name__ == '__main__':
    main()
