/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  The ASF licenses this file to You
 * under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.roller.weblogger.webservices.tagdata;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.URLStrategy;
import org.apache.roller.weblogger.business.WeblogEntryManager;
import org.apache.roller.weblogger.business.Weblogger;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.business.WeblogManager;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
import org.apache.roller.weblogger.pojos.TagStat;
import org.apache.roller.weblogger.pojos.Weblog;
import org.apache.roller.weblogger.util.Utilities;


/**
 * Return tag statistics in JSON or Atom Category format.
 * These URLs are supported:
 * <ul>
 * <li>/roller-services/tagdata - get tag data for entire site</li>
 * <li>/roller-services/tagdata/weblog/[handle] - get tag data for specific weblog</li>
 * </ul>
 * See the <a href="http://cwiki.apache.org/confluence/display/ROLLER/Proposal+Tag+Data+API">
 * Tag Data API</a> proposal for details.
 * 
 * @author Elias Torres (<a href="mailto:eliast@us.ibm.com">eliast@us.ibm.com</a>)
 * @author Dave Johnson (<a href="mailto:davidm.johnson@sun.com">davidm.johnson@sun.com</a>)
 */
public class TagDataServlet extends HttpServlet {

    private static final int MAX = WebloggerConfig.getIntProperty("services.tagdata.max", 30);

    
    @Override
    protected void doPost(
            HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        doGet(request, response);
    }

    
    @Override
    public void doGet(
            HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {

        // TODO: last modified or ETag support, caching, etc.

        String[] pathInfo = new String[0];
        
        if (request.getPathInfo() != null) {
            pathInfo = Utilities.stringToStringArray(request.getPathInfo(),"/");
        }
        
        boolean siteWide;
        String handle;

        if (pathInfo.length == 0) {
            siteWide = true;
            // we'll use the front-page weblog to form URLs
            handle = WebloggerRuntimeConfig.getProperty("site.frontpage.weblog.handle");
        } else if (pathInfo.length == 2 && "weblog".equals(pathInfo[0]) && StringUtils.isAlphanumeric(pathInfo[1])) {
            siteWide = false;
            handle = pathInfo[1];
        } else {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Malformed URL");
            return;
        }

        String prefix = request.getParameter("prefix");

        if(prefix != null && !StringUtils.isAlphanumeric(prefix)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Malformed URL");
            return;
        }
        
        String format = "json";  // default
        
        if (request.getParameter("format") != null) {
            
            format = request.getParameter("format");
            if(!format.equals("json") && !format.equals("xml")) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Malformed URL");
                return;
            }
        }
        
        int page = 0;
        if(request.getParameter("page") != null) {
            try {
                page = Integer.parseInt(request.getParameter("page"));
            } catch (NumberFormatException notIgnored) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Malformed URL");
                return;
            }
        }

        Weblogger roller = WebloggerFactory.getWeblogger();
        List<TagStat> tags;
        Weblog weblog;
        try {
            WeblogManager wmgr = roller.getWeblogManager();
            WeblogEntryManager emgr = roller.getWeblogEntryManager();
            weblog = wmgr.getWeblogByHandle(handle);
            if(weblog == null) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Weblog not found");
                return;
            }
            // get tags, if site-wide then don't specify weblog
            tags = emgr.getTags(siteWide ? null : weblog, null, prefix, page * MAX, MAX + 1);

        } catch (WebloggerException we) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ERROR fetching tags");
            return;
        }

        if ("json".equals(format)) {
            response.setContentType("application/json; charset=utf-8");
            PrintWriter pw = response.getWriter();
            pw.println("{ \"prefix\": \"" + (prefix == null ? "" : StringEscapeUtils.escapeJson(prefix)) + "\",");
            pw.println("  \"weblog\": \"" + (!siteWide ? weblog.getHandle() : "") + "\",");
            pw.println("  \"tagcounts\": [" );
            int count = 0;
            for (Iterator<TagStat> it = tags.iterator(); it.hasNext();) {
                TagStat stat = it.next();
                pw.print("    { \"tag\" : \"");
                pw.print(stat.getName());
                pw.print("\", ");
                pw.print("\"count\" : ");
                pw.print(stat.getCount());
                pw.print(" }");
                if (it.hasNext()) {
                    pw.println(", ");
                }
                if (count++ > MAX) {
                    break;
                }
            }
            pw.println("\n  ]\n}");
            response.flushBuffer();
            
        } else if ("xml".equals(format)) {
            URLStrategy urlstrat = roller.getUrlStrategy();
            response.setContentType("application/tagdata+xml; charset=utf-8");
            PrintWriter pw = response.getWriter();
            pw.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); 
            pw.println("<categories fixed=\"no\" "); 
            pw.println("   xmlns=\"http://www.w3.org/2007/app\""); 
            pw.println("   xmlns:atom=\"http://www.w3.org/2005/Atom\""); 
            pw.println("   xmlns:tagdata=\"http://roller.apache.org/ns/tagdata\">");
            int count = 0;
            for (TagStat stat : tags) {
                String term = stat.getName();
                // gWCURL fields: weblog, locale, category, dateString, tags, pageNum, absolute
                String viewURI = urlstrat.getWeblogCollectionURL(weblog,
                        null, null, null,
                        Collections.singletonList(stat.getName()),
                        0, true);
                int frequency = stat.getCount();
                pw.print("<atom:category term=\"" + term + "\" tagdata:frequency=\"" + frequency + "\" ");
                pw.println("tagdata:href=\"" + StringEscapeUtils.escapeXml10(viewURI) + "\" />");
                if (count++ > MAX) {
                    break;
                }
            }
            if (tags.size() > MAX) {
                // get next URI, if site-wide then don't specify weblog
                String nextURI = urlstrat.getWeblogTagsJsonURL(weblog, true, page + 1);
                pw.println("<atom:link rel=\"next\" href=\"" + StringEscapeUtils.escapeXml10(nextURI) + "\" />");
            }
            if (page > 0) {
                // get prev URI, if site-wide then don't specify weblog
                String prevURI = urlstrat.getWeblogTagsJsonURL(weblog, true, page - 1);
                pw.println("<atom:link rel=\"previous\" href=\"" + StringEscapeUtils.escapeXml10(prevURI) + "\" />");
            }
            pw.println("</categories>");
            response.flushBuffer();
        }
    }
}

