/*
 * @(#)GraphModelBase.java
 *
 * Copyright 2001-2002 by Oracle Corporation,
 * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Oracle Corporation.
 */

package oracle.jbo.html.jsp.graph;

import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
import javax.servlet.jsp.JspException;

import javax.swing.SwingUtilities;

import java.util.StringTokenizer;

import oracle.dss.graph.Graph;

import oracle.jbo.Row;
import oracle.jbo.RowSetIterator;
import oracle.jbo.RowSet;

import oracle.jbo.html.jsp.datatags.Utils;


public abstract class GraphModelBase
	extends TagSupport
        implements GraphConstants
{
    
    protected final static String emptyString = "";

    private static ColumnCountHelper columnCountHelper;

    protected RowSetIterator iterator = null;

    protected String[] mDataValueAttrNames;
    protected String mDataValueAttrNamesAsString;

    protected String mSeriesLabelAttrName;
    
    protected String mColumnLabelsAsString;
    private String[] colLabels;

    private String mDataSourceName;

    public GraphModelBase()
    {
	   super();
    }

    abstract protected oracle.dss.util.DataSource getGraphDataSource();

    abstract protected void rsiChanged(RowSetIterator newRSI, GraphTagBase graphTag);

    abstract protected void refreshView();


    // helper method used by BIBeanDataAccessAdapter
    abstract protected String getColumnLabel(int i);
    
    abstract protected int getColumnCount();
        
    abstract protected String getRowLabel(int i);
    
    abstract protected long getRowCount();
        
    abstract protected Object getValue(int row, int col);

    /**
    * Process the start tag for this instance.
    *
    * The doStartTag() method assumes that all setter methods have been
    * invoked before.
    *
    * When this method is invoked, the body has not yet been invoked.
    *
    * @returns EVAL_BODY_INCLUDE if the tag wants to process body, SKIP_BODY if it
    * does ont want to process it.
    */
    public int doStartTag() throws JspException
    {
        
       logDebug("GraphMoldeBase : search for Graph tag");
       
       GraphTagBase  graphTag = null;

       Tag tag = super.findAncestorWithClass(this, 
		   GraphTagBase.class);

       if ( tag == null )
       {
    	   throw new JspException("This tag should be used within a Graph tag");
       }
       else
       {
           graphTag = (GraphTagBase)tag;
            
           Graph graph = graphTag.getGraph();
             
           logDebug("GraphMoldeBase : set datasource for graph");
       }
	
       oracle.jbo.html.DataSource ds = 
		Utils.getDataSourceFromContext(super.pageContext, getDatasource());

       logDebug("GraphMoldeBase : Datasource name : " + getDatasource());

       if ( ds == null )
       {
          StringBuffer str = new StringBuffer();
        
          str.append("Datasource ");
    
          str.append(getDatasource());
    
          str.append("not found");
       }
       else
       {
           logDebug("GraphMoldeBase : obtain rowset");
    
           RowSet rs = ds.getRowSet();
    
           rsiChanged(rs, graphTag);	
       }
       
       if ( graphTag != null )
       {
           Graph graph = graphTag.getGraph();
           
           graph.setDataSource(getGraphDataSource());
    
           refreshView();
       }
       

       return Tag.SKIP_BODY;
    }
 

    public void setDatasource(String dataSourceName)
    { 
	   this.mDataSourceName = dataSourceName;
    }

    public String getDatasource()
    {
	   return mDataSourceName;
    }

    public void setSeriesLabelAttrName(String seriesLabelAttrName)
    {
	   this.mSeriesLabelAttrName = seriesLabelAttrName;

    }

    public String getSeriesLabelAttrName()
    {
	   return mSeriesLabelAttrName;
    }

    public void setColumnLabels(String labels)
    {
        // TODO parse and change label to array
        // super.setColumnLabelsInternal(...);
        mColumnLabelsAsString = labels;
    
        String[] col = parse(mColumnLabelsAsString);
    
        setColumnLabelsInternal(col);
    }

    public String getColumnLabels()
    {
	   return 	mColumnLabelsAsString; 
    }

    public void setDataValueAttrNames(String name)
    {
        this.mDataValueAttrNamesAsString = name;
    
        String[] attrs = parse(mColumnLabelsAsString);
    
        setDataValueAttrNamesInternal(attrs);
    }

    public String getDataValueAttrNames()
    {
	   return mDataValueAttrNamesAsString;
    }


    protected void setDataValueAttrNamesInternal(String[] names)
    {
	   this.mDataValueAttrNames = names;
    }

    protected String[] getDataValueAttrNamesInternal()
    {
	   return mDataValueAttrNames;
    }

    protected void setColumnLabelsInternal(String[] labels)  
    {
	   this.colLabels = labels;
    }

    protected String[] getColumnLabelsInternal()
    {
	   return colLabels;
    }

    /**
    *  Constructor
    *
    *  @param iterBinding Supplies label for columns and data values for 
    *         the chart. Last column is treated as label.
    */
    public GraphModelBase(RowSetIterator iterator, 
            String[] dataValueAttrNames)
    {
        setRowSetIterator(iterator);

        setDataValueAttrNamesInternal(dataValueAttrNames);
    } 


    protected void setRowSetIterator(RowSetIterator iter)
    {
	   this.iterator = iter;
    }

    protected RowSetIterator getRowSetIterator()
    {
	   return iterator;
    }

    
    /**
     *  Returns the number of column values required to plot a datapoint. 
     *  For example, in the case of HLC graph, three values are needed per marker,
     *  or in the case of XY graph, two values are needed.
     *
     * @return number Value count per marker.
     */
     public static int getNumberOfColumnPerMarker(int graphType)
     {
         if (columnCountHelper == null)
            columnCountHelper = new ColumnCountHelper();
    
          return columnCountHelper.findNumberOfColumnsFor(graphType);
     }
    
     protected int rowIndexToRangeIndex(int rowIndex)
     {

         if (iterator != null)
         {
            int rangeStart = iterator.getRangeStart();

            if (rangeStart <= 0)
            {
               return rowIndex;
            }
         
            return rowIndex - rangeStart;
         }
         else
         {
            return rowIndex;
         }
    }

    public boolean isDirty()
    {
       return false;
    }

    protected Object getAttributeFromRow(int rangeIndex, int attrIndex)
    {
        Row row = getRowSetIterator().getRowAtRangeIndex(rangeIndex);
    
        return row.getAttribute(attrIndex);
    }

    protected Object getAttributeFromRow(int rangeIndex, String attrName)
    {
        Row row = getRowSetIterator().getRowAtRangeIndex(rangeIndex);
    
        return row.getAttribute(attrName);
    }

    
    protected void refreshBIBeanAdapter(final BIBeanDataAccessAdapter adapter)
    {	
        try
        {
            adapter.refresh();
        }
        catch(Exception exc)
        {
            System.err.println("refresh error occured in graph");
        }
    }

    
    /** 
    * Break comma seperated string to an array of strings 
    */
    protected String[] parse(String s)
    {
        StringTokenizer st  = new StringTokenizer(s,",", false);
    
        String[] parsedValues = new String[st.countTokens()];

        int i = 0;

        while (st.hasMoreTokens())
              parsedValues[i++] = st.nextToken();

        return parsedValues;
    }


    protected void logDebug(String s)
    {
	   //System.out.println("SBSK >> " + s);
    }
          
}

class ColumnCountHelper
{
	static MarkerDef[] defs = 
	{
		new MarkerDef(0,54, 1),  // 3d & 2d
		new MarkerDef(55,60, 1), // pie
		new MarkerDef(61,62, 2), // xyscatter
		new MarkerDef(63,64, 2), // xyscatter with label
		new MarkerDef(65,66, 2), // Polar charts
		new MarkerDef(67,69, 1), // Radar
		new MarkerDef(70,70, 4), // Stock chart SHLC
		new MarkerDef(71,71, 5), // Stock chart SHLVC
		new MarkerDef(72,72, 4), // Stock chart SC
		new MarkerDef(73,75, 2), // Stock chart HL
		new MarkerDef(76,78, 3), // Stock Chart three values per marker
		new MarkerDef(79,81, 4), // Stock chart SC - four values per marker
		new MarkerDef(82,82, 3), // Stock chart SC - four values per marker
		new MarkerDef(83,83, 5), // Stock chart - five values per marker
		new MarkerDef(84,84, 5), // Candle Stock chart - five values per marker
		new MarkerDef(85,85, 1), // Histogram chart
	};

	int findNumberOfColumnsFor(int graphType)
	{
	    for (int i=0; i < defs.length; i++)
	    {
		if (defs[i].isBelongsToRange(graphType))
		{
		    return defs[i].numValuesPerMarker;
		}
	     }
	     return 1;
	}

}

class MarkerDef
{
	int start;
	int end;
	int numValuesPerMarker;

	MarkerDef(int start, int end, int numValuesPerMarker)
	{
		this.start = start;
		this.end   = end;
		this.numValuesPerMarker = numValuesPerMarker;
	}

	boolean isBelongsToRange(int graphType)
	{
		if ((graphType > start)  && (graphType < end))
			return true;
		else if ((graphType == start) || (graphType == end))
			return true;

		return false;
	}
}





