/* This code is part of Freenet. It is distributed under the GNU General
 * Public License, version 2 (or at your option any later version). See
 * http://www.gnu.org/ for further details of the GPL. */
package freenet.clients.http;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;

import freenet.client.HighLevelSimpleClient;
import freenet.l10n.NodeL10n;
import freenet.node.DarknetPeerNode;
import freenet.node.Node;
import freenet.node.NodeClientCore;
import freenet.node.NodeStarter;
import freenet.node.PeerManager;
import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.MultiValueTable;
import freenet.support.SizeUtil;
import freenet.support.api.HTTPRequest;
import freenet.support.api.HTTPUploadedFile;

public class N2NTMToadlet extends Toadlet {
	private Node node;
	private LocalFileN2NMToadlet browser;
	protected N2NTMToadlet(Node n, NodeClientCore core,
			HighLevelSimpleClient client) {
		super(client);
		browser = new LocalFileN2NMToadlet(core, client);
		this.node = n;
	}

	public Toadlet getBrowser() {
		return browser;
	}

	public void handleMethodGET(URI uri, HTTPRequest request, ToadletContext ctx)
			throws ToadletContextClosedException, IOException,
			RedirectException {

	if(!ctx.checkFullAccess(this))
	    return;

		if (request.isParameterSet("peernode_hashcode")) {
			PageNode page = ctx.getPageMaker().getPageNode(l10n("sendMessage"), ctx);
			HTMLNode pageNode = page.outer;
			HTMLNode contentNode = page.content;

			String peernode_name = null;
			String input_hashcode_string = request.getParam("peernode_hashcode");
			int input_hashcode = -1;
			try {
				input_hashcode = Integer.parseInt(input_hashcode_string);
			} catch (NumberFormatException e) {
				// ignore here, handle below
			}
			if (input_hashcode != -1) {
				DarknetPeerNode[] peerNodes = node.getDarknetConnections();
				for (DarknetPeerNode pn: peerNodes) {
					int peer_hashcode = pn.hashCode();
					if (peer_hashcode == input_hashcode) {
						peernode_name = pn.getName();
						break;
					}
				}
			}
			if (peernode_name == null) {
				contentNode.addChild(createPeerInfobox("infobox-error",
						l10n("peerNotFoundTitle"), l10n("peerNotFoundWithHash",
								"hash", input_hashcode_string)));
				this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
				return;
			}
			HashMap<String, String> peers = new HashMap<String, String>();
			peers.put(input_hashcode_string, peernode_name);
			createN2NTMSendForm(pageNode, ctx.isAdvancedModeEnabled(), contentNode, ctx, peers);
			this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
			return;
		}
		MultiValueTable<String, String> headers = MultiValueTable.from("Location", "/friends/");
		ctx.sendReplyHeaders(302, "Found", headers, null, 0);
	}

	private String l10n(String key, String pattern[], String value[]) {
		return NodeL10n.getBase().getString("N2NTMToadlet." + key, pattern, value);
	}

	private String l10n(String key, String pattern, String value) {
		return NodeL10n.getBase().getString("N2NTMToadlet." + key, new String[] { pattern }, new String[] { value });
	}

	private static String l10n(String key) {
		return NodeL10n.getBase().getString("N2NTMToadlet." + key);
	}

	/*
	 * File size limit is 1 MiB (1024*1024 bytes) or 5% of maximum Java memory, whichever is greater.
	 */
	private static long maxSize(){
		long memory = NodeStarter.getMemoryLimitBytes();
		if(memory == Long.MAX_VALUE || memory <= 0) return 1024*1024;
		long maxMem = Math.round(0.05*memory);
		long limit = Math.max(maxMem, 1024*1024);
		return limit;
	}

	private static HTMLNode createPeerInfobox(String infoboxType,
			String header, String message) {
		HTMLNode infobox = new HTMLNode("div", "class", "infobox "+infoboxType);
		infobox.addChild("div", "class", "infobox-header", header);
		HTMLNode infoboxContent = infobox.addChild("div", "class", "infobox-content");
		infoboxContent.addChild("#", message);
		HTMLNode list = infoboxContent.addChild("ul");
		Toadlet.addHomepageLink(list);
		list.addChild("li").addChild("a", new String[] { "href", "title" },
			new String[] { "/friends/", l10n("returnToFriends") },
			l10n("friends"));
		return infobox;
	}

	public void handleMethodPOST(URI uri, HTTPRequest request, ToadletContext ctx)
			throws ToadletContextClosedException, IOException,
			RedirectException {

	if(!ctx.checkFullAccess(this))
	    return;

		//Browse button clicked. Redirect.
		if(request.isPartSet("n2nm-browse"))
		{
			try {
				throw new RedirectException(LocalFileN2NMToadlet.PATH);
			} catch (URISyntaxException e) {
				//Should be impossible because the browser is registered with .PATH.
			}
			return;
		}

		if (request.isPartSet("n2nm-upload") || request.isPartSet(LocalFileBrowserToadlet.selectFile) || request.isPartSet("send")) {
			File filename = null;
			String message = request.getPartAsStringFailsafe("message", 1024 * 1024);
			message = message.trim();
			String messageHead = message.substring(0, Math.min(message.length(), 1024));
			if (message.length() > 1024 * 128) {
				this.writeTextReply(ctx, 400, "Bad request", l10n("tooLong"));
				return;
			}
			PageNode page = ctx.getPageMaker().getPageNode(l10n("processingSend"), ctx);
			HTMLNode pageNode = page.outer;
			HTMLNode contentNode = page.content;
			HTMLNode peerTableInfobox = contentNode.addChild("div", "class", "infobox infobox-normal");
			DarknetPeerNode[] peerNodes = node.getDarknetConnections();
			if(request.isPartSet(LocalFileBrowserToadlet.selectFile)) {
				String fnam = request.getPartAsStringFailsafe("filename", 1024);
				if(fnam != null && !fnam.isEmpty()) {
					filename = new File(fnam);
					if(!(filename.exists() && filename.canRead())) {
						peerTableInfobox.addChild("#", l10n("noSuchFileOrCannotRead"));
						Toadlet.addHomepageLink(peerTableInfobox);
						this.writeHTMLReply(ctx, 400, "OK", pageNode.generate());
						return;
					}
				}
			}

			HTMLNode peerTable = peerTableInfobox.addChild("table", "class", "n2ntm-send-statuses");
			HTMLNode peerTableHeaderRow = peerTable.addChild("tr");
			peerTableHeaderRow.addChild("th", l10n("peerName"));
			peerTableHeaderRow.addChild("th", l10n("sendStatus"));
			for (DarknetPeerNode pn: peerNodes) {
				if (request.isPartSet("node_" + pn.hashCode())) {
					int status;

					if(filename != null) {
						try {
							status = pn.sendFileOffer(filename, messageHead);
						} catch (IOException e) {
							peerTableInfobox.addChild("#", l10n("noSuchFileOrCannotRead"));
							Toadlet.addHomepageLink(peerTableInfobox);
							addUnsentMessageTextInfo(peerTableInfobox, message);
							this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
							return;
						}
					} else if(request.isPartSet("n2nm-upload")) {
						try{
							HTTPUploadedFile file = request.getUploadedFile("n2nm-upload");
							if(!file.getFilename().isEmpty()) {
								long size = request.getUploadedFile("n2nm-upload").getData().size();
								if(size > 0) {
									long limit = maxSize();
									if(size > limit){
										peerTableInfobox.addChild("#", l10n("tooLarge", new String[] {"attempt", "limit"},
												new String[] {SizeUtil.formatSize(size, true), SizeUtil.formatSize(limit, true)}));
										HTMLNode list = peerTableInfobox.addChild("ul");
										Toadlet.addHomepageLink(list);
										list.addChild("li").addChild("a", new String[] { "href", "title" },
												new String[] { "/friends/", l10n("returnToFriends") },
												l10n("friends"));
										addUnsentMessageTextInfo(peerTableInfobox, message);
										this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
										return;
									}
									status = pn.sendFileOffer(request.getUploadedFile("n2nm-upload"), messageHead);
								}
							}
						} catch (IOException e) {
							peerTableInfobox.addChild("#", l10n("uploadFailed"));
							Toadlet.addHomepageLink(peerTableInfobox);
							addUnsentMessageTextInfo(peerTableInfobox, message);
							this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
							return;
						}
					}
					status = pn.sendTextFeed(message);

					String sendStatusShort;
					String sendStatusLong;
					String sendStatusClass;
					if(status == PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF) {
						sendStatusShort = l10n("delayedTitle");
						sendStatusLong = l10n("delayed");
						sendStatusClass = "n2ntm-send-delayed";
						Logger.normal(this, "Sent N2NTM to '"
								+ pn.getName() + "': " + message);
					} else if(status == PeerManager.PEER_NODE_STATUS_CONNECTED) {
						sendStatusShort = l10n("sentTitle");
						sendStatusLong = l10n("sent");
						sendStatusClass = "n2ntm-send-sent";
						Logger.normal(this, "Sent N2NTM to '"
								+ pn.getName() + "': " + message);
					} else {
						sendStatusShort = l10n("queuedTitle");
						sendStatusLong = l10n("queued");
						sendStatusClass = "n2ntm-send-queued";
						Logger.normal(this, "Queued N2NTM to '"
								+ pn.getName() + "': " + message);
					}
					HTMLNode peerRow = peerTable.addChild("tr");
					peerRow.addChild("td", "class", "peer-name").addChild("#", pn.getName());
					peerRow.addChild("td", "class", sendStatusClass)
									.addChild("span",
									new String[] { "title", "style" },
									new String[] { sendStatusLong,
											"border-bottom: 1px dotted; cursor: help;" },
									sendStatusShort);
				}
			}
			HTMLNode infoboxContent = peerTableInfobox.addChild("div", "class",
					"n2ntm-message-text");
			infoboxContent.addChild("#", message);
			HTMLNode list = peerTableInfobox.addChild("ul");
			Toadlet.addHomepageLink(list);
			list.addChild("li").addChild("a", new String[] { "href", "title" },
					new String[] { "/friends/", l10n("returnToFriends") },
					l10n("friends"));
			this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
			return;
		}
		MultiValueTable<String, String> headers = MultiValueTable.from("Location", "/friends/");
		ctx.sendReplyHeaders(302, "Found", headers, null, 0);
	}

	public static void addUnsentMessageTextInfo(HTMLNode node, String message) {
		node.addChild("p", l10n("unsentMessageText"));
		node.addChild("p", message);
	}

	public static void createN2NTMSendForm(HTMLNode pageNode, boolean advancedMode,
			HTMLNode contentNode, ToadletContext ctx, HashMap<String, String> peers)
			throws ToadletContextClosedException, IOException {
		HTMLNode infobox = contentNode.addChild("div", new String[] { "class",
				"id" }, new String[] { "infobox", "n2nbox" });
		infobox.addChild("div", "class", "infobox-header", l10n("sendMessage"));
		HTMLNode messageTargets = infobox.addChild("div", "class",
				"infobox-content");
		messageTargets.addChild("p", l10n("composingMessageLabel"));
		HTMLNode messageTargetList = messageTargets.addChild("ul");
		// Iterate peers
		for (String peer_name: peers.values()) {
			messageTargetList.addChild("li", peer_name);
		}
		HTMLNode infoboxContent = infobox.addChild("div", "class", "infobox-content");
		HTMLNode messageForm = ctx.addFormChild(infoboxContent, "/send_n2ntm/", "sendN2NTMForm");
		// Iterate peers
		for (String peerNodeHash : peers.keySet()) {
			messageForm.addChild("input", new String[] { "type", "name",
					"value" }, new String[] { "hidden", "node_" + peerNodeHash,
					"1" });
		}
		messageForm.addChild("textarea", new String[] { "id", "name", "rows",
				"cols" }, new String[] { "n2ntmtext", "message", "8", "74" });
		if(advancedMode){
			messageForm.addChild("br");
			messageForm.addChild("#", NodeL10n.getBase().getString("N2NTMToadlet.mayAttachFile"));
			if(ctx.isAllowedFullAccess()) {
				messageForm.addChild("br");
				messageForm.addChild("#", NodeL10n.getBase().getString("QueueToadlet.insertFileBrowseLabel")+": ");
				messageForm.addChild("input", new String[] { "type", "name", "value" }, new String[] { "submit", "n2nm-browse", NodeL10n.getBase().getString("QueueToadlet.insertFileBrowseButton") + "..." });
				messageForm.addChild("br");
			}
			messageForm.addChild("#", NodeL10n.getBase().getString("N2NTMToadlet.sizeWarning", "limit", SizeUtil.formatSize(maxSize(), true)));
			messageForm.addChild("br");
			messageForm.addChild("#", NodeL10n.getBase().getString("QueueToadlet.insertFileLabel") + ": ");
			messageForm.addChild("input", new String[] {"type", "name", "value" }, new String[] { "file", "n2nm-upload", "" });
			messageForm.addChild("br");
		}
		messageForm.addChild("input", new String[] { "type", "name", "value" },
				new String[] { "submit", "send", l10n("sendMessageShort") });
	}

	@Override
	public String path() {
		return "/send_n2ntm/";
	}
}
