package org.ourfilesystem.db;

/*
 *  
    OurFileSystem is a peer2peer file sharing program.
    Copyright (C) 2012  Robert Gass

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

 */

import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.ourfilesystem.core.CoreUserInterface;
import org.ourfilesystem.core.EventInterface;
import org.ourfilesystem.security.CryptoDataBaseInterface;
import org.ourfilesystem.security.KeySet;

public class DataBaseComImpl implements DataBaseComInterface {

	private CryptoDataBaseInterface Security;
	private LinkedList<EventInterface> Event;
	private StorageInterface Store;
	private TimeInterface Time;
	private CoreUserInterface Core;

	public DataBaseComImpl(CoreUserInterface core) {
		Event = new LinkedList<EventInterface>();
		Core = core;
	}
	
	@Override
	public void addEventInterface(EventInterface evt) {
		synchronized (Event) {
			Event.add(evt);
		}
	}

	@Override
	public Peer getPeer(Object digest) {
		return Store.getPeer(digest);
	}

	@Override
	public boolean processPeerPosts(List<Post> posts) {
		boolean ok = false;
		Iterator<Post> i = posts.iterator();
		while (i.hasNext()) {
			Post p = i.next();
			if (p.getSignedDigest() != null) {
				if (p.getSignedDigest().getDigest() != null) {
					Peer peer = Store.getPeer(p.getSignedDigest().getPeerIdentifier());
					if (peer != null) {
						if (peer.getPeerKeysAndIdentity() != null && peer.getPeerKeysAndIdentity().getPublicSigningKey() != null) {
							if (Security.verifyPost(p, peer.getPeerKeysAndIdentity().getPublicSigningKey())) {
								LocalPost lp = new LocalPost();
								lp.setPost(p);
								lp.setLocalDate(Time.getTime());
								Store.savePost(lp);
								Iterator<EventInterface> ie = getEventInterfaces().iterator();
								while (ie.hasNext()) {
									ie.next().newPostReceived(lp);									
								}
								ok = true;
							}												
						}
					}
				}
			}
		}
		return ok;
	}

	
	private LinkedList<EventInterface> getEventInterfaces() {
		LinkedList<EventInterface> te = new LinkedList<EventInterface>();
		synchronized (Event) {
			te.addAll(Event);
		}
		return te;
	}
	
	
	@Override
	public Object processPeerFile(File frs) {
		Object checkdig = Security.digestFile(frs);
		FileReference fr = new FileReference();
		fr.setUnsignedDigest(checkdig);
		fr.setFile(frs);
		LocalFileReference lf = new LocalFileReference();
		lf.setFileReference(fr);
		lf.setLocalDate(Time.getTime());
		Store.saveFile(lf);
		Core.addLocalPost(null, checkdig);
		Iterator<EventInterface> i = getEventInterfaces().iterator();
		while (i.hasNext()) {
			i.next().newFileDownloaded(lf);
		}
		return checkdig;
	}

	@Override
	public void processPeers(List<Peer> peers) {
		Iterator<Peer> i = peers.iterator();
		while (i.hasNext()) {
			boolean doupdate = true;
			Peer p = i.next();
			if (!Security.verifyLocation(p, p.getPeerKeysAndIdentity().getPublicSigningKey())) {
				doupdate = false;
			}
			Peer myp = getMyPeer();
			Peer chk = Store.getPeer(p.getPeerKeysAndIdentity().getSignature().getDigest());
			if (chk != null) {
				if (chk.getUpdateCount() == p.getUpdateCount()) {
					if (!chk.getLocation().equals(p.getLocation())) {
						//TODO: This needs to give an error or warning or something.
					}
				}
				if (chk.getUpdateCount() > p.getUpdateCount()) {
					doupdate = false;
				}
				else {
					/*
					 * We check our own ID to see if we're still self signed.
					 * If we're still self signed then we save a copy of our peer
					 * data that has been signed by another peer (assuming we
					 * have already signed them).
					 */
					if (myp.equals(p)) {
						if (!myp.getPeerKeysAndIdentity().getSignature().getDigest().equals(
								myp.getPeerKeysAndIdentity().getSignature().getPeerIdentifier())) {
							//Ok, we already have a copy that has been signed by someone else.
							//so we don't want to save this new copy.
							doupdate = false;
						}						
					}
					else {
						/*
						 * Ok, we must keep the original signature chaining of peers as we 
						 * save them.  Otherwise we could destroy our chain or create loops.
						 * So we only save the updated location data.
						 */
						p.setPeerKeysAndIdentity(chk.getPeerKeysAndIdentity());
					}
				}
			}
			if (doupdate) {
				if (DataBaseTools.verifyPeerSignature(p, Store, Security)) {
					if (myp.equals(p)) {
						Store.saveMyPeerData(p);
					}
					Store.savePeer(p);
					Iterator<EventInterface> ie = getEventInterfaces().iterator();
					while (ie.hasNext()) {
						ie.next().newPeerReceived(p);
					}
				}
			}
		}
	}

	@Override
	public List<Post> requestPosts(Object peerid, long startnumber, long endnumber) {
		List<Post> pl = new LinkedList<Post>();
		Peer peer = Store.getPeer(peerid);
		if (peer != null) {
			List<LocalPost> lpl = Store.getPeerPosts(peer, startnumber, endnumber);
			Iterator<LocalPost> i = lpl.iterator();
			while (i.hasNext()) {
				LocalPost lp = i.next();
				pl.add(lp.getPost());
			}
		}
		return pl;
	}

	@Override
	public File requestsFiles(Object digest) {
		LocalFileReference fr = Store.getFileReference(digest);
		if (fr != null) {
			if (fr.getFileReference() != null) {
				File f = fr.getFileReference().getFile();
				if (f.exists()) {
					return f;
				}
			}
		}
		return null;
	}

	@Override
	public List<Peer> requestPeers() {
		List<Peer> rl = Store.getPeerList();
		rl.add(getMyPeer());
		return rl;
	}

	@Override
	public Peer getMyPeer() {
		return Store.getMyPeerData();
	}

	@Override
	public KeySet getMyKeySet() {
		return Store.getMyKeySet();
	}

	public void setSecurity(CryptoDataBaseInterface security) {
		Security = security;
	}

	public void setStore(StorageInterface store) {
		Store = store;
	}

	public void setTime(TimeInterface time) {
		Time = time;
	}

	@Override
	public boolean isBadPeer(Object dig) {
		return Store.isBadPeer(dig);
	}

}
