package hirondelle.predict.pub.json;

import hirondelle.predict.main.lists.PredictionList;
import hirondelle.predict.main.lists.PredictionListDAO;
import hirondelle.predict.main.prediction.Prediction;
import hirondelle.predict.main.prediction.PredictionDAO;
import hirondelle.web4j.action.ActionImpl;
import hirondelle.web4j.action.ResponsePage;
import hirondelle.web4j.database.DAOException;
import hirondelle.web4j.database.SqlId;
import hirondelle.web4j.model.AppException;
import hirondelle.web4j.request.RequestParameter;
import hirondelle.web4j.request.RequestParser;
import hirondelle.web4j.util.Consts;
import hirondelle.web4j.util.Util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.logging.Logger;

/**
 Serve a given {@link PredictionList} in <a href='http://www.json.org/'>JSON</a> format.
 Such data would typically be consumed by javascript code, which would render the data 
 in some desired way.
 
 <P>See the <tt>/pub/JsonTest.jsp</tt> for an example of using javascript to call this 
 action, and render the data in a web page.
   
 @view /pub/view.json - this view can be reused by any other feature in the app 
 that needs to serve JSON.
*/
public final class ViewPublicListJsonAction  extends ActionImpl {

  /** Constructor. */
  public ViewPublicListJsonAction(RequestParser aRequestParser){
    super(JSON_RESPONSE, aRequestParser);
  }
  
  public static final RequestParameter LIST_ID = RequestParameter.withLengthCheck("ListId");
  public static final SqlId LIST_PREDICTIONS =  new SqlId("LIST_PREDICTIONS");

  /**
   Fetch a {@link PredictionList}, its {@link Prediction}s, and its average score, and place 
   them all in request scope. 
  */
  @Override public ResponsePage execute() throws AppException {
    //the response's content-type is set to application/json in the JSP, not here

    //fetch the underlying data in the usual way
    List<Prediction> predictions = fDAO.list(getIdParam(LIST_ID));
    Integer averageScore = Prediction.calculateAverageScore(predictions);
    PredictionList list = fetchPredictionList();
    
    //build a string containing your json data, out of the items fetched above 
    String json = formatAsJson(predictions, averageScore, list); //hard-coded result!; see below
    
    //add your json string to request scope, just like any other data
    //the key has to match the reference in the JSP (this example uses 'data' as the key)
    addToRequest(DATA, json);
    
    //forward to a dead-simple jsp (see constructor); it adds any headers you may need, usually 
    //content-type and encoding; the exact same page can be referenced any time you need 
    //to serve json data 
    return getResponsePage();
  }

  // PRIVATE 
  private PredictionDAO fDAO = new PredictionDAO();
  private static final Logger fLogger = Util.getLogger(ViewPublicListJsonAction.class);
  
  private PredictionList fetchPredictionList() throws DAOException {
    PredictionListDAO dao = new PredictionListDAO();
    PredictionList result = dao.fetchPublic(getIdParam(LIST_ID));
    fLogger.fine("Parent list: " + result);
    return result;
  }
  
  /*
    A reference to a single, simple JSP you have made for serving all of your json data.
    
    This style references a JSP relative to the root directory of your web app. That JSP 
    has a simple job: to accept a single object named 'data', and call its toString method. 
    
    <P>Using a JSP as a means of serving the json text lets you control headers and 
    such, using all the tools available in JSP's. Note that JSP's are designed to 
    serve ANY kind of text content you can think of: HTML, plain text, XML, 
    JSON - anything at all. So, there's nothing strange about using a JSP
    to help serve json data.
    
    <P>The style seen below references a file in the application's root directory. 
    That's the preferred style when more than one feature needs to return JSON data. If your 
    app serves a lot of JSON, you should define such a JSON_RESPONSE object in 
    a SINGLE place, and just reference it where you need to serve JSON data.
    
    <P>Note the suffix, '.json'. You need to add a simple servlet-mapping entry in web.xml, to tell
    Tomcat (or whatever your container is) that any .json files are to be served 
    with its usual, built-in JSP servlet.
  */
  private static final ResponsePage JSON_RESPONSE = new ResponsePage("/pub/view.json").setIsRedirect(false);
  
  /*
   Use this style if the JSP is in the same directory as this class (which is not the case here). 
   This style is preferred only when this class is the only feature serving JSON in the app.
  */
  //private static final ResponsePage FORWARD = new ResponsePage("view.json", ViewPublicListJsonAction.class);
  
  /**
   This implementation cheats: it returns hard-coded text. You would never 
   do this in a real application! 
   
   The general idea is that you would use your tool of choice for creating a 
   JSON string out of the given objects. How you do that is up to you; web4j itself 
   has no special help for you in this regard.
  */
  private String formatAsJson(List<Prediction> aPredictions, Integer aAverageScore, PredictionList aList){
    //the args are ignored; return hard-coded json text
    StringBuilder result = new StringBuilder();
    try {
      InputStream input = this.getClass().getResourceAsStream("hard-coded-json.txt"); 
      BufferedReader reader = new BufferedReader(new InputStreamReader(input));
      try {
        String line = null;
        while ((line = reader.readLine()) != null) {
          result.append(line + Consts.NEW_LINE);
        }      
      }
      finally {
        if (reader != null) reader.close();
      }
    }
    catch(IOException ex){
      ex.printStackTrace();
    }
    return result.toString();
  }
  
}