/*ChartHelper.java

	Purpose:
		
	Description:
		
	History:
		Oct 18, 2010 2:27:40 PM     2010, Created by ashish

Copyright (C) 2010 Potix Corporation. All Rights Reserved.
 */
package org.zkoss.zssex.util;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.zkoss.chart.Axis;
import org.zkoss.chart.Legend;
import org.zkoss.chart.Point;
import org.zkoss.chart.Title;
import org.zkoss.chart.model.CategoryModel;
import org.zkoss.chart.model.ChartsModel;
import org.zkoss.chart.options3D.Options3D;
import org.zkoss.chart.plotOptions.PiePlotOptions;
import org.zkoss.chart.plotOptions.PlotOptions;
import org.zkoss.json.JSONObject;
//import org.zkoss.json.JavaScriptValue;
import org.zkoss.zss.ui.impl.JavaScriptValue; // ZSS-1179: so Serialize OK
import org.zkoss.util.logging.Log;
import org.zkoss.zss.model.*;
import org.zkoss.zss.model.SChart.BarDirection;
import org.zkoss.zss.model.SChart.ChartGrouping;
import org.zkoss.zss.model.SChart.ChartLegendPosition;
import org.zkoss.zss.model.SChart.ChartType;
import org.zkoss.zss.model.chart.SChartData;
import org.zkoss.zss.model.chart.SGeneralChartData;
import org.zkoss.zss.model.impl.sys.FormatEngineImpl;
import org.zkoss.zss.model.sys.format.FormatContext;
import org.zkoss.zss.model.sys.format.FormatEngine;
import org.zkoss.zss.model.sys.format.FormatResult;
import org.zkoss.zss.model.util.Strings;
import org.zkoss.zssex.ui.ZssCharts;

/**
 * Internal Use Only. Helper Utility class for handling Chart.
 * 
 * @author ashish
 * 
 */
public class HighchartsHelper {

	private static final Log logger = Log.lookup(HighchartsHelper.class.getName());
	private static final String DOUGHNUT_INNER_SIZE = "80%";
	private static ChartsModelManager chartsModelManager = new ChartsModelManager();

	/**
	 * Creates {@link org.zkoss.zul.Chart} based on the {@link SChart}
	 * 
	 * @param chartInfo
	 * @return the created {@link org.zkoss.zul.Chart} based on the
	 *         {@link SChart}
	 */
	public static ZssCharts createCharts(SChart chartInfo) {
		final ZssCharts chart = new ZssCharts();
		getMapper(chartInfo).doSetting(chart, chartInfo);
		
		// (ZKCHARTS-22) workaround: animation may cause zk charts rendering failure.
		chart.setAnimation(false);
		chart.getNavigation().getButtonOptions().setEnabled(false);
		
		return chart;
	}
	
	private static TypeMapper getMapper(SChart chartInfo) {
		try{
			return TypeMapper.valueOf(chartInfo.getType().toString());
		} catch (IllegalArgumentException e) {
			return TypeMapper.OTHER;
		} catch (NullPointerException e) {
			return TypeMapper.OTHER;
		}
	}
	
	public static void drawCharts(ZssCharts chart, SChart chartInfo) {
		// draw chart data
		ChartsModel model = chartsModelManager.getChartsModel(chartInfo);
		
		// draw chart view, like label, tooltip, unit of axis
		if(model != null)
			drawChartInner(chart, model, chartInfo);
	}
	
	/**
	 * Pre-set fonts of {@link Chart} upon configuration.
	 * 
	 * @param chart
	 */
	private static void drawChartInner(ZssCharts chart, ChartsModel model, SChart chartInfo) {
		chart.setTitle(getChartTitle(chartInfo));
		chart.setLegend(getLegen(model, chartInfo));
		chart.setModel(model);
		
		//ZSS-822
		AxisDrawer.drawAxis(chart, chartInfo);
		
		// set empty text to prevent from showing default label
		chart.getYAxis().getTitle().setText("");
		chart.getXAxis().getTitle().setText("");
		
		PiePlotOptions options = chart.getPlotOptions().getPie();
		options.getDataLabels().setEnabled(false);
		options.setShowInLegend(true);
	}

	private static Legend getLegen(ChartsModel model, SChart chartInfo) {
		Legend legend = new Legend();
		// need to reverse the legend elements for limitation in hightcharts
		// see implementation in CategoryModelSelector
		if(CategoryModel.class.isAssignableFrom(model.getClass()) 
				&& (chartInfo.getGrouping() == ChartGrouping.STACKED 
					|| chartInfo.getGrouping() == ChartGrouping.PERCENT_STACKED))
			legend.setReversed(true);
		
		String align = getLegendHorizontalAlign(chartInfo.getLegendPosition());
		legend.setAlign(align);
		// many labels in a horizontal order will make chart be too small.
		legend.setLayout(align.equals("center") ? "horizontal" : "vertical");
		legend.setVerticalAlign(getLegendVerticalAlign(chartInfo.getLegendPosition()));
		return legend;
	}
	
	//ZSS-822
	private static class AxisDrawer {
		
		// represent axis
		private final static String X = "x";
		private final static String Y = "y";
		private final static String LOW = "low";
		private final static String HIGH = "high";
		
		// represent value
		private final static String HIGHEST = "highest";
		private final static String LOWEST = "lowest";
		
		private interface AxisGetter{
			double getValue(Point p);
		}
		
		private static void drawAxis(ZssCharts chart, SChart chartInfo) {
			//ZSS-822
			switch (chartInfo.getType()) {
			case PIE:
			case DOUGHNUT:
			case BAR:
			case COLUMN:
			case LINE:
			case AREA:
				if (!chartInfo.getValueAxises().isEmpty() && 
						!(chartInfo.getGrouping() == ChartGrouping.STACKED || chartInfo.getGrouping() == ChartGrouping.PERCENT_STACKED)) {
					final Axis axis = chart.getYAxis();
					if (axis != null) {
						final SChartAxis saxis = chartInfo.getValueAxises().get(0); 
						
						Map<String, Double> hlmap = getHighLow(chart, Y);
						if(setupAxis(axis, saxis, hlmap.get(HIGHEST), hlmap.get(LOWEST))) {
							// SchartAxis is used to build formatted value
							// AxisGetter is used to get specific axis value
							Map<SChartAxis, AxisGetter> axisGetters = new HashMap<SChartAxis, HighchartsHelper.AxisDrawer.AxisGetter>(2);
							axisGetters.put(saxis, getAxisGetter(Y));
							
							setupTooltip(chart, axisGetters, 
									"\\'<span style=\"font-size: 10px\">\\' + this.key + \\'</span><br/>" + 
									"<span style=\"color:\\' + this.series.color + \\'\">\\' + " +
									"this.series.name + " +
									"\\':</span><b>\\' + " +
									"this.valueConst[this.point.y] + " +
									"\\'</b><br/>\\'");
						}
					}
				}
				break;
			case SCATTER:
				final int size = chartInfo.getValueAxises().size(); 
				final Axis zxaxis = chart.getXAxis();
				final Axis zyaxis = chart.getYAxis();
				
				boolean isYDid = false;
				boolean isXDid = false;
				
				// SchartAxis is used to build formatted value
				// AxisGetter is used to get specific axis value
				Map<SChartAxis, AxisGetter> axisGetters = new HashMap<SChartAxis, HighchartsHelper.AxisDrawer.AxisGetter>(2);
				
				if (chartInfo.getBarDirection() == BarDirection.HORIZONTAL) {
					if (size > 0 && zyaxis != null) {
						SChartAxis yaxis = chartInfo.getValueAxises().get(0);
						Map<String, Double> hlmap = getHighLow(chart, Y);
						if(isYDid = setupAxis(zyaxis, yaxis, hlmap.get(HIGHEST), hlmap.get(LOWEST)))
							axisGetters.put(yaxis, getAxisGetter(Y));
					}
					if (size > 1 && zxaxis != null) {
						SChartAxis xaxis = chartInfo.getValueAxises().get(1);
						Map<String, Double> hlmap = getHighLow(chart, X);
						if(isXDid = setupAxis(zxaxis, xaxis, hlmap.get(HIGHEST), hlmap.get(LOWEST)))
							axisGetters.put(xaxis, getAxisGetter(X));
					}
				} else {
					if (size > 0 && zxaxis != null) {
						SChartAxis xaxis = chartInfo.getValueAxises().get(0);
						Map<String, Double> hlmap = getHighLow(chart, X);
						if(isXDid = setupAxis(zxaxis, xaxis, hlmap.get(HIGHEST), hlmap.get(LOWEST)))
							axisGetters.put(xaxis, getAxisGetter(X));
					}
					if (size > 1 && zyaxis != null) {
						SChartAxis yaxis = chartInfo.getValueAxises().get(1);
						Map<String, Double> hlmap = getHighLow(chart, Y);
						if(isYDid = setupAxis(zyaxis, yaxis, hlmap.get(HIGHEST), hlmap.get(LOWEST)))
							axisGetters.put(yaxis, getAxisGetter(Y));
					}
				}
				
				if(axisGetters.size() > 0)
					setupTooltip(chart, axisGetters, 
							"\\'<span style=\"color:\\' + this.series.color + \\'\">\\' + " +
							"this.series.name + " +
							"\\'</span><br/>x: <b>\\' +" +
							(isXDid ? "this.valueConst[this.point.x.toString()]" : "this.point.x") +
							"+\\'</b><br/>y: <b>\\' + " +
							(isYDid ? "this.valueConst[this.point.y.toString()]" : "this.point.y") +
							"+\\'</b><br/>\\'");
				break;
			case BUBBLE:
				final int size0 = chartInfo.getValueAxises().size();
				final Axis zxaxis0 = chart.getXAxis();
				final Axis zyaxis0 = chart.getYAxis();
				
				boolean isXDid0 = false;
				boolean isYDid0 = false;
				boolean isZDid0 = false;
				
				// SchartAxis is used to build formatted value
				// AxisGetter is used to get specific axis value
				Map<SChartAxis, AxisGetter> axisGetters0 = new HashMap<SChartAxis, HighchartsHelper.AxisDrawer.AxisGetter>(2);
				
				if (size0 > 0 && zxaxis0 != null) {
					SChartAxis xaxis = chartInfo.getValueAxises().get(0);
					Map<String, Double> hlmap = getHighLow(chart, X);
					if(isXDid0 = setupAxis(zxaxis0, xaxis, hlmap.get(HIGHEST), hlmap.get(LOWEST)))
						axisGetters0.put(xaxis, getAxisGetter(X));
				}
				if (size0 > 1 && zyaxis0 != null) {
					SChartAxis yaxis = chartInfo.getValueAxises().get(1);
					Map<String, Double> hlmap = getHighLow(chart, LOW);
					if(isYDid0 = setupAxis(zyaxis0, yaxis, hlmap.get(HIGHEST), hlmap.get(LOWEST)))
						axisGetters0.put(yaxis, getAxisGetter(LOW));
				}
				if (size0 > 2) {
					SChartAxis zaxis = chartInfo.getValueAxises().get(2);
					if(isZDid0 = (zaxis != null && !Strings.isBlank(zaxis.getFormat())))
						axisGetters0.put(zaxis, getAxisGetter(HIGH));
				}
				
				if(axisGetters0.size() > 0)
					setupTooltip(chart, axisGetters0, 
							"\\'<span style=\"color:\\' + this.series.color + \\'\">\\' + " +
							"this.series.name + " +
							"\\':</span><br/>(\\' +" +
							(isXDid0 ? "this.valueConst[this.point.x.toString()]" : "this.point.x") +
							"+ \\', \\' + " +
							(isYDid0 ? "this.valueConst[this.point.y.toString()]" : "this.point.y") +
							"+ \\'), Size:\\' + " +
							(isZDid0 ? "this.valueConst[this.point.z.toString()]" : "this.point.z"));
				break;
			case STOCK:
			case RADAR:
			case SURFACE:
			case OF_PIE:
			default:
				break;
			}
			
		}
		
		private static AxisGetter getAxisGetter(String type)	{
			if(type.equals(X))
				return new AxisGetter(){
					@Override
					public double getValue(Point p) {
						return p.getX().doubleValue();
					}
			};
			else if(type.equals(Y))
				return new AxisGetter(){
					@Override
					public double getValue(Point p) {
						return p.getY().doubleValue();
					}
			};
			else if(type.equals(LOW))
				return new AxisGetter(){
					@Override
					public double getValue(Point p) {
						return p.getLow().doubleValue();
					}
			};
			else if(type.equals(HIGH))
				return new AxisGetter(){
					@Override
					public double getValue(Point p) {
						return p.getHigh().doubleValue();
					}
			};
			else
				return null;
		}
		
		private static Map<String, Double> getHighLow(ZssCharts chart, String type) {
			double highest = - Double.MAX_VALUE;
			double lowest = Double.MAX_VALUE;
			AxisGetter getter = getAxisGetter(type);
			for(int i = 0, size = chart.getSeriesSize(); i < size; i++) {
				for (Point point : chart.getSeries(i).getData()) {
					double value = getter.getValue(point);
					if(value > highest)
						highest = value;
					if(value < lowest)
						lowest = value;
				}
			}
			
			Map<String, Double> map = new HashMap<String, Double>(2);
			map.put(HIGHEST, highest);
			map.put(LOWEST, lowest);
			
			return map;
		}
		
		private static String trimTrailingZeros(String number) {
		    if(!number.contains(".")) {
		        return number;
		    }

		    return number.replaceAll("\\.?0*$", "");
		}
		
		private static void setupTooltip(ZssCharts chart, Map<SChartAxis, AxisGetter> axisGetters, String pattern) {			
			JSONObject axisLabelMap = new JSONObject();

			for(SChartAxis axis: axisGetters.keySet()) {
				AxisGetter getter = axisGetters.get(axis);

				FormatContext formatContext = new FormatContext();
				FormatEngine formatEngine = new FormatEngineImpl();
				FormatResult result = formatEngine.format(axis.getFormat(), 1d, formatContext, 12);
				NumberFormat format = (NumberFormat) result.getFormater();
				
				for(int i = 0, size = chart.getSeriesSize(); i < size; i++) {
					for (Point point : chart.getSeries(i).getData()) {
						double value = getter.getValue(point);
						// use "" + value here is for escaping java default toString unexpected case
						// e.g. 0.003 in double type will became "0.0030 
						axisLabelMap.put(trimTrailingZeros(String.valueOf(value)), format.format(value));
					}
				}
			}
				
			String str = axisLabelMap.toJSONString();
			// the syntax ';;;;; and ;;;' are used in escaping in ChartDrawer class for export
			// DONT remove it. if you want to update this statement, you should also check ChartDrawer.
			chart.getTooltip().addExtraAttr("formatter"
					, new JavaScriptValue("';;;;;if(!this.valueConst) { var objMap = this.valueConst = {}, objTmp = " + str + 
							// ZSS-885: IE11 can only accept floating-point value small than 0.025602343784161967 (including, in same digits number)
							"; Object.keys(objTmp).forEach(function(key){objMap[parseFloat(key).toString()] = objTmp[key];})};" +
							" return " + pattern + ";;;'"));
			
		}

		private static boolean setupAxis(Axis axis, SChartAxis saxis, double high, double low) {
			if (saxis != null && !Strings.isBlank(saxis.getFormat())) {
				FormatContext formatContext = new FormatContext();
				FormatEngine formatEngine = new FormatEngineImpl();
				FormatResult result = formatEngine.format(saxis.getFormat(), 1d, formatContext, 12);
				NumberFormat format = (NumberFormat) result.getFormater();
				List<Number> ticks = new ArrayList<Number>(10);
				if(high == low) {
					if(high == 0) {
						high = 1;
						low = -1;
					} else {
						double range = high / 2;
						high += range;
						low -= range; 
					}
				}
				double distance = high - low;
				double size = distance / 8;
				if(size == 0)
					size = high / 10;
				size = normalize(size, distance);
				JSONObject axisLabelMap = new JSONObject();
				
				double tick = low - size;
				if(tick < 0 && low >= 0) tick = 0;
				ticks.add(tick);
				// use "" + value here is for escaping java default toString unexpected case
				// e.g. 0.003 in double type will became "0.0030 
				axisLabelMap.put(trimTrailingZeros(String.valueOf(tick)), format.format(tick));
				for(tick += size; tick <= high; tick += size) {
					ticks.add(tick);
					// use "" + value here is for escaping java default toString unexpected case
					// e.g. 0.003 in double type will became "0.0030 
					axisLabelMap.put(trimTrailingZeros(String.valueOf(tick)), format.format(tick));
				}
				ticks.add(tick);
				// use "" + value here is for escaping java default toString unexpected case
				// e.g. 0.003 in double type will became "0.0030 
				axisLabelMap.put(trimTrailingZeros(String.valueOf(tick)), format.format(tick));
				
				String str = axisLabelMap.toJSONString();
				axis.setTickPositions(ticks);
				// the syntax ';;;;; and ;;;' are used in escaping in ChartDrawer class for export
				// DONT remove it. if you want to update this statement, you should also check ChartDrawer.
				axis.getLabels().addExtraAttr("formatter",  
						new JavaScriptValue("';;;;;if(!this.valueConst) { var objMap = this.valueConst = {}, objTmp = " + str + 
								// ZSS-885: IE11 can only accept floating-point value small than 0.025602343784161967 (including, in same digits number)
								"; Object.keys(objTmp).forEach(function(key){objMap[parseFloat(key).toString()] = objTmp[key];})};" +
								"; return this.valueConst[this.value.toString()];;;'"));
				return true;
			}
			return false;
		}
		
		private static double normalize(double size, double distance) {
			double exp10 = Math.log10(size);
			int exp = (int) Math.floor(exp10);
			double tens = Math.pow(10d, exp);
			int num = (int) Math.floor(size / tens);
			switch(num) {
			case 0:
			case 1:
			case 2:
				num = tens < 1 && distance > 1 ? 5 : 2;
				break;
			case 3:
			case 4:
			case 5:
				num = 5;
				break;
			case 6:
			case 7:
			case 8:
			case 9:
				num = 10;
				break;
			}
			return  num * tens;
		}
	}

	

	private static String getLegendHorizontalAlign(ChartLegendPosition legendPosition) {
		if(legendPosition == ChartLegendPosition.TOP || legendPosition == ChartLegendPosition.BOTTOM)
			return "center";
		if(legendPosition == ChartLegendPosition.TOP_RIGHT)
			return "right";
		if(legendPosition != null)
			return legendPosition.toString().toLowerCase();
		
		return "right";
	}
	
	private static String getLegendVerticalAlign(ChartLegendPosition legendPosition) {
		if(legendPosition == ChartLegendPosition.LEFT || legendPosition == ChartLegendPosition.RIGHT)
			return "middle";
		if(legendPosition == ChartLegendPosition.TOP_RIGHT)
			return "top";
		if(legendPosition != null)
			return legendPosition.toString().toLowerCase();
		
		return "middle";
	}

	private static Title getChartTitle(SChart chartInfo) {
		Title title = new Title();
		final String t = chartInfo.getTitle();

		title.setAlign("center");
		title.setText(title == null ? "" : !Strings.isEmpty(t) ? t : getFirstSeriesTitle(chartInfo));
		return title;
	}
	
	private static String getFirstSeriesTitle(SChart chartInfo) {
		//ZSS-821
		SChartData scd = chartInfo.getData();
		if (scd instanceof SGeneralChartData) {
			SGeneralChartData cd = (SGeneralChartData) chartInfo.getData();
			if (cd.getNumOfSeries() > 0) {
				if (cd.getNumOfSeries() == 1) {
					return cd.getSeries(0).getName();
				} else {
					switch (chartInfo.getType()) {
					case PIE:
					case DOUGHNUT:
						return cd.getSeries(0).getName();
					default:
						return "";
					}
				}
			}
		}
		return "";
	}
	
	
	private enum TypeMapper{
		AREA(ChartType.AREA, ZssCharts.AREA, new Setting() {

			@Override
			public void doSetting(ZssCharts chart, SChart info) {
				if(info.getGrouping() == ChartGrouping.STACKED)
					chart.getPlotOptions().getArea().setStacking("normal");
				else if(info.getGrouping() == ChartGrouping.PERCENT_STACKED)
					chart.getPlotOptions().getArea().setStacking("percent");
				
				// for 3D
				boolean isThreeD = info.isThreeD();
				if(isThreeD) {
					Options3D option = chart.getOptions3D();
					option.setEnabled(isThreeD);
					option.setAlpha(6);
					option.setBeta(6);
					option.setDepth(50);
					option.setViewDistance(25);
					chart.setOptions3D(option);
				}
			}

		}),
		BAR(ChartType.BAR, ZssCharts.BAR, new Setting() {

				@Override
				public void doSetting(ZssCharts chart, SChart info) {
					if(info.getGrouping() == ChartGrouping.STACKED)
						chart.getPlotOptions().getBar().setStacking("normal");
					else if(info.getGrouping() == ChartGrouping.PERCENT_STACKED)
						chart.getPlotOptions().getBar().setStacking("percent");
					
					// for 3D
					boolean isThreeD = info.isThreeD();
					if(isThreeD) {
						Options3D option = chart.getOptions3D();
						option.setEnabled(isThreeD);
						option.setAlpha(6);
						option.setBeta(6);
						option.setDepth(50);
						option.setViewDistance(25);
						chart.setOptions3D(option);
					}
				}
	
			}),
		COLUMN(ChartType.COLUMN, "column", new Setting() {

				@Override
				public void doSetting(ZssCharts chart, SChart info) {
					if(info.getGrouping() == ChartGrouping.STACKED)
						chart.getPlotOptions().getColumn().setStacking("normal");
					else if(info.getGrouping() == ChartGrouping.PERCENT_STACKED)
						chart.getPlotOptions().getColumn().setStacking("percent");
					
					// for 3D
					boolean isThreeD = info.isThreeD();
					if(isThreeD) {
						Options3D option = chart.getOptions3D();
						option.setEnabled(isThreeD);
						option.setAlpha(6);
						option.setBeta(6);
						option.setDepth(50);
						option.setViewDistance(25);
						chart.setOptions3D(option);
					}
				}
	
			}),
		BUBBLE(ChartType.BUBBLE, ZssCharts.BUBBLE),
		DOUGHNUT(ChartType.DOUGHNUT, "pie", new Setting() {
	
				@Override
				public void doSetting(ZssCharts chart, SChart info) {
					PlotOptions pOptions = chart.getPlotOptions();
					pOptions.getPie().setShowInLegend(true);
					pOptions.getPie().setInnerSize(DOUGHNUT_INNER_SIZE);
				}
	
			}),
		LINE(ChartType.LINE, ZssCharts.LINE, new Setting() {

			@Override
			public void doSetting(ZssCharts chart, SChart info) {
				if(info.getGrouping() == ChartGrouping.STACKED)
					chart.getPlotOptions().getLine().setStacking("normal");
				else if(info.getGrouping() == ChartGrouping.PERCENT_STACKED)
					chart.getPlotOptions().getLine().setStacking("percent");
				
				// for 3D
				boolean isThreeD = info.isThreeD();
				if(isThreeD) {
					Options3D option = chart.getOptions3D();
					option.setEnabled(isThreeD);
					option.setAlpha(6);
					option.setBeta(6);
					option.setDepth(50);
					option.setViewDistance(25);
					chart.setOptions3D(option);
				}
			}

		}),
		OF_PIE(ChartType.OF_PIE, "ofpie"),
		PIE(ChartType.PIE, ZssCharts.PIE, new Setting() {

				@Override
				public void doSetting(ZssCharts chart, SChart info) {
					PlotOptions pOptions = chart.getPlotOptions();
					pOptions.getPie().setShowInLegend(true);
					
					// for 3D
					boolean isThreeD = info.isThreeD();
					if(isThreeD) {
						Options3D option = chart.getOptions3D();
						option.setEnabled(isThreeD);
						option.setAlpha(50);
						option.setBeta(0);
						pOptions.getPie().setDepth(35);
						chart.setOptions3D(option);
					}
				}
	
			}),
		RADAR(ChartType.RADAR, "radar"),
		SCATTER(ChartType.SCATTER, ZssCharts.SCATTER, new Setting() {

			@Override
			public void doSetting(ZssCharts chart, SChart info) {
				// for 3D
				boolean isThreeD = info.isThreeD();
				if(isThreeD) {
					Options3D option = chart.getOptions3D();
					option.setEnabled(isThreeD);
					option.setAlpha(10);
					option.setBeta(30);
					option.setDepth(250);
					option.setViewDistance(5);
					chart.setOptions3D(option);
				}
			}
			
		}),
		SURFACE(ChartType.SURFACE, "surface"),
		OTHER(null, "");
		
//		ChartType type;
		String innerType;
		Setting setting;
		
		TypeMapper(ChartType type, String innerType) {
//			this.type = type;
			this.innerType = innerType;
		}
		
		TypeMapper(ChartType type, String innerType, Setting setting) {
//			this.type = type;
			this.innerType = innerType;
			this.setting = setting;
		}
		
		void doSetting(ZssCharts charts, SChart info){
			charts.setType(innerType);
			if(setting != null)
				setting.doSetting(charts, info);
		}
		
		interface Setting{
			void doSetting(ZssCharts charts, SChart info);
		}
	};
}
