/* A UI subcomponent to display and edit wikitext.
 *
 * 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.wiki.child;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static ys.wikiparser.Utils.*;

import fniki.wiki.ChildContainer;
import fniki.wiki.ChildContainerException;
import fniki.wiki.Query;

import wormarc.FileManifest;
import wormarc.IOUtil;

import fniki.wiki.FreenetWikiTextParser;
import static fniki.wiki.HtmlUtils.*;
import static fniki.wiki.Validations.*;
import fniki.wiki.WikiApp;
import fniki.wiki.WikiContext;
import fniki.wiki.WikiTextStorage;

public class WikiContainer implements ChildContainer {
    private final static String ENCODING = "UTF-8";

    public WikiContainer() {}

    public String handle(WikiContext context) throws ChildContainerException {
        try {
            String action = context.getAction();
            if (action.equals("finished")) {
                // Hack: Ignore "finished".
                // This happens when the user hits the back button and picks
                // a link from a finished task page. e.g. changelog.
                action = "view";
            }
            String title = context.getTitle();
            if (!isAlphaNumOrUnder(title)) {
                // Titles must be legal page names.
                context.raiseAccessDenied("Couldn't work out query.");
            }

            if (!isLowerCaseAlpha(action)) {
                // Illegal action
                context.raiseAccessDenied("Couldn't work out query.");
            }

            Query query = context.getQuery();

            if (action.equals("view")) {
                return handleView(context, title);
            } else if (action.equals("edit")) {
                return handleEdit(context, title);
            } else if (action.equals("delete")) {
                return handleDelete(context, title);
            } else if (action.equals("revert")) {
                return handleRevert(context, title);
            } else if (action.equals("save")) {
                return handleSave(context, query);
            } else  {
                context.raiseAccessDenied("Couldn't work out query.");
            }
        } catch (IOException ioe) {
            context.logError("WikiContainer.handle", ioe);
            context.raiseServerError("Unexpected Error in WikiContainer.handle. Sorry :-(");
        }
        return "unreachable code";
    }

    private String handleView(WikiContext context, String name) throws IOException {
        return getPageHtml(context, name);
    }

    private String handleEdit(WikiContext context, String name) throws IOException {
        return getEditorHtml(context, name);
    }

    private String handleDelete(WikiContext context, String name) throws IOException {
        if (context.getStorage().hasPage(name)) {
            context.getStorage().deletePage(name);
        }

        // LATER: do better.
        return getPageHtml(context, name);
    }

    private String handleRevert(WikiContext context, String name) throws ChildContainerException, IOException {
        context.getStorage().revertLocalChange(name);
        context.raiseRedirect(context.makeLink("/" + name), "Redirecting...");
        return "unreachable code";
    }

    private String handleSave(WikiContext context, Query form) throws ChildContainerException, IOException {
        // Name is included in the query data.
        System.err.println("handleSave -- ENTERED");
        String name = form.get("savepage");
        String wikiText = form.get("savetext");

        System.err.println("handleSave --got params");
        if (name == null || wikiText == null) {
            context.raiseAccessDenied("Couldn't parse parameters from POST.");
        }

        System.err.println("Writing: " + name);
        context.getStorage().putPage(name, unescapeHTML(wikiText));
        System.err.println("Raising redirect!");
        context.raiseRedirect(context.makeLink("/" + name), "Redirecting...");
        System.err.println("SOMETHING WENT WRONG!");
        return "unreachable code";
    }

    private String unescapedTitleFromName(String name) {
        return name.replace("_", " ");
    }

    private String getPageHtml(WikiContext context, String name) throws IOException {
        StringBuilder buffer = new StringBuilder();
        addHeader(name, buffer);
        if (context.getStorage().hasPage(name)) {
            buffer.append(renderXHTML(context, context.getStorage().getPage(name)));
        } else {
            if (name.equals(context.getString("default_page", "Front_Page"))) {
                buffer.append(renderXHTML(context,
                                          context.getString("default_wikitext",
                                                            "Page doesn't exist in the wiki yet.")));
            } else if (context.getStorage().hasPage("PageDoesNotExist")) {
                buffer.append(renderXHTML(context, context.getStorage().getPage("PageDoesNotExist")));	
            } else {
                buffer.append("Page doesn't exist in the wiki yet.");
            }
        }
        addFooter(context, name, buffer);
        return buffer.toString();
    }

    private void addHeader(String name, StringBuilder buffer) throws IOException {
        buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        buffer.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +
                      "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
        buffer.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
        buffer.append("<head><title>\n");
        buffer.append(escapeHTML(unescapedTitleFromName(name)));
        buffer.append("</title>\n");
        buffer.append("<style type=\"text/css\">div.indent{margin-left:20px;} " +
                      "div.center{text-align:center;} " +
                      "blockquote{margin-left:20px;background-color:#e0e0e0;} " +
                      "span.underline{text-decoration:underline;}</style>\n");
        buffer.append("</head>\n");
        buffer.append("<body>\n");
        buffer.append("<h1>\n");
        buffer.append(escapeHTML(unescapedTitleFromName(name)));
        buffer.append("</h1><hr>\n");
    }

    private String makeLocalLink(WikiContext context, String name, String action, String label) {
        String href = makeHref(context.makeLink("/" + name), action, name, null, null);
        return String.format("<a href=\"%s\">%s</a>", href, escapeHTML(label));
    }

    private void addFooter(WikiContext context, String name, StringBuilder buffer) throws IOException {
        buffer.append("<hr>\n");
        buffer.append("Parent Version: ");
        // DCI: css class to make this smaller.
        String version = getVersionHex(context.getString("parent_uri", null));
        buffer.append(escapeHTML(version));
        buffer.append("<hr>\n");

        buffer.append(makeLocalLink(context, name, "edit", "Edit"));
        buffer.append(" this page.<br>");

        buffer.append(makeLocalLink(context, name, "delete", "Delete"));
        buffer.append(" this page without confirmation!<br>");

        if (context.getStorage().hasLocalChange(name)) {
            buffer.append(makeLocalLink(context, name, "revert", "Revert"));
            buffer.append(" local changes to this page without confirmation!<br>");
        }

        buffer.append(makeLocalLink(context, "fniki/submit", null, "Submit"));
        buffer.append(" local changes. <br>");

        buffer.append(makeLocalLink(context, "fniki/changelog", "confirm", "Show"));
        buffer.append(" change history for this version. <br>");

        buffer.append(makeLocalLink(context, "fniki/getversions", "confirm", "Discover"));
        buffer.append(String.format(" other recent version of this wiki (wikiname: [%s], FMS group: [%s])<br>",
                                    escapeHTML(context.getString("wikiname", "NOT_SET")),
                                    escapeHTML(context.getString("fms_group", "NOT_SET"))));

        buffer.append(makeLocalLink(context, "fniki/config", "view", "View"));
        buffer.append(" configuration.<p/>\n");

        buffer.append(makeLocalLink(context, "fniki/resettoempty", "view", "Create Wiki!"));
        buffer.append(" (<em>careful:</em> This deletes all content and history without confirmation.)<p/>\n");

        buffer.append(gotoPageFormHtml(context.makeLink("/" + name),
                                       context.getString("default_page", "Front_Page")));


        buffer.append("</body></html>");
    }

    private String getEditorHtml(WikiContext context, String name) throws IOException {
        String template = null;
        try {
            // IMPORTANT: Only multipart/form-data encoding works in plugins.
            // IMPORTANT: Must be multipart/form-data even for standalone because
            //            the Freenet ContentFilter rewrites the encoding in all forms
            //            to this value.
            template = IOUtil.readUtf8StringAndClose(SettingConfig.class.getResourceAsStream("/edit_form.html"));
        } catch (IOException ioe) {
            return "Couldn't load edit_form.html template from jar???";
        }

        String escapedName = escapeHTML(unescapedTitleFromName(name));
        String href = makeHref(context.makeLink("/" +name),
                               "save", null, null, null);
        String wikiText = "Page doesn't exist in the wiki yet.";
        if (context.getStorage().hasPage(name)) {
            wikiText = context.getStorage().getPage(name);
        }

        return String.format(template,
                             escapedName,
                             escapedName,
                             href,
                             escapeHTML(name), // i.e. with '_' chars
                             escapeHTML(wikiText),
                             // IMPORTANT: Required by Freenet Plugin.
                             // Doesn't need escaping.
                             context.getString("form_password", "FORM_PASSWORD_NOT_SET"));
    }

    public String renderXHTML(WikiContext context, String wikiText) {
        return new FreenetWikiTextParser(wikiText, context.getParserDelegate()).toString();
    }
}