/**
 * Bobo Browse Engine - High performance faceted/parametric search implementation 
 * that handles various types of semi-structured data.  Written in Java.
 * 
 * Copyright (C) 2005-2006  John Wang
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * To contact the project administrators for the bobo-browse project, 
 * please go to https://sourceforge.net/projects/bobo-browse/, or 
 * send mail to owner@browseengine.com.
 */
package com.browseengine.bobo.servlet;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.json.JSONException;
import com.browseengine.bobo.api.BrowseException;
import com.browseengine.bobo.api.BrowseRequest;
import com.browseengine.bobo.api.BrowseResult;
import com.browseengine.bobo.impl.QueryProducer;
import com.browseengine.bobo.protobuf.BrowseProtobufConverter;
import com.browseengine.bobo.server.protocol.BoboHttpRequestParam;
import com.browseengine.bobo.server.protocol.BrowseJSONSerializer;
import com.browseengine.bobo.service.BrowseService;
import com.browseengine.bobo.service.BrowseServiceFactory;
import com.browseengine.bobo.service.util.XStreamDispenser;
import com.browseengine.solr.BoboRequestBuilder;
import com.thoughtworks.xstream.XStream;
public class BrowseServlet
  extends HttpServlet{
  
  /**
   * 
   */
  private static final long serialVersionUID = 1L;
  private static Logger logger=Logger.getLogger(BrowseServlet.class);
  
  private static class BoboDefaultQueryBuilder{
    BoboDefaultQueryBuilder(){
      
    }
    public Query parseQuery(String query, String defaultField) {
      try {
        return QueryProducer.convert(query,defaultField);
      } catch (ParseException e) {
        e.printStackTrace();
        return null;
      }
    }
    
     private static Pattern sortSep = Pattern.compile(",");
    public Sort parseSort(String sortSpec) {
      if (sortSpec==null || sortSpec.length()==0) return null;
        String[] parts = sortSep.split(sortSpec.trim());
        if (parts.length == 0) return null;
        SortField[] lst = new SortField[parts.length];
        for( int i=0; i<parts.length; i++ ) {
          String part = parts[i].trim();
          boolean top=true;
            
          int idx = part.indexOf( ' ' );
          if( idx > 0 ) {
            String order = part.substring( idx+1 ).trim();
          if( "desc".equals( order ) || "top".equals(order) ) {
            top = true;
          }
          else if ("asc".equals(order) || "bottom".equals(order)) {
            top = false;
          }
          else {
            throw new IllegalArgumentException("Unknown sort order: "+order);
          }
          part = part.substring( 0, idx ).trim();
          }
          else {
        throw new IllegalArgumentException("Missing sort order." );
          }
          
          if( "score".equals(part) ) {
            if (top) {
              // If thre is only one thing in the list, just do the regular thing...
              if( parts.length == 1 ) {
                return null; // do normal scoring...
              }
              lst[i] = SortField.FIELD_SCORE;
            }
            else {
              lst[i] = new SortField(null, SortField.SCORE, true);
            }
          } 
          else {
            lst[i] = new SortField(part,SortField.STRING,top);
          }
        }
        return new Sort(lst);
    }
    
    
  }
  
  private BrowseService _svc;
  public BrowseServlet() {
    super();
  }
  
  
  
  @Override
  public void init(ServletConfig config) throws ServletException {
    super.init(config);
    _svc=getServiceInstance(config);
  }
  protected BrowseService getServiceInstance(ServletConfig config) throws ServletException{
    String indexDir=System.getProperty("index.directory");
    if (indexDir==null || indexDir.length()==0){
      indexDir = getInitParameter("index.directory");
    }
    if (null == indexDir || indexDir.length() == 0)
      throw new ServletException("No index directory configured");
  
    try {
      return BrowseServiceFactory.createBrowseService(new File(indexDir));
    } catch (BrowseException e) {
      throw new ServletException(e.getMessage(),e.getCause());
    }
  }
  
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    SolrParams params = new BoboHttpRequestParam(req);
    String qstring = params.get(CommonParams.Q);
    String df = params.get(CommonParams.DF);
    String sortString = params.get(CommonParams.SORT);
    BoboDefaultQueryBuilder qbuilder = new BoboDefaultQueryBuilder();
    Query query = qbuilder.parseQuery(qstring, df);
    Sort sort = qbuilder.parseSort(sortString);
    BrowseRequest br = null; 
    try {
      br=BoboRequestBuilder.buildRequest(params,query,sort);
      logger.info("REQ: "+BrowseProtobufConverter.toProtoBufString(br));
      BrowseResult result=_svc.browse(br);
      res.setCharacterEncoding("UTF-8");
      Writer writer=res.getWriter();
      
      String outputFormat=req.getParameter("output");
      if ("json".equals(outputFormat)){
        try{
          String val=BrowseJSONSerializer.serialize(result);
          writer.write(val);
        }
        catch(JSONException je){
          throw new IOException(je.getMessage());
        }
      }
      else{
        XStream xstream=XStreamDispenser.getXMLXStream();
        writer.write(xstream.toXML(result));
      }
      
      
      
    } catch (BrowseException e) {
      throw new ServletException(e.getMessage(),e);
    }
  }
  
  @Override
  public void destroy() {
    super.destroy();
    try {
      _svc.close();
    } catch (BrowseException e) {
      logger.warn("Problem shutting down browse engine.",e);
    }
  }
}