/*  
 * Copyright 2006 T. Mark Cyster 
 *
 * Licensed 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
 */
package com.fortpoint.taglib.template;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import javax.servlet.jsp.JspException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletRequest;
import javax.servlet.jsp.PageContext;
import java.net.HttpURLConnection;
import javax.servlet.http.HttpServletRequest;

/** 
 * Capable of loading a jsp template (a jsp page containing template:put tags) 
 * and then writing the loaded template against a pageContext
 *
 * Understands the template as a set of fragments before each 
 * section in a template. See also the TemplateProcessor which
 * is used by ths class to load a jsp template
 *
 * @author Offermatica (T.M Cyster)
*/
class Template {
  private static final int HTTP_CODE_MIN_OK = HttpURLConnection.HTTP_OK;
  private static final int HTTP_CODE_MAX_OK = 299;

  private Map<String, String> params = new HashMap<String, String>();
  private String url;
  private Iterator<TemplateFragment> fragments = null;

  Template(String url) {
    this.url = url;
  }

  public void setParam(String name, String value) {
    if (StringUtils.isBlank(name) || StringUtils.isBlank(value)) {
    }
    params.put(name, value);
  }

  public void end(PageContext pageContext) throws JspException {
    putFragmentsUptoSection("", pageContext);
  }

  public void putFragmentsUptoSection(String name, PageContext pageContext)
    throws JspException {
    boolean found = false;
    try {
      while (getFragments(name, pageContext).hasNext()) {
        TemplateFragment fragment = getFragments(name, pageContext).next();
        pageContext.getOut().print(fragment.getContent());
        if (fragment.getName().equals(name)) {
          found = true;
          break;
        }
      }
      if (!found) {
        pageContext.getOut().flush();
        throw new JspException("Template '" 
         + url + "'does not contain put for section '" 
         + name + "' or sections are out of order.");
      }
    } catch (IOException e) {
      throw new JspException("Problem writing template");
    }
  }

  private Iterator<TemplateFragment> getFragments(String name, 
    PageContext pageContext) throws JspException {

    if (fragments != null) {
      return fragments;
    }

    ServletContext servletContext = pageContext.getServletContext();

    ServletRequest servletRequest = pageContext.getRequest();
    
    RequestDispatcher requestDispatcher = 
       servletContext.getRequestDispatcher(getTemplateUrl(pageContext));
        
    TemplateProcessor templateProcessor = new TemplateProcessor(
      (HttpServletResponse) pageContext.getResponse());
      
    TemplateBuilder builder = (TemplateBuilder) pageContext.getAttribute(
      "_templateBuilder", PageContext.REQUEST_SCOPE);

    pageContext.setAttribute("_templateBuilder", 
      (TemplateBuilder) templateProcessor, PageContext.REQUEST_SCOPE);

    TemplateResponse servletResponse = 
      templateProcessor.getHttpServletResponse();

    List<TemplateFragment> fragmentList;
    try {
      requestDispatcher.include(servletRequest, servletResponse);
      fragmentList = templateProcessor.end(pageContext);
    } catch (IOException e)  {
      throw new JspException(e);
    } catch (RuntimeException e) {
      throw new JspException(e);
    } catch (ServletException e) {
      Throwable rootCause = e.getRootCause();
      if (rootCause == null) {
        throw new JspException(e);
      } else {
        throw new JspException(rootCause);
      }
    }
    fragments = fragmentList.iterator();

    if (servletResponse.getStatus() < HTTP_CODE_MIN_OK || 
      servletResponse.getStatus() > HTTP_CODE_MAX_OK) {
      throw new JspException("Problem while retrieving template '" 
        + url + "' got status code: " + servletResponse.getStatus());
    }

    // I expected status code (above) to tell me if template not found - doesn't
    // so test if template has something in it
    long length = 0;
    for (TemplateFragment fragment : fragmentList) {
      length = length + fragment.getContent().length();
    }
    if (length == 0) {
      try {
        pageContext.getOut().flush();
      } catch (IOException e) {
        // ignore - just flushing page containing template
        // to help users debug their page
        // When users fix template problem if there is still a problem
        // here they will get this exception
      }
      throw new JspException("Template '" 
        + url + "' contained no content (template probably not found)");
    }

    if (builder != null) {
      pageContext.setAttribute("_templateBuilder", 
        builder, PageContext.REQUEST_SCOPE);
    } else {
      pageContext.removeAttribute("_templateBuilder", 
        PageContext.REQUEST_SCOPE);
    }

    return fragments;
  }

  private String getTemplateUrl(PageContext pageContext) {
    StringBuilder templateUrl = new StringBuilder(this.url);

    if (!this.url.startsWith("/")) {
      String servletPath = (
        (HttpServletRequest) pageContext.getRequest()).getServletPath();
      templateUrl.insert(0,
        servletPath.substring(0, servletPath.lastIndexOf('/') + 1));
     }

    String seperator = "?";
    if (templateUrl.indexOf(seperator) >= 0) {
      seperator = "&";
    }
    for (String name : params.keySet()) {
      templateUrl.append(seperator);
      templateUrl.append(name);
      templateUrl.append("=");
      templateUrl.append(params.get(name));
      seperator = "&";
    }

    System.out.println("templateUrl=" + templateUrl.toString());
    return templateUrl.toString();
  }
}

