#include <jni.h>
#include <eccw3c.h>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* ecc stuff*/
#include "bigf.h"
#include "global.h"
#include "glue.h"

extern int strlen();

extern gfparm gf;

void printKeyPair(Keypair);
void printSig(Signature);
void hash_to_string(char *, char *);
void sprint_densel(char *cp, densel el);
void sprint_subfarray_as_dense(char *cp, subfarray x);
void sprint_gfpoint(char *pubx, char *puby, gfpoint gf);
densel string_to_densel(char*);
int string_to_densel2(char*, densel *el);
gfpoint strings_to_gfpoint(char * x, char * y);


/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_init
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_w3c_www_dsig_eccDsig_ecc_1init
  (JNIEnv *env, jobject obj)
{
	glue_init();
}

/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_getSHA1
 * Signature: ([B)Lw3c/www/dsig/Sha1Hash;
 */
JNIEXPORT jobject JNICALL Java_w3c_www_dsig_eccDsig_ecc_1getSHA1
  (JNIEnv *env, jobject obj, jbyteArray hdata)
{
	unsigned char digest[20];

	int len;
	jbyte *hash_data;
	jbyte hash[32];
	jobject ret;
	jbyteArray bah;
	jclass clsSha1Hash;
	jmethodID ctorSha1Hash;

	// get the hash data into a char[]
	len = (*env)->GetArrayLength(env, hdata);
	hash_data = (*env)->GetByteArrayElements(env, hdata, 0);

	/* create digest from hash_data */
	/* note: not calling through glue layer, but direct to ecc base code */
	sha1fxn_buffer(hash_data, len, digest);

	hash_to_string(digest,(char*)hash);

	/* load the needed class into the JVM and get jclass vars for it*/
	clsSha1Hash = (*env)->FindClass(env,"Sha1Hash");

	// get the method id of the ctor
	// specify <init> as name and V (void) as return type to get ctor id
	// Sha1Hash constructor takes byte[], type signature is [B
	ctorSha1Hash = (*env)->GetMethodID(env,clsSha1Hash,
                                             "<init>",
                                             "([B)V");

	//allocate storage for the byte array
	bah = (*env)->NewByteArray(env,20);

	//stuff the bytes into the array
	(*env)->SetByteArrayRegion(env, bah, 0, 20, hash);

	ret = (*env)->NewObject(env, clsSha1Hash, ctorSha1Hash, bah);

	return (ret);
}


/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_genKeyPair
 * Signature: ()Lw3c/www/dsig/EccKeyPair;
 */
JNIEXPORT jobject JNICALL Java_w3c_www_dsig_eccDsig_ecc_1genKeyPair
  (JNIEnv *env, jobject obj)
{
	Keypair kp;
	jobject ret;
	jobject kptmp, ptmp;
	jbyte priv[64];
	jbyte pubx[64];
	jbyte puby[64];
	jbyteArray bapriv, bax, bay;
	jclass clsEccPubKey, clsEccPrivateKey, clsEccKeyPair;
	jmethodID ctorEccPubKey, ctorEccPrivateKey, ctorEccKeyPair;

	glue_genKeyPair(&kp); /*call the base implementation */

	sprint_gfpoint((char*)pubx,(char*)puby,kp.public_key);
	sprint_densel((char*)priv,kp.private_key);

	/* load the needed classes into the JVM and get jclass vars for them*/
    clsEccKeyPair = (*env)->FindClass(env,"w3c/www/dsig/EccKeyPair");
	clsEccPubKey = (*env)->FindClass(env,"w3c/www/dsig/EccPubKey");
	clsEccPrivateKey = (*env)->FindClass(env,"w3c/www/dsig/EccPrivateKey");

	// get the method id's of the ctor
	// specify <init> as name and V (void) as return type to get ctor id

	// EccKeyPair ctor takes EccPrivateKey and EccPubKey: type signature is 
	// LEccPrivateKeyLEccPubKey;
	ctorEccKeyPair = (*env)->GetMethodID(env,clsEccKeyPair,
                                             "<init>",
                                             "(Lw3c/www/dsig/EccPrivateKey;Lw3c/www/dsig/EccPubKey;)V");

	// EccPubKey ctor takes 2 byte arrays: JavaVM type signature is [B[B
    ctorEccPubKey = (*env)->GetMethodID(env,clsEccPubKey,
                                             "<init>",
                                             "([B[B)V");

	// EccPrivateKey ctor takes 1 byte arrays: JavaVM type signature is [B
	ctorEccPrivateKey = (*env)->GetMethodID(env,clsEccPrivateKey,
                                             "<init>",
                                             "([B)V");

	//allocate byte arrays for priv, x and y
	bapriv = (*env)->NewByteArray(env,39);
	bax = (*env)->NewByteArray(env,39);
	bay = (*env)->NewByteArray(env,39);

	//stuff the bytes into the arrays
	(*env)->SetByteArrayRegion(env, bapriv, 0, 39, priv);
	(*env)->SetByteArrayRegion(env, bax, 0, 39, pubx);
	(*env)->SetByteArrayRegion(env, bay, 0, 39, puby);

	//make a public key of bax and bay
	kptmp = (*env)->NewObject(env, clsEccPubKey, ctorEccPubKey, bax, bay);

	//make a private key of bapriv
	ptmp = (*env)->NewObject(env, clsEccPrivateKey, ctorEccPrivateKey, bapriv);

	ret = (*env)->NewObject(env, clsEccKeyPair, ctorEccKeyPair, ptmp, kptmp);

	return (ret);
}

/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_Sign
 * Signature: ([B[B)Lw3c/www/dsig/ecSig;
 */
JNIEXPORT jobject JNICALL Java_w3c_www_dsig_eccDsig_ecc_1Sign
  (JNIEnv * env, jobject jo, jbyteArray sdata, jbyteArray privkey)
{
	int len;
	jbyte *sign_data, *privkey_data;
	Signature signature;
	densel private_key;
	jobject ret;
	jbyte rbytes[64];
	jbyte sbytes[64];
	jbyteArray bar, bas;
	jclass clsecSig;
	jmethodID ctorecSig;

	// get the sign data into a char[]
	len = (*env)->GetArrayLength(env, sdata);
	sign_data = (*env)->GetByteArrayElements(env, sdata, 0);

	// convert the privkey bytearray into a densel
	privkey_data = (*env)->GetByteArrayElements(env, privkey, 0);
	private_key = string_to_densel(privkey_data);

	glue_sign(sign_data, len, private_key, &signature);

	//return an ecSig object (cloned from getSig)
	//get the r and s values from the signature into bytearrays

	//r
	sprint_densel((char*)rbytes,signature.r);
	bar = (*env)->NewByteArray(env,39);
	(*env)->SetByteArrayRegion(env, bar, 0, 39, rbytes);

	//s
	sprint_densel((char*)sbytes,signature.s);	
	bas = (*env)->NewByteArray(env,39);
	(*env)->SetByteArrayRegion(env, bas, 0, 39, sbytes);

	// load the needed class into the JVM and get jclass var for it
    clsecSig = (*env)->FindClass(env,"ecSig");

	// get the method id's of the ctor
	// specify <init> as name and V (void) as return type to get ctor id
	// EccPubKey ctor takes 2 byte arrays: JavaVM type signature is [B[B
    ctorecSig = (*env)->GetMethodID(env,clsecSig,
                                             "<init>",
                                             "([B[B)V");

	ret = (*env)->NewObject(env, clsecSig, ctorecSig, bar, bas);

	return (ret);  //return the ecSig signature object

}

/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_Verify
 * Signature: (Lw3c/www/dsig/ecSig;[BLw3c/www/dsig/EccPubKey;)Z
 */
JNIEXPORT jboolean JNICALL Java_w3c_www_dsig_eccDsig_ecc_1Verify
  (JNIEnv * env, jobject jo, jobject sig, jbyteArray sdata, jobject pubk)
{
	jboolean sv;
	int len;
	jbyte *r_data, *s_data, *x_data, *y_data, *sign_data;
	Signature signature;
	gfpoint public_key;
	jclass clsEccPubKey, clsecSig;
	jfieldID field_r, field_s, field_x, field_y;
	jbyteArray sig_r, sig_s, jx, jy;

	len = (*env)->GetArrayLength(env, sdata);
	sign_data = (*env)->GetByteArrayElements(env, sdata, 0);

	//convert ecSig object to a Signature struct
	clsecSig = (*env)->FindClass(env,"ecSig");

	//r
	field_r = (*env)->GetFieldID(env, clsecSig, "r", "[B");
	sig_r = (jbyteArray)(*env)->GetObjectField(env, sig, field_r);
	r_data = (*env)->GetByteArrayElements(env, sig_r, 0);
	signature.r = new_densenum(gf.n);
	string_to_densel2(r_data, &(signature.r));

	//s
	field_s = (*env)->GetFieldID(env, clsecSig, "s", "[B");
	sig_s = (jbyteArray)(*env)->GetObjectField(env, sig, field_s);
	s_data = (*env)->GetByteArrayElements(env, sig_s, 0);
	signature.s = new_densenum(gf.n);
	string_to_densel2(s_data, &(signature.s));

	//convert java public key object into a EccPubKey struct
	clsEccPubKey = (*env)->FindClass(env,"EccPubKey");

	//x
	field_x = (*env)->GetFieldID(env, clsEccPubKey, "x", "[B");
	jx = (jbyteArray)(*env)->GetObjectField(env, pubk, field_x);
	x_data = (*env)->GetByteArrayElements(env, jx, 0);

	//y
	field_y = (*env)->GetFieldID(env,clsEccPubKey,"y", "[B");
	jy = (jbyteArray)(*env)->GetObjectField(env, pubk, field_y);
	y_data = (*env)->GetByteArrayElements(env, jy, 0);

	public_key = strings_to_gfpoint(x_data,y_data);
	sv = (jboolean)glue_verify(signature,sign_data,len,public_key);

	return (sv);
}


/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_genKeyPair_handle
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_w3c_www_dsig_eccDsig_ecc_1genKeyPair_1handle
  (JNIEnv *env, jobject obj)
{
	Keypair *keypair;
	keypair = (Keypair*)malloc(sizeof(Keypair));
	glue_genKeyPair(keypair);
	return ((jint)keypair);
}

/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_getPriv_with_handle
 * Signature: (I)[B
 */
JNIEXPORT jbyteArray JNICALL Java_w3c_www_dsig_eccDsig_ecc_1getPriv_1with_1handle
  (JNIEnv *env, jobject jo, jint kpint)
{
    jbyteArray ret;
	char privstr[64];
	Keypair *kp;
	kp = (Keypair*)kpint;

	sprint_densel((char*)privstr,kp->private_key);
	ret = (*env)->NewByteArray(env,39);

	(*env)->SetByteArrayRegion(env, ret, 0, 39, (char*)privstr);
	return ret;
}


/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_getPub_with_handle
 * Signature: (I)Lw3c/www/dsig/EccPubKey;
 */
JNIEXPORT jobject JNICALL Java_w3c_www_dsig_eccDsig_ecc_1getPub_1with_1handle
  (JNIEnv *env, jobject jo, jint kpint)
{
	jobject ret;
	jbyte pubx[64];
	jbyte puby[64];
	jbyteArray bax, bay;
	Keypair *kp;
	jclass clsEccPubKey;
	jmethodID ctorEccPubKey;

	//get the x and y of the pubkey into byte form
	kp = (Keypair*)kpint;
	sprint_gfpoint((char*)pubx,(char*)puby,kp->public_key);

	// load the needed class into the JVM and get jclass var for it
    clsEccPubKey = (*env)->FindClass(env,"EccPubKey");

	// get the method id's of the ctor
	// specify <init> as name and V (void) as return type to get ctor id
	// EccPubKey ctor takes 2 byte arrays: JavaVM type signature is [B[B
    ctorEccPubKey = (*env)->GetMethodID(env,clsEccPubKey,
                                             "<init>",
                                             "([B[B)V");
	//allocate byte arrays for x and y
	bax = (*env)->NewByteArray(env,39);
	bay = (*env)->NewByteArray(env,39);

	//stuff the bytes into the array
	(*env)->SetByteArrayRegion(env, bax, 0, 39, pubx);
	(*env)->SetByteArrayRegion(env, bay, 0, 39, puby);

	//Call the constructor
	ret = (*env)->NewObject(env, clsEccPubKey, ctorEccPubKey, bax, bay);
	return (ret);
}


/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_getKeyPair_with_handle
 * Signature: (I)Lw3c/www/dsig/EccKeyPair;
 */
JNIEXPORT jobject JNICALL Java_w3c_www_dsig_eccDsig_ecc_1getKeyPair_1with_1handle
  (JNIEnv * env, jobject jo, jint kpint)
{
	jobject ret;
	jobject kptmp;
	jbyte priv[64];
	jbyte pubx[64];
	jbyte puby[64];
	jbyteArray bapriv, bax, bay;
	Keypair *kp;
	jclass clsEccPubKey, clsKeyPair;
	jmethodID ctorEccPubKey, ctorKeyPair;

	//overall plan:
	//build a pubkey object
	//pass pubkey and privkey (which is a byte[]) to keypair constructor

	//get the public and private key values into byte form
	kp = (Keypair*)kpint;
	sprint_gfpoint((char*)pubx,(char*)puby,kp->public_key);
	sprint_densel((char*)priv,kp->private_key);

	// load the needed classes into the JVM and get jclass vars for them
    clsKeyPair = (*env)->FindClass(env,"KeyPair");
	clsEccPubKey = (*env)->FindClass(env,"EccPubKey");

	// get the method id's of the ctor
	// specify <init> as name and V (void) as return type to get ctor id

	// EccKeyPair ctor takes byte[] and PubKey: type signature is [BLPubKey;
	ctorKeyPair = (*env)->GetMethodID(env,clsKeyPair,
                                             "<init>",
                                             "([BLEccPubKey;)V");
	// EccPubKey ctor takes 2 byte arrays: JavaVM type signature is [B[B
    ctorEccPubKey = (*env)->GetMethodID(env,clsEccPubKey,
                                             "<init>",
                                             "([B[B)V");

	//allocate byte arrays for x and y
	bapriv = (*env)->NewByteArray(env,39);
	bax = (*env)->NewByteArray(env,39);
	bay = (*env)->NewByteArray(env,39);

	//stuff the bytes into the array
	(*env)->SetByteArrayRegion(env, bapriv, 0, 39, priv);
	(*env)->SetByteArrayRegion(env, bax, 0, 39, pubx);
	(*env)->SetByteArrayRegion(env, bay, 0, 39, puby);

	kptmp = (*env)->NewObject(env, clsEccPubKey, ctorEccPubKey, bax, bay);

	ret = (*env)->NewObject(env, clsKeyPair, ctorKeyPair, bapriv, kptmp);

	return (ret);
}

/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_Sign_with_handle
 * Signature: ([BI)I
 */
JNIEXPORT jint JNICALL Java_w3c_www_dsig_eccDsig_ecc_1Sign_1with_1handle
  (JNIEnv * env, jobject jo, jbyteArray sdata, jint kpint)
{
	int len;
	jbyte *sign_data;
	Keypair *kp;
	Signature *signature;

	len = (*env)->GetArrayLength(env, sdata);
	sign_data = (*env)->GetByteArrayElements(env, sdata, 0);

	kp = (Keypair*)kpint;
	signature = (Signature*)malloc(sizeof(Signature));

	glue_sign(sign_data, len, kp->private_key, signature);

	return ((jint)signature);	
}

/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_getSig_with_handle
 * Signature: (I)Lw3c/www/dsig/ecSig;
 */
JNIEXPORT jobject JNICALL Java_w3c_www_dsig_eccDsig_ecc_1getSig_1with_1handle
  (JNIEnv * env, jobject jo, jint sigint)
{
	jobject ret;
	jbyte rbytes[64];
	jbyte sbytes[64];
	jbyteArray bar, bas;
	Signature *sig;
	jclass clsecSig;
	jmethodID ctorecSig;


	//sigint contains the address of a Signature 
	//created in glue_sign() and passed back to java.
	sig = (Signature*)sigint;  

	//get the r and s values from the signature into bytearrays
	//r
	sprint_densel((char*)rbytes,sig->r);
	bar = (*env)->NewByteArray(env,40);
	(*env)->SetByteArrayRegion(env, bar, 0, 40, rbytes);

	//s
	sprint_densel((char*)sbytes,sig->s);	
	bas = (*env)->NewByteArray(env,40);
	(*env)->SetByteArrayRegion(env, bas, 0, 40, sbytes);

	// load the needed class into the JVM and get jclass var for it
    clsecSig = (*env)->FindClass(env,"ecSig");

	// get the method id's of the ctor
	// specify <init> as name and V (void) as return type to get ctor id
	// EccPubKey ctor takes 2 byte arrays: JavaVM type signature is [B[B
    ctorecSig = (*env)->GetMethodID(env,clsecSig,
                                             "<init>",
                                             "([B[B)V");

	ret = (*env)->NewObject(env, clsecSig, ctorecSig, bar, bas);

	return (ret);
}


/*
 * Class:     w3c_www_dsig_eccDsig
 * Method:    ecc_Verify_with_handle
 * Signature: (I[BI)Z
 */
JNIEXPORT jboolean JNICALL Java_w3c_www_dsig_eccDsig_ecc_1Verify_1with_1handle
  (JNIEnv * env, jobject jo, jint sigint, jbyteArray sdata, jint kpint)
{
	jboolean sv;
	int len;
	jbyte *sign_data;
	Signature *sig;
	Keypair *kp;

	len = (*env)->GetArrayLength(env, sdata);
	sign_data = (*env)->GetByteArrayElements(env, sdata, 0);

	sig = (Signature*)sigint;
	kp = (Keypair*)kpint;
	sv = (jboolean)glue_verify(*sig,sign_data,len,kp->public_key);

	return (sv);
}

/////////////////////////////////////////////////
//Utililty Functions
/////////////////////////////////////////////////
void sprint_gfpoint(char *pubx, char *puby, gfpoint gf)
{
	sprint_subfarray_as_dense(pubx, gf.x);
    sprint_subfarray_as_dense(puby, gf.y);
}

	
void printKeyPair(Keypair kp) {
	printf(">>> Private key: (a random number)\n");
	fprint_densel(stdout,"d: ",kp.private_key,"\n");
	printf(">>> Public key: (a point on the curve)\n");
	fprint_point_compact(stdout,kp.public_key);
	printf("\n");
}

void printSig(Signature sig) {
	printf(">>> Signature:\n");
	fprint_densel(stdout,"r: ",sig.r,"\n");
	fprint_densel(stdout,"s: ",sig.s,"\n");
	printf("\n");
}

