/* Freenet Plugin to run WikiApp.
 *
 * Copyright (C) 2010, 2011 Darrell Karbott
 *
 * This library 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.0 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks
 *
 *  This file was developed as component of
 * "fniki" (a wiki implementation running over Freenet).
 */

package fniki.freenet.plugin;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import freenet.pluginmanager.AccessDeniedPluginHTTPException;
import freenet.pluginmanager.FredPlugin;
import freenet.pluginmanager.FredPluginHTTP;
import freenet.pluginmanager.FredPluginThreadless;
import freenet.pluginmanager.NotFoundPluginHTTPException;
import freenet.pluginmanager.PluginHTTPException;
import freenet.pluginmanager.PluginRespirator;
import freenet.pluginmanager.RedirectPluginHTTPException;
import freenet.support.api.HTTPRequest;

import fniki.wiki.ArchiveManager;
import fniki.wiki.Query;
import fniki.wiki.Request;
import fniki.wiki.WikiApp;

import fniki.wiki.AccessDeniedException;
import fniki.wiki.NotFoundException;
import fniki.wiki.RedirectException;
import fniki.wiki.ChildContainerException;

public class Fniki implements FredPlugin, FredPluginHTTP, FredPluginThreadless {
    private WikiApp mWikiApp;

    public void terminate() {
        System.err.println("terminating...");
    }

    public void runPlugin(PluginRespirator pr) {
        try {
            ArchiveManager archiveManager = new ArchiveManager();

            // DCI: Parameter handling?
            archiveManager.setFcpHost("127.0.0.1");
            archiveManager.setFcpPort(9481);

            archiveManager.setFmsHost("127.0.0.1");
            archiveManager.setFmsPort(1119);

            // YOU MUST SET THESE OR THE PLUGIN WON'T LOAD.
            archiveManager.setPrivateSSK("FMS_PRIVATE_SSK");
            archiveManager.setFmsId("FMS_ID");

            archiveManager.setFmsGroup("biss.test000");
            archiveManager.setBissName("testwiki");

            String fproxyPrefix = "http://127.0.0.1:8888/";
            boolean enableImages = true;

            archiveManager.createEmptyArchive();

            WikiApp wikiApp = new WikiApp(archiveManager);
            final String containerPrefix = wikiApp.getString("container_prefix", null);
            if (containerPrefix == null) {
                throw new RuntimeException("Assertion Failure: container_prefix not set!");
            }
            wikiApp.setFproxyPrefix(fproxyPrefix);
            wikiApp.setAllowImages(enableImages);

            // IMPORTANT:
            // HTTP POSTS will be rejected without any useful error message if your form
            // doesn't contain a hidden field with the freenet per boot form password.
            wikiApp.setFormPassword(pr.getNode().clientCore.formPassword);

            mWikiApp = wikiApp;

        } catch (IOException ioe) {
            System.err.println("EPIC FAIL!");
            ioe.printStackTrace();
        }
    }

    private static class ServerPluginHTTPException extends PluginHTTPException {
	private static final long serialVersionUID = -1;

	public static final short code = 500; // Bad Request
	public ServerPluginHTTPException(String errorMessage, String location) {
            super(errorMessage, location);
	}
    }

    private static class PluginQuery implements Query {
        private final HTTPRequest mParent;
        private final String mTitle;
        private final String mAction;
        private final String mSaveText;
        private final String mSavePage;

        PluginQuery(HTTPRequest parent, String path) {
            mParent = parent;

            String title = path;
            if (parent.isParameterSet("title")) {
                title = parent.getParam("title");
            }
            mTitle = title;

            // DCI: validate title here

            String action = "view";
            if (parent.isParameterSet("action")) {
                action = parent.getParam("action");
            }
            mAction = action;

            // Handle multipart form parameters.
            System.err.println("Dumping list of parts...");
            String saveText = "";
            String savePage = "";
            try {
                for (String part : parent.getParts()) {
                    if (part.equals("savetext")) {
                        // DCI: magic numbers
                        saveText = new String(parent.getPartAsBytesFailsafe(part, 64 * 1024), "utf-8");
                        continue;
                    }
                    if (part.equals("savepage")) {
                        savePage = new String(parent.getPartAsBytesFailsafe(part, 64 * 1024), "utf-8");
                    }
                }
            } catch (UnsupportedEncodingException ue) {
                // Shouldn't happen.
                ue.printStackTrace();
            }
            mSaveText = saveText;
            mSavePage = savePage;

            parent.freeParts(); // DCI: test!, put in finally?

        }

        public boolean containsKey(String paramName) {
            if (paramName.equals("title") || paramName.equals("action") ||
                paramName.equals("savetext") || paramName.equals("savepage")) {
                return true;
            }
            return mParent.isParameterSet(paramName);
        }

        public String get(String paramName) {
            if (paramName.equals("title")) {
                return mTitle;
            }
            if (paramName.equals("action")) {
                return mAction;
            }
            if (paramName.equals("savetext")) {
                return mSaveText;
            }
            if (paramName.equals("savepage")) {
                return mSavePage;
            }
            if (!containsKey(paramName)) {
                return null;
            }
            return mParent.getParam(paramName);
        }
    }

    private static class PluginRequest implements Request {
        private final Query mQuery;
        private final String mPath;
        PluginRequest(HTTPRequest parent, String containerPrefix) { // DCI throws IOException {
            for (String key : parent.getParameterNames()) {
                String value = parent.getParam(key);
                if (value.length() > 128) {
                    value = value.substring(0, 128) + "...";
                }
                System.err.println(String.format("[%s] => [%s]", key, value));
            }

            String path = parent.getPath();
            if (!path.startsWith(containerPrefix)) {
                // This should be impossible because of the way plugin requests are routed.
                throw new RuntimeException("Request doesn't start with: " + containerPrefix);
            }

            System.err.println("Raw path: " + path);
            path = path.substring(containerPrefix.length());

            while(path.startsWith("/")) {
                path = path.substring(1).trim();
            }
            mQuery = new PluginQuery(parent, path);
            mPath = path;
        }

        public String getPath() { return mPath; }
        public Query getQuery() { return mQuery; }
    }

    public String handle(HTTPRequest request) throws PluginHTTPException {
        // DCI: cleanup container_prefix usage

        try {
            mWikiApp.setRequest(new PluginRequest(request, mWikiApp.getString("container_prefix", null)));
            return mWikiApp.handle(mWikiApp);
        } catch(AccessDeniedException accessDenied) {
            throw new AccessDeniedPluginHTTPException(accessDenied.getMessage(),
                                                      mWikiApp.getString("container_prefix", null));
        } catch(NotFoundException notFound) {
            throw new NotFoundPluginHTTPException(notFound.getMessage(),
                                                  mWikiApp.getString("container_prefix", null));
        } catch(RedirectException redirected) {
            throw new RedirectPluginHTTPException(redirected.getMessage(),
                                              redirected.getLocation());
        } catch(ChildContainerException serverError) {
            throw new ServerPluginHTTPException(serverError.getMessage(),
                                                mWikiApp.getString("container_prefix", null));
        }
    }

    public String handleHTTPGet(HTTPRequest request) throws PluginHTTPException {
        return handle(request);
    }

    public String handleHTTPPost(HTTPRequest request) throws PluginHTTPException {
        return handle(request);
    }
}