/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */
package thinstock;

import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import ilog.views.chart.IlvChart;
import ilog.views.chart.IlvDataInterval;
import ilog.views.chart.IlvDataWindow;
import ilog.views.chart.IlvDoublePoints;
import ilog.views.chart.data.IlvDataPoints;
import ilog.views.chart.data.IlvDataSetPoint;
import ilog.views.chart.faces.servlet.IlvFacesChartServlet;
import ilog.views.chart.faces.servlet.IlvFacesChartServletSupport;
import ilog.views.chart.servlet.IlvChartServerAction;
import ilog.views.chart.servlet.IlvChartServerActionEvent;
import ilog.views.chart.servlet.IlvChartServletSupport;
import ilog.views.chart.servlet.IlvDefaultIMapAttributes;
import ilog.views.chart.servlet.IlvDefaultIMapDefinition;
import ilog.views.chart.servlet.IlvIMapArea;
import ilog.views.chart.servlet.IlvIMapAttributes;
import ilog.views.chart.servlet.IlvIMapDefinition;
import ilog.views.chart.servlet.IlvJPEGEncoder;
import ilog.views.chart.servlet.IlvPanAction;
import ilog.views.chart.servlet.IlvParameterException;
import ilog.views.chart.servlet.IlvServletRequestParameters;
import ilog.views.chart.servlet.IlvZoomAction;
import ilog.views.util.IlvResourceUtil;
import ilog.views.util.servlet.IlvServletPageIdUtil;
import stock.ExponentAxisTransformer;
import stock.StockUtil;
import stock.ThresholdLines;

/**
 * A <code>ChartServletSupport</code> subclass to display stock quotes in DHTML
 * views.
 */
class StockDemoSupport extends IlvFacesChartServletSupport {

  public static final String LOAD_QUOTES_ACTION = "LoadQuotesAction";

  public static final String LOWER_INDICATOR_ACTION = "SetLowerIndicatorAction";

  public static final String UPPER_INDICATOR_ACTION = "SetUpperIndicatorAction";

  public static final String CHART_PARAM = "chart";

  public static final String MAIN_CHART = "main";

  public static final String LOWER1_CHART = "lower1";

  public static final String LOWER2_CHART = "lower2";

  // A ServerAction to load quotes
  final class LoadQuotesServerAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent e) throws ServletException {
      StockDemoApp demo = getApp(e.getRequest());
      String symbol = e.getStringParameter(0);
      Date from = new Date(e.getLongParameter(1));
      Date to = new Date(e.getLongParameter(2));
      int freq = e.getIntParameter(3);
      int ns = e.getParameterCount() - 4;
      String[] secsymbols = null;
      if (ns > 0) {
        secsymbols = new String[ns];
        for (int i = 0; i < ns; ++i)
          secsymbols[i] = e.getStringParameter(4 + i);
      }
      demo.loadQuotes(symbol, from, to, freq, secsymbols);
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.REQUEST_THREAD;
    }
  }

  // A ServerAction to set an indicator on a lower chart
  final class LowerIndicatorServerAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent event) throws ServletException {
      StockDemoApp demo = getApp(event.getRequest());
      int indicatorIdx = event.getIntParameter(0);
      // int chartIdx = event.getIntParameter(1);
      String chartId = event.getStringParameter(1);
      int chartIdx;
      // if (LOWER1_CHART.equals(chartId))
      // chartIdx = 0;
      // else
      // chartIdx = 1;
      String nb = chartId.substring(5);
      chartIdx = Integer.parseInt(nb) - 1;
      demo.setLowerIndicator(chartIdx, indicatorIdx);
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.REQUEST_THREAD;
    }
  }

  // A ServerAction to set an indicator on the main chart
  final class UpperIndicatorServerAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent event) throws ServletException {
      StockDemoApp demo = getApp(event.getRequest());
      int[] indicators = new int[event.getParameterCount()];
      for (int i = 0; i < event.getParameterCount(); ++i)
        indicators[i] = event.getIntParameter(i);
      demo.setUpperIndicators(indicators);
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.REQUEST_THREAD;
    }
  }

  // A ServerAction to change the type of the overview representation
  final class SetOverviewRepAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent evt) throws ServletException {
      StockDemoApp demo = getApp(evt.getRequest());
      demo.setOverviewRepresentation(evt.getIntParameter(0));
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.REQUEST_THREAD;
    }
  }

  // A ServerAction to adjust threshold lines.
  static final class SetThresholdAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent evt) throws ServletException {
      if (ThresholdLines.get(evt.getChart(), 0) == null)
        return;
      Point upperPt = new Point((int) evt.getFloatParameter(0), (int) evt.getFloatParameter(1));
      Point lowerPt = new Point((int) evt.getFloatParameter(2), (int) evt.getFloatParameter(3));
      IlvChart chart = evt.getChart();
      try {
        upperPt = IlvChartServletSupport.toChartArea(upperPt, new IlvServletRequestParameters(evt.getRequest()), chart);
        lowerPt = IlvChartServletSupport.toChartArea(lowerPt, new IlvServletRequestParameters(evt.getRequest()), chart);
      } catch (IlvParameterException e) {
        throw new ServletException(e.getMessage());
      }
      IlvDoublePoints pts = new IlvDoublePoints(2);
      pts.add(lowerPt.x, lowerPt.y);
      pts.add(upperPt.x, upperPt.y);
      chart.toData(pts);
      ThresholdLines.get(evt.getChart(), 0)
          .setThresholds(new IlvDataInterval(Math.floor(pts.getY(0)), Math.floor(pts.getY(1))));
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.SWING_EVENT_THREAD;
    }
  }

  // A ServerAction to toggle the threshold lines visibility.
  final class ToggleThresholdAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent evt) throws ServletException {
      StockDemoApp app = getApp(evt.getRequest());
      app.setThreshold(evt.getIntParameter(0) == 1 ? true : false);
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.REQUEST_THREAD;
    }
  }

  // A ServerAction to set/unset a Logarithmic Price Axis.
  static final class ToggleLogScaleAction implements IlvChartServerAction {

    Override
    public void actionPerformed(IlvChartServerActionEvent evt) throws ServletException {
      if (evt.getIntParameter(0) == 1)
        evt.getChart().getYAxis(0).setTransformer(new ExponentAxisTransformer(.5));
      else
        evt.getChart().getYAxis(0).setTransformer(null);
    }

    Override
    public int getExecutionThread() {
      return IlvChartServerAction.REQUEST_THREAD;
    }
  }

  // --------------------------------------------------------------------------
  // IMapAttributes subclasses
  // --------------------------------------------------------------------------

  /**
   * A IlvDefaultIMapAttributes subclass that generates map area tags for a
   * detailed renderer.
   */
  static final class StockDemoIMapAttributes extends IlvDefaultIMapAttributes {

    private int ptidx;

    private int pos;

    private StockDemoApp app;

    public StockDemoIMapAttributes(StockDemoApp app, int pos, int ptIdx) {
      super("javascript:void(0)");
      this.ptidx = ptIdx;
      this.pos = pos;
      this.app = app;
    }

    Override
    public String getExtraAttributes() {
      StringBuffer buffer = new StringBuffer("onMouseOver=\"");
      buffer.append("highlightPoint(");
      buffer.append(pos);
      buffer.append(",");
      buffer.append(app.getStockDataSource().getDate(ptidx).getTime());
      buffer.append(",");
      buffer.append(app.getStockDataSource().getOpenDataSet().getYData(ptidx));
      buffer.append(",");
      buffer.append(app.getStockDataSource().getHighDataSet().getYData(ptidx));
      buffer.append(",");
      buffer.append(app.getStockDataSource().getLowDataSet().getYData(ptidx));
      buffer.append(",");
      buffer.append(app.getStockDataSource().getCloseDataSet().getYData(ptidx));
      buffer.append(");return true;\" onMouseOut=\"");
      buffer.append("unhighlightPoint();return true;\"");
      return buffer.toString();
    }
  }

  /**
   * An IlvDefaultIMapAttributes subclass that generates map area tags for the
   * lower charts.
   */
  static final class LowerIndicatorIMapAttributes extends IlvDefaultIMapAttributes {

    private String caption;

    private String date;

    private String value;

    public LowerIndicatorIMapAttributes(String date, String value, String caption) {
      super("javascript:void(0)");
      this.caption = caption;
      this.value = value;
      this.date = date;
    }

    Override
    public String getExtraAttributes() {
      StringBuffer buffer = new StringBuffer("onmouseover=\"return tooltipMouseOver(event, '");
      buffer.append("<b>Date:</b>");
      buffer.append(date);
      buffer.append("<br/><b>Value:</b>");
      buffer.append(value);
      buffer.append("','");
      buffer.append(caption);
      buffer.append("');\" onmouseout=\"return tooltipMouseOut();\"");
      return buffer.toString();
    }
  }

  // ---------------------------------------------------------------------------

  private static DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT);

  /**
   * Initializes a new <code>StockDemo</code>.
   */
  public StockDemoSupport(ServletConfig config) {
    ((IlvJPEGEncoder) getImageEncoder(JPEG_IMAGE_FORMAT)).setJPEGQuality(1.0f);
    installServerActions();
  }

  Override
  public void setServlet(HttpServlet servlet) {
    super.setServlet(servlet);
    setParameterValidationListener(new ChartStockParameterValidationListener(servlet.getServletContext()));
  }

  /**
   * Install server actions.
   */
  private void installServerActions() {
    addServerAction(LOAD_QUOTES_ACTION, new LoadQuotesServerAction());
    addServerAction(LOWER_INDICATOR_ACTION, new LowerIndicatorServerAction());
    addServerAction(UPPER_INDICATOR_ACTION, new UpperIndicatorServerAction());
    addServerAction("ZoomAction", new IlvZoomAction());
    addServerAction("PanAction", new IlvPanAction());
    addServerAction("SetOverviewRepAction", new SetOverviewRepAction());
    addServerAction("SetThresholdAction", new SetThresholdAction());

    addServerAction("ToggleThresholdAction", new ToggleThresholdAction());
    addServerAction("ToggleLogScaleAction", new ToggleLogScaleAction());
  }

  // --------------- IlvChartServletSupport overrided methods -----------------

  /**
   * Allows you to prepare the chart content before the presentation is
   * generated. This method is invoked on the AWT event dispatch thread. This
   * default implementation does nothing.
   * 
   * @param params
   *          The parameters parsed from the HTTP request.
   * @param chart
   *          The chart returned by {@link #getChart}.
   */
  Override
  protected void prepareChart(IlvServletRequestParameters params, IlvChart chart) throws ServletException {
    super.prepareChart(params, chart);
    String[] actionParams = null;
    try {
      actionParams = params.getStringArray(ACTION_PARAM, ",()");
    } catch (IlvParameterException e) {
      return;
    }
    String actionName = actionParams[0];
    if (LOAD_QUOTES_ACTION.equals(actionName)) {
      // data have been loaded in the request thread by the LoadQuotes server
      // action
      int ns = actionParams.length - 5;
      String[] secsymbols = null;
      if (ns > 0) {
        secsymbols = new String[ns];
        for (int i = 0; i < ns; ++i)
          secsymbols[i] = actionParams[5 + i];
      }
      getApp(params.getServletRequest()).primaryDataLoaded(actionParams[1], secsymbols);
    }
  }

  Override
  protected IlvChart getChart(IlvServletRequestParameters params) throws javax.servlet.ServletException {
    String chartType = params.getString(CHART_PARAM, MAIN_CHART);
    IlvChart chart = null;
    StockDemoApp demo = getApp(params.getServletRequest());
    if (demo == null) {
      String msg = IlvResourceUtil.getCurrentLocaleString(StockDemo.class, "notRetriveAppInstance");
      throw new ServletException(msg);
    }
    if (chartType.equals(MAIN_CHART))
      chart = demo.getMainChart();
    else if (chartType.equals(LOWER1_CHART))
      chart = demo.getLowerChart(0);
    else if (chartType.equals(LOWER2_CHART))
      chart = demo.getLowerChart(1);

    if (chart == null) {
      String msg = IlvResourceUtil.getCurrentLocaleString(StockDemo.class, "unknownChartType");
      throw new ServletException(msg);
    }
    return chart;
  }

  /**
   * Returns the requested visible window. The default implementation returns
   * the current visible window updated according to the xMin, xMax, yMin and
   * yMax optional request parameters.
   */
  Override
  protected IlvDataWindow getRequestedVisibleWindow(IlvServletRequestParameters params, IlvChart chart)
      throws ServletException {
    IlvDataWindow dw = super.getRequestedVisibleWindow(params, chart);
    if (params.getString(REQUEST_PARAM, IMAGE_REQUEST).equals(IMAGE_REQUEST)
        && params.getString(CHART_PARAM, MAIN_CHART).equals(MAIN_CHART))
      dw.yRange = StockUtil.getYDataRange(chart, 0, dw.xRange);
    return dw;
  }

  // Fix JV-4108
  Override
  protected Map<String, Object> getAdditionalCapabilities(IlvChart chart, IlvServletRequestParameters params) {

    Map<String, Object> addCapabilities = new HashMap<String, Object>();
    boolean resetForms = false;
    HttpServletRequest request = params.getServletRequest();
    synchronized (request.getSession(false)) {
      if (StockDemo.getSessionAttribute(request, StockDemo.NEW_SESSION_ATT) != null) {
        resetForms = true;
        StockDemo.setSessionAttribute(request, StockDemo.NEW_SESSION_ATT, null);
      }
    }
    addCapabilities.put("resetForms", resetForms);

    if (params.getString(CHART_PARAM, MAIN_CHART).equals(MAIN_CHART)) {
      ThresholdLines threshold = ThresholdLines.get(chart, 0);
      StockDemoApp app;
      try {
        app = getApp(params.getServletRequest());

        addCapabilities.put("detailedViewVisible", Boolean.valueOf(!app.isOverviewVisible()));
        addCapabilities.put("symbolFound", app.getStockDataSource().getSymbol());

        if (threshold != null) {
          Rectangle r = threshold.getUpperAnnotation().getBounds(null).getBounds();
          try {
            r = toRefComp(r, params, chart);
          } catch (IlvParameterException e) {
            // Logger.getLogger("thinstock").log(Level.SEVERE,
            // "An error occurred during additional capabilities generation");
            // e.printStackTrace() ;
            throw new ServletException(e.getMessage());
          }
          Map<String, Object> objMap = new HashMap<String, Object>();
          objMap.put("left", r.x);
          objMap.put("top", r.y);
          objMap.put("width", r.width);
          objMap.put("height", r.height);
          addCapabilities.put("upperAnnoBounds", objMap);
          r = threshold.getLowerAnnotation().getBounds(null).getBounds();
          try {
            r = toRefComp(r, params, chart);
          } catch (IlvParameterException e) {
            // Logger.getLogger("thinstock").log(Level.SEVERE,
            // "An error occurred during additional capabilities generation");
            throw new ServletException(e.getMessage());
          }
          objMap = new HashMap<String, Object>();
          objMap.put("left", r.x);
          objMap.put("top", r.y);
          objMap.put("width", r.width);
          objMap.put("height", r.height);
          addCapabilities.put("lowerAnnoBounds", objMap);
        }
      } catch (ServletException e1) {
        // Logger.getLogger("thinstock").log(Level.SEVERE, "An error occurred
        // during additional capabilities generation");
        e1.printStackTrace();
      }

    }
    return addCapabilities;
  }

  /**
   * Allows you to add additional capabilities to a capability request when the
   * mime format is "text/html". This default implementation does nothing.
   * 
   * @param chart
   *          The chart returned by {@link #getChart getChart}.
   * @param params
   *          The parameters parsed from the current HTTP request.
   * @param writer
   *          The servlet print writer for writing additional text/html
   *          capabilities.
   */
  // protected void additionalTextCapabilities(IlvChart chart,
  // IlvServletRequestParameters params, PrintWriter out)
  // throws IOException, ServletException {
  // boolean resetForms = false;
  // HttpServletRequest request = params.getServletRequest();
  // synchronized (request.getSession(false)) {
  // if (StockDemo.getSessionAttribute(request, StockDemo.NEW_SESSION_ATT) !=
  // null) {
  // resetForms = true;
  // StockDemo.setSessionAttribute(request, StockDemo.NEW_SESSION_ATT, null);
  // }
  // }
  // out.println("var resetForms=" + resetForms);
  // if (params.getString(CHART_PARAM, MAIN_CHART).equals(MAIN_CHART)) {
  // ThresholdLines threshold = ThresholdLines.get(chart, 0);
  // StockDemoApp app = getApp(params.getServletRequest());
  // out.println("var detailedViewVisible=" + !app.isOverviewVisible());
  // out.println("var symbolFound='" + app.getStockDataSource().getSymbol() +
  // "';");
  // if (threshold != null) {
  // Rectangle r = null;
  // try {
  // r = threshold.getUpperAnnotation().getBounds(null).getBounds();
  // } catch (Exception e) {
  // e.printStackTrace() ;
  // }
  // try {
  // r = toRefComp(r, params, chart);
  // } catch (IlvParameterException e) {
  // throw new ServletException(e.getMessage());
  // }
  // out.println("var upperAnnoBounds=new Object();");
  // out.println("upperAnnoBounds.left=" + r.x);
  // out.println("upperAnnoBounds.top=" + r.y);
  // out.println("upperAnnoBounds.width=" + r.width);
  // out.println("upperAnnoBounds.height=" + r.height);
  // r = threshold.getLowerAnnotation().getBounds(null).getBounds();
  // try {
  // r = toRefComp(r, params, chart);
  // } catch (IlvParameterException e) {
  // throw new ServletException(e.getMessage());
  // }
  // out.println("var lowerAnnoBounds=new Object();");
  // out.println("lowerAnnoBounds.left=" + r.x);
  // out.println("lowerAnnoBounds.top=" + r.y);
  // out.println("lowerAnnoBounds.width=" + r.width);
  // out.println("lowerAnnoBounds.height=" + r.height);
  // }
  // }
  // }

  /**
   * Allows you to add additional capabilities to a capability request when the
   * mime format is "application/octet-stream". This default implementation does
   * nothing.
   * 
   * @param chart
   *          The chart returned by {@link #getChart getChart}.
   * @param params
   *          The parameters parsed from the current HTTP request.
   * @param dataStream
   *          The servlet data output stream for writing additional binary
   *          capabilities.
   */
  // protected void additionalBinaryCapabilities(IlvChart chart,
  // IlvServletRequestParameters params, DataOutputStream dout)
  // throws IOException, ServletException {
  // boolean resetForms = false;
  // HttpServletRequest request = params.getServletRequest();
  // synchronized (request.getSession(false)) {
  // if (StockDemo.getSessionAttribute(request, StockDemo.NEW_SESSION_ATT) !=
  // null) {
  // resetForms = true;
  // StockDemo.setSessionAttribute(request, StockDemo.NEW_SESSION_ATT, null);
  // }
  // }
  // dout.writeBoolean(resetForms);
  // if (params.getString(CHART_PARAM, MAIN_CHART).equals(MAIN_CHART)) {
  // ThresholdLines threshold = ThresholdLines.get(chart, 0);
  // StockDemoApp app = getApp(params.getServletRequest());
  // dout.writeBoolean(!app.isOverviewVisible());
  // dout.writeUTF(app.getStockDataSource().getSymbol());
  // dout.writeBoolean(params.getBoolean(StockDemo.NEW_SESSION_ATT, false));
  // if (threshold != null) {
  // Rectangle r = threshold.getUpperAnnotation().getBounds(null).getBounds();
  // try {
  // r = toRefComp(r, params, chart);
  // } catch (IlvParameterException e) {
  // throw new ServletException(e.getMessage());
  // }
  // dout.writeInt(r.x);
  // dout.writeInt(r.y);
  // dout.writeInt(r.width);
  // dout.writeInt(r.height);
  // r = threshold.getLowerAnnotation().getBounds(null).getBounds();
  // try {
  // r = toRefComp(r, params, chart);
  // } catch (IlvParameterException e) {
  // throw new ServletException(e.getMessage());
  // }
  // dout.writeInt(r.x);
  // dout.writeInt(r.y);
  // dout.writeInt(r.width);
  // dout.writeInt(r.height);
  // }
  // }
  // }

  /**
   * Writes the image map area tags into the specified writer. This method is
   * invoked on the AWT event dispatch thread during the image map generation
   * process to generate the area tags.
   * 
   * @param output
   *          The output writer.
   * @param params
   *          The parameters parsed from the request.
   * @param chart
   *          The <code>IlvChart</code> for the specified request.
   */
  Override
  protected void generateMapAreaTags(PrintWriter output, IlvServletRequestParameters params, IlvChart chart)
      throws IOException {
    super.generateMapAreaTags(output, params, chart);

    String chartName = params.getString(CHART_PARAM, null);
    if (chartName == null)
      return;
    Rectangle plotRect = chart.getChartArea().getPlotRect();
    try {
      plotRect = toRefComp(plotRect, params, chart);
    } catch (IlvParameterException e) {
    }

    // Generate "background area" to forward events
    IlvIMapAttributes attr = null;
    if (chartName.equals(MAIN_CHART)) {
      attr = new IlvDefaultIMapAttributes("javascript:void(0)") {

        Override
        public String getExtraAttributes() {
          // Fix JV-5629 for FF
          return "onmousedown=\"mapMouseDown(event);return false;\" onmouseup=\"mapMouseUp(event);return false;\" onmousemove=\"mapMouseMove(event);return false;\" onmouseover=\"window.status=' ';return true;\"";
        }
      };
    }

    if (attr != null) {
      IlvIMapArea area = new IlvIMapArea(plotRect, attr);
      output.println(area.getTag());
    }
  }

  /**
   * Returns the <code>IlvIMapDefinition</code> used by the
   * <code>IlvImageMapBuilder</code> to generate the image map tags.
   */
  Override
  protected IlvIMapDefinition getIMapDefinition(IlvServletRequestParameters params, IlvChart chart) {
    String chartName = params.getString(CHART_PARAM, null);
    if (chartName == null)
      return null;
    IlvIMapDefinition mapDef = null;
    StockDemoApp app;
    try {
      app = getApp(params.getServletRequest());
    } catch (ServletException e) {
      return null;
    }
    if (chartName.equals(MAIN_CHART)) {
      IlvDataPoints pts = app.getDetailedDataSet().getDataInside(chart.getCoordinateSystem(0).getVisibleWindow(), 0,
          false);
      IlvDataSetPoint[] dspts = new IlvDataSetPoint[pts.size()];
      IlvIMapAttributes[] attrs = new IlvIMapAttributes[pts.size()];
      for (int i = 0; i < attrs.length; ++i) {
        attrs[i] = new StockDemoIMapAttributes(app, i, pts.getIndex(i));
        dspts[i] = new IlvDataSetPoint(app.getDetailedDataSet(), pts.getIndex(i));
      }
      pts.dispose();
      try {
        mapDef = new IlvDefaultIMapDefinition(dspts, attrs);
      } catch (IllegalArgumentException e) {
        String msg = IlvResourceUtil.getServerLocaleString(StockDemo.class, "notCreateMapDefinition");
        System.out.println(msg + e.getMessage());
      }
    } else {
      int chartIdx = chartName.equals(LOWER1_CHART) ? 0 : 1;
      IlvDataPoints pts = app.getLowerIndicatorDataSet(chartIdx)
          .getDataInside(chart.getCoordinateSystem(0).getVisibleWindow(), 0, false);
      if (pts != null) {
            IlvDataSetPoint[] dspts = new IlvDataSetPoint[pts.size()];
            IlvIMapAttributes[] attrs = new IlvIMapAttributes[pts.size()];
            for (int i = 0; i < attrs.length; ++i) {
              String date = dateFormat.format(app.getStockDataSource().getDate(pts.getIndex(i)));
              String label = chart.getYScale(0)
                  .computeLabel(app.getLowerIndicatorDataSet(chartIdx).getYData(pts.getIndex(i)));
              attrs[i] = new LowerIndicatorIMapAttributes(date, label, app.getLowerIndicatorName(chartIdx));
              dspts[i] = new IlvDataSetPoint(app.getLowerIndicatorDataSet(chartIdx), pts.getIndex(i));
            }
            pts.dispose();
            
        try {
          mapDef = new IlvDefaultIMapDefinition(dspts, attrs);
        } catch (IllegalArgumentException e) {
          String msg = IlvResourceUtil.getServerLocaleString(StockDemo.class, "notCreateMapDefinition");
          System.out.println(msg + e.getMessage());
        }
      }
    }
    return mapDef;
  }

  // --------------------------------------------------------------------------

  private StockDemoApp getApp(HttpServletRequest request) throws ServletException {
    HttpSession session = request.getSession();
    if (session != null) {
      StockDemoApp app = null;
      synchronized (session) {
        app = (StockDemoApp) StockDemo.getSessionAttribute(request, StockDemo.APP_SESSION_KEY);
        if (app == null) {
          app = ((StockDemo) getServlet()).createApp();
          StockDemo.setSessionAttribute(request, StockDemo.APP_SESSION_KEY, app);
        }
      }
      if (app != null)
        return app;
    }
    String msg = IlvResourceUtil.getCurrentLocaleString(StockDemo.class, "notRetrieveAppFromSession");
    throw new ServletException(msg);
  }
}

class FacesStockDemoSupport extends StockDemoSupport {

  public FacesStockDemoSupport(ServletConfig conf) {
    super(conf);
  }

  Override
  protected void prepareChart(IlvServletRequestParameters params, IlvChart chart) throws ServletException {
  }
}

/**
 * A simple application that displays stock information. The sample loads quotes
 * from the Yahoo finance Web pages and displays them in two distinct charts on
 * which you can perform interactions. The displayed information is the stock
 * prices and the exchanged volume. The data is retrieved by sending a formatted
 * <i>cgi query </i> to the Yahoo Web site, and the result is decoded using a
 * custom <code>IlvDataReader</code> object. This reader (an instance of
 * <code>CSVDataReader</code>) extracts the data from the input stream and
 * initializes the data source with the corresponding data sets.
 */

public class StockDemo extends IlvFacesChartServlet {

  public static final String APP_SESSION_KEY = "StockDemoApp";

  public static final String NEW_SESSION_ATT = "thinstock_New_Session";

  // --------------- Support for multiple browser pages (tabs) ----------------

  /**
   * This method allows support for multiple browser pages to this application
   * as it retrieves the session attributes using a page identifier that is
   * unique to the page accessing this application. Different pages will have
   * different identifiers.
   * 
   * @param request
   *          The request associated with the session
   * @param attributeKey
   *          The attribute key to access the attribute value
   * @return The attribute value, if any.
   */
  public static Object getSessionAttribute(HttpServletRequest request, String attributeKey) {
    // Since the 'request' is already known, class IlvServletPageIdUtil
    // is used. Otherwise, class IlvFacesPageIdUtil is able to retrieve the
    // session directly from the FacesContext
    return IlvServletPageIdUtil.getSessionAttributeWithPageId(request, attributeKey);
  }

  /**
   * This method allows support for multiple browser pages to this application
   * as it stores the session attributes using a page identifier that is unique
   * to the page accessing this application. Different pages will have different
   * identifiers.
   * 
   * If the given value is null, the attribute is removed from the session.
   * 
   * @param request
   *          The request associated with the session
   * @param attributeKey
   *          The attribute key to access the attribute value
   * @param attributeValue
   *          The attribute value to be stored in the session, or
   */
  static void setSessionAttribute(HttpServletRequest request, String attributeKey, Object attributeValue) {
    // Since the 'request' is already known, class IlvServletPageIdUtil
    // is used. Otherwise, class IlvFacesPageIdUtil is able to retrieve the
    // session directly from the FacesContext
    IlvServletPageIdUtil.setSessionAttributeWithPageId(request, attributeKey, attributeValue);
  }

  /**
   * Creates an <code>IlvChartServletSupport</code> instance. This method is
   * called by the {link #getServletSupport} method when the support has not
   * been created. This method should be overridden to provide a default
   * implementation.
   */
  Override
  protected IlvChartServletSupport createServletSupport() {
    return new FacesStockDemoSupport(getServletConfig());
  }

  /**
   * Prepares a session for the client. This method is called at every request
   * before any other method to allow the user to prepare a session for his
   * servlet. The actual implementation is empty, but if your servlet needs to
   * create a session you can overwrite this method to do it.
   * 
   * @param request
   *          The current HTTP request.
   * @exception ServletException
   *              If the request for the GET could not be handled.
   */
  Override
  protected void prepareSession(HttpServletRequest request) throws ServletException {
    HttpSession session = request.getSession(true);
    synchronized (session) {
      StockDemoApp demo = (StockDemoApp) StockDemo.getSessionAttribute(request, APP_SESSION_KEY);
      if (null == demo) {
        // new page in session
        demo = createApp();
        StockDemo.setSessionAttribute(request, APP_SESSION_KEY, demo);
        StockDemo.setSessionAttribute(request, NEW_SESSION_ATT, new Object());
      }
    }
  }

  /**
   * Create a new <code>StockDemoApp</code> instace.
   */
  protected StockDemoApp createApp() {
    return new FacesStockDemoApp(getServletConfig());
  }

  Override
  protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
    // System.out.println("---> " + arg0.getQueryString());
    HttpSession session = arg0.getSession(true);
    synchronized (session) {
      super.service(arg0, arg1);
    }
  }
}