package org.ourfilesystem.test.db;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Date;

import static org.junit.Assert.*;

import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.junit.Test;
import org.ourfilesystem.db.FileReference;
import org.ourfilesystem.db.LocalFileReference;
import org.ourfilesystem.db.LocalPost;
import org.ourfilesystem.db.Peer;
import org.ourfilesystem.db.Post;
import org.ourfilesystem.db.StorageImpl;
import org.ourfilesystem.security.KeySet;
import org.ourfilesystem.security.PublicKeySetSigned;
import org.ourfilesystem.security.SignedDigest;
import org.ourfilesystem.utilities.BBytes;

public class StorageImplTest {

	public RSAKeyParameters getPublicKey(long val) {
		RSAKeyParameters pub = new RSAKeyParameters(false,                 //boolean isPrivate,            
				BigInteger.valueOf(val),   //java.math.BigInteger modulus, 
				BigInteger.valueOf(val+1));  //java.math.BigInteger exponent 
		return pub;
	}
	
	public RSAPrivateCrtKeyParameters getPrivateKey(long val) {
		RSAPrivateCrtKeyParameters priv = new RSAPrivateCrtKeyParameters(BigInteger.valueOf(val),   //java.math.BigInteger modulus,          
																	 BigInteger.valueOf(val+1),    //java.math.BigInteger publicExponent,   
																	 BigInteger.valueOf(val+2),   //java.math.BigInteger privateExponent,  
																	 BigInteger.valueOf(val+3),     //java.math.BigInteger p,                
																	 BigInteger.valueOf(val+4),     //java.math.BigInteger q,                
																	 BigInteger.valueOf(val+5),    //java.math.BigInteger dP,               
																	 BigInteger.valueOf(val+6),    //java.math.BigInteger dQ,               
																	 BigInteger.valueOf(val+7));   //java.math.BigInteger qInv              
		return priv;
	}
	
	public BBytes getBBytes(long val) {
		byte b[] = new byte[Long.SIZE/Byte.SIZE];
		ByteBuffer buf = ByteBuffer.wrap(b);
		buf.putLong(val);
		return new BBytes(b);
	}
	
	public SignedDigest getSignedDigest(long v) {
		SignedDigest sd = new SignedDigest();
		sd.setDigest(getBBytes(400+v));
		sd.setPeerIdentifier(getBBytes(500+v));
		sd.setSignature(getBBytes(600+v));
		return sd;
	}
	
	public PublicKeySetSigned getPublicKeySetSigned(long v) {
		PublicKeySetSigned pub = new PublicKeySetSigned();
		pub.setPublicEncryptionKey(getPublicKey(v+200));
		pub.setPublicSigningKey(getPublicKey(v+300));
		pub.setSignature(getSignedDigest(v));
		return pub;
	}
	
	public Peer getPeer(long v) {
		Peer p = new Peer();
		p.setLocation("overhere" + v);
		p.setPeerKeysAndIdentity(getPublicKeySetSigned(v));	
		return p;
	}
	
	public Post getPost(long v, BBytes peerid, BBytes fileref, boolean has) {
		Post p = new Post();
		p.setMessage("Message" + v);
		p.setPostNumber(v);
		SignedDigest dig = new SignedDigest();
		dig.setDigest(getBBytes(750+v));
		dig.setPeerIdentifier(peerid);
		dig.setSignature(getBBytes(650+v));
		if (fileref != null) {
			p.setFileReferenceDigest(fileref);
			p.setPosterHasFile(has);
		}
		p.setSignedDigest(dig);
		return p;
	}
	
	public FileReference getFileRef(long id) {
		FileReference ref = new FileReference();
		try {
			File tf = File.createTempFile("blahblah", ".dat");
			FileOutputStream fos = new FileOutputStream(tf);
			PrintWriter pw = new PrintWriter(fos);
			pw.println(id);
			pw.close();
			ref.setFile(tf);
			ref.setUnsignedDigest(getBBytes(id));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return ref;
	}
	
	public LocalPost getLocalPost(long id, BBytes peerid, BBytes fileref, boolean has) {
		LocalPost lp = new LocalPost();
		lp.setPost(getPost(id, peerid, fileref, has));
		lp.setLocalDate(new Date(id));
		return lp;
	}
	
	public LocalFileReference getLocalFileReference(long id) {
		LocalFileReference lf = new LocalFileReference();
		lf.setFileReference(getFileRef(id));
		lf.setLocalDate(new Date(id));
		return lf;
	}
	
	public void deleteDir(String str) {
		File f = new File(str);
		deleteDir(f);
	}
	
	public void deleteDir(File f) {
		if (f.exists()) {
			if (f.isDirectory()) {
				File fl[] = f.listFiles();
				for (int c = 0; c < fl.length; c++) {
					deleteDir(fl[c]);
				}
			}
			f.delete();
		}
	}
	
	public static boolean RSAKeyParametersEquals(Object o0, Object o1) {
		RSAKeyParameters p0 = (RSAKeyParameters)o0;
		RSAKeyParameters p1 = (RSAKeyParameters)o1;
		if (!p0.getExponent().equals(p1.getExponent())) { return false; }
		if (!p0.getModulus().equals(p1.getModulus())) { return false; }
		return true;
	}
	
	public static boolean peerEquals(Peer p0, Peer p1) {
		if (p0.getLocation() != null) {
			if (p1.getLocation() != null) {
				if (!p0.getLocation().equals(p1.getLocation())) { System.out.println(">1"); return false; }
			}
			else {
				System.out.println(">2"); 
				return false;
			}
		}
		else if (p1.getLocation() != null) { System.out.println(">3");  return false; }
		if (!RSAKeyParametersEquals(p0.getPeerKeysAndIdentity().getPublicEncryptionKey(), 
				p1.getPeerKeysAndIdentity().getPublicEncryptionKey())) {
			System.out.println(">4");
			return false;
		}
		if (!RSAKeyParametersEquals(p0.getPeerKeysAndIdentity().getPublicSigningKey(),
				p1.getPeerKeysAndIdentity().getPublicSigningKey())) {
			System.out.println(">5"); 
			return false;
		}
		SignedDigest p0sig = p0.getPeerKeysAndIdentity().getSignature();
		SignedDigest p1sig = p1.getPeerKeysAndIdentity().getSignature();
		if (!p0sig.getPeerIdentifier().equals(p1sig.getPeerIdentifier())) { System.out.println(">6"); return false; }
		if (!p0sig.getDigest().equals(p1sig.getDigest())) { System.out.println(">7"); return false; }
		if (!p0sig.getSignature().equals(p1sig.getSignature())) { System.out.println(">8"); return false; }
		if (p0.getUpdateCount() != p1.getUpdateCount()) { System.out.println(">9"); return false; }
		return true;
	}
	
	@Test
	public void test() {
		
		deleteDir("tmp");
		
		KeySet ks = new KeySet();
		ks.setPrivateEncryptionKey(getPrivateKey(10));
		ks.setPrivateSigningKey(getPrivateKey(100));
		ks.setPublicKeySet(getPublicKeySetSigned(5));
		
		StorageImpl s = new StorageImpl("tmp");
		s.saveMyKeySet(ks);
		
		Peer p = getPeer(10);
		
		s.savePeer(p);
		s.saveMyPeerData(p);
		
		LocalPost lp = getLocalPost(12L, (BBytes)p.getPeerKeysAndIdentity().getSignature().getDigest(), null, false);
		s.savePost(lp);
		
		s.close();
		
		StorageImpl s2 = new StorageImpl("tmp");
		Peer p2 = s2.getPeer(p.getPeerKeysAndIdentity().getSignature().getDigest());
		assertTrue(peerEquals(p, p2));
		
	}
	
}
