package org.ourfilesystem.simulator.com;

/*
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 java.util.concurrent.ConcurrentLinkedQueue;
import java.util.Random;

import org.ourfilesystem.com.ComPeerInterface;
import org.ourfilesystem.com.ConnectionUpdateInterface;
import org.ourfilesystem.core.CoreComInterface;
import org.ourfilesystem.core.EventInterface;
import org.ourfilesystem.db.DataBaseComInterface;
import org.ourfilesystem.db.Peer;
import org.ourfilesystem.db.Post;
import org.ourfilesystem.utilities.Cloning;

public class ComPeerImpl implements ComPeerInterface, ConnectionUpdateInterface {
	
	public static List<ComPeerImpl> PeerConnections = new LinkedList<ComPeerImpl>();
	
	public static void StepProcess() {
		List<ComPeerImpl> copylist = new LinkedList<ComPeerImpl>();
		copylist.addAll(PeerConnections);
		Iterator<ComPeerImpl> i = copylist.iterator();
		while (i.hasNext()) {
			ComPeerImpl c = i.next();
			c.Process();
		}
		i = PeerConnections.iterator();
		while (i.hasNext()) {
			ComPeerImpl c = i.next();
			if (!c.isOk()) {
				i.remove();
			}
		}
	}
	
	public boolean Closed;
	
	public Random Random;
	
	public CoreComInterface Core;
	public DataBaseComInterface DB;
	public ComPeerImpl ConnectedTo;
	
	public ConcurrentLinkedQueue<PeerRequestQueueEntry> PeerRequests;
	public ConcurrentLinkedQueue<PostRequestQueueEntry> PostRequests;
	public ConcurrentLinkedQueue<FileRequestQueueEntry> FileRequests;
	public ConcurrentLinkedQueue<Post> SentPosts;
	
	public ConcurrentLinkedQueue<List<Peer>> PeerReturns;
	public ConcurrentLinkedQueue<List<Post>> PostReturns;
	public ConcurrentLinkedQueue<File> FileReturns;
	
	public ComPeerImpl() {
		PeerRequests = new ConcurrentLinkedQueue<PeerRequestQueueEntry>() ;
		PostRequests = new ConcurrentLinkedQueue<PostRequestQueueEntry>() ;
		FileRequests = new ConcurrentLinkedQueue<FileRequestQueueEntry>() ;	
		PeerReturns  = new ConcurrentLinkedQueue<List<Peer>>() ;
		PostReturns  = new ConcurrentLinkedQueue<List<Post>>() ;
		FileReturns  = new ConcurrentLinkedQueue<File>() ;
		SentPosts = new ConcurrentLinkedQueue<Post>();
		Closed = false;
		Random = new Random();
		PeerConnections.add(this);
	}
	
	public Peer getThisPeer() {
		return DB.getMyPeer();
	}
	
	@Override
	public Peer getPeer() {
		if (ConnectedTo != null) {
			return ConnectedTo.getThisPeer();
		}
		return null;
	}

	@Override
	public boolean isOk() {
		return !Closed;
	}

	@Override
	public int getPendingRequests() {
		return PeerRequests.size() +
				PostRequests.size() +
				FileRequests.size() +
				PeerReturns.size() +
				PostReturns.size() + 
				FileReturns.size();
	}

	@Override
	public void Close() {
		if (!Closed) {
			Closed = true;
			if (ConnectedTo != null) {
				ConnectedTo.Close();
			}
			while (this.getPendingRequests() > 0) {
				Process();
			}
			Core.connectionClosed(this.getPeer().getPeerKeysAndIdentity().getSignature().getDigest(), true);
		}
	}

	@Override
	public void requtestPosts(Object peerid, long fromnumber, long tonumber) {
		PostRequests.add(new PostRequestQueueEntry(peerid, fromnumber, tonumber));
	}

	@Override
	public void requestPeers() {
		PeerRequests.add(new PeerRequestQueueEntry());
	}

	@Override
	public void requestsFile(Object digest) {
		FileRequests.add(new FileRequestQueueEntry(digest));
	}
	
	public List<Post> doPostRequest(PostRequestQueueEntry p) {
		List<Post> lp = new LinkedList<Post>();
		List<Post> dlp = DB.requestPosts(p.Peer, p.From, p.To);
		Iterator<Post> i = dlp.iterator();
		while (i.hasNext()) {
			lp.add(Cloning.clonePost(i.next()));
		}
		return lp;
	}
	
	public List<Peer> doPeerRequest(PeerRequestQueueEntry p) {
		List<Peer> lp = new LinkedList<Peer>();
		List<Peer> dlp = DB.requestPeers();
		Iterator<Peer> i = dlp.iterator();
		while (i.hasNext()) {
			lp.add(Cloning.clonePeer(i.next()));
		}
		return lp;
	}
	
	public File doFileRequest(FileRequestQueueEntry p) {
		return DB.requestsFiles(p.Digest);
	}
	
	public void Process() {
		if (Random.nextFloat() < 0.4) {
			return;
		}
		if (Random.nextFloat() < 0.1) {
			Close();
		}
		List<Post> postreturns = PostReturns.poll();
		if (postreturns != null) {
			DB.processPeerPosts(postreturns);
		}
		List<Peer> peerreturns = PeerReturns.poll();
		if (peerreturns != null) {
			DB.processPeers(peerreturns);
		}
		File f = FileReturns.poll();
		if (f != null) {
			DB.processPeerFile(f);
		}
		PostRequestQueueEntry pr = PostRequests.poll();
		if (pr != null) {
			if (isOk()) {
				List<Post> p = ConnectedTo.doPostRequest(pr);
				if (p != null) {
					PostReturns.add(p);
				}
			}
			else {
				Core.requestPostsFailed(getPeer().getPeerKeysAndIdentity().getSignature().getDigest(), pr.Peer, pr.From, pr.To);
			}
		}
		PeerRequestQueueEntry er = PeerRequests.poll();
		if (er != null) {
			if (isOk()) {
				List<Peer> p = ConnectedTo.doPeerRequest(er);
				if (p != null) {
					PeerReturns.add(p);
				}
			}
		}
		FileRequestQueueEntry fr = FileRequests.poll();
		if (fr != null) {
			if (isOk()) {
				File file = ConnectedTo.doFileRequest(fr);
				if (file != null) {
					FileReturns.add(file);
				}
			}
			else {
				Core.requestFileFailed(getPeer().getPeerKeysAndIdentity().getSignature().getDigest(), fr.Digest);
			}
		}
		Post p = SentPosts.poll();
		if (p != null) {
			List<Post> pl = new LinkedList<Post>();
			pl.add(p);
			ConnectedTo.DB.processPeerPosts(pl);
		}
	}

	@Override
	public void sendPost(Post post) {
		SentPosts.add(post);
	}

	@Override
	public void addEventInterface(EventInterface ev) {
		// TODO Auto-generated method stub
		
	}

}
