package hiro.yoshioka.sdh2;

import hiro.yoshioka.chart.ChartInfo;
import hiro.yoshioka.chart.PieChartInfo;
import hiro.yoshioka.chart.TimeChartInfo;
import hiro.yoshioka.sdh.DatabaseType;
import hiro.yoshioka.sdh.HeaderType;
import hiro.yoshioka.sdh.RDHMetaColumn;
import hiro.yoshioka.sdh.ResultSetDataHolder;
import hiro.yoshioka.sdh.ResultSetMetaCopy;
import hiro.yoshioka.sdh.StringRecordData;
import hiro.yoshioka.sdh.pair.DifferenceStringData;
import hiro.yoshioka.sql.resource.IDBSchema;
import hiro.yoshioka.sql.resource.IDBTable;
import hiro.yoshioka.sql.resource.xml.DBSchemaValue;
import hiro.yoshioka.sql.resource.xml.DBTableValue;
import hiro.yoshioka.sql.util.CommentInfo;
import hiro.yoshioka.util.LineSeparatorEnum;
import hiro.yoshioka.util.SQLDataType;
import hiro.yoshioka.util.StringUtil;

import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;

public class ResultSetDataHolder2 extends ResultSetDataHolder {
	private static final long serialVersionUID = -971743343622454131L;
	private Map<String, String> columnNameSubstitutionMap;
	String sqlStatement;
	Object[] binds;
	String tableNameE;
	String tableName;
	String schemaNameE;
	String connectionDisplayString;

	public static final Double ZERO = new Double(0);
	// 1 [2006]
	// 2 [06]
	// 3 [08]
	public static final Pattern FORMAT_DATE = Pattern
			.compile("(\\d\\d\\d\\d).(\\d\\d).(\\d\\d)");
	public static final Pattern FORMAT_DATE2 = Pattern
			.compile("(\\d\\d).(\\d\\d).(\\d\\d\\d\\d)");

	static final String PRE_HTML;

	static {
		List<String> preList = new ArrayList<String>();
		preList.add("<html>");
		preList.add(" <head>");
		preList.add(" <script type='text/javascript' src='https://www.google.com/jsapi'></script>");
		preList.add(" <script type='text/javascript'>");
		preList.add("  google.load('visualization', '1', {packages:['table','annotatedtimeline']});");
		preList.add("  google.setOnLoadCallback(drawTable);");
		preList.add("  function drawTable() {");

		PRE_HTML = StringUtil.join(preList, StringUtil.LINE_SEPARATOR);
		List<String> postList = new ArrayList<String>();
		// post table

	}

	public ResultSetDataHolder2() {
	}

	public ResultSetDataHolder2(String[] argNames, ResultSetMetaData meta,
			DatabaseType databaseType) throws NullPointerException {
		super(argNames, meta, databaseType);
	}

	public String getConnectionDisplayString() {
		return connectionDisplayString;
	}

	public void setConnectionDisplayString(String connectionDisplayString) {
		this.connectionDisplayString = connectionDisplayString;
	}

	private String getSubstitutionColumnName(String sourceName) {
		System.out.println("            " + sourceName + "/"
				+ this.columnNameSubstitutionMap);
		if (this.columnNameSubstitutionMap == null) {
			return sourceName;
		}
		String name = this.columnNameSubstitutionMap.get(sourceName);
		if (StringUtil.isEmpty(name)) {
			return sourceName;
		}
		return name;
	}

	public String getTableNameE() {
		if (StringUtil.isEmpty(tableNameE)) {
			if (hasResultSetMetaData()) {
				try {
					return StringUtil.nvl(meta.getTableName(1));
				} catch (SQLException e) {
				}
			}
		}
		return StringUtil.nvl(tableNameE);
	}

	public String getSqlStatement() {
		return sqlStatement;
	}

	public void setSqlStatement(String sqlStatement) {
		this.sqlStatement = sqlStatement;
	}

	public Object[] getBinds() {
		return binds;
	}

	public void setBinds(Object[] binds) {
		this.binds = binds;
	}

	public void setSchemaNameE(String schemaNameE) {
		this.schemaNameE = schemaNameE;
	}

	public String getSchemaNameE() {
		return StringUtil.nvl(schemaNameE);
	}

	public String generateUpdateScript(int row) {
		StringBuilder buf = new StringBuilder();
		buf.append("UPDATE ");
		if (!StringUtil.isEmpty(getSchemaNameE())) {
			buf.append(getSchemaNameE()).append(".");
		}
		buf.append(getTableNameE());
		buf.append(" SET ");
		for (int i = 1; i < key.length; i++) {
			DifferenceStringData diff = (DifferenceStringData) getPair(row, i);
			if (diff.fDifferenceKind.isChange()) {
				buf.append(getSubstitutionColumnName(key[i]));
				buf.append("=");
				if (StringUtil.isEmpty(getStringData(row, key[i]))) {
					buf.append("NULL");
				} else {
					if (needsQuote(i)) {
						buf.append("'");
						buf.append(getStringData(row, key[i]));
						buf.append("'");
					} else {
						buf.append(getStringData(row, key[i]));
					}
				}
				buf.append(",");
			}
		}
		buf.setLength(buf.length() - 1);
		buf.append(" WHERE ");
		for (int i = 0; i < pkPositions.length; i++) {
			if (i > 0) {
				buf.append(" AND ");
			}
			int pos = pkPositions[i] + 1;
			buf.append(getSubstitutionColumnName(key[pos]));
			DifferenceStringData diff = (DifferenceStringData) getPair(row, pos);
			if (diff.fDifferenceKind.isNoChange()) {
				buf.append("=");
				if (needsQuote(pos)) {
					buf.append("'");
					buf.append(getStringData(row, key[pos]));
					buf.append("'");
				} else {
					buf.append(getStringData(row, key[pos]));
				}
			} else {
				buf.append("=");
				if (needsQuote(pos)) {
					buf.append("'");
					buf.append(diff.fDiffString);
					buf.append("'");
				} else {
					buf.append(diff.fDiffString);
				}
			}
		}
		buf.append(";");
		return buf.toString();
	}

	public String generateDeleteScript(int row) {
		StringBuilder buf = new StringBuilder();
		buf.append("DELETE FROM ");
		if (!StringUtil.isEmpty(getSchemaNameE())) {
			buf.append(getSchemaNameE()).append(".");
		}
		buf.append(getTableNameE());

		buf.append(" WHERE ");
		for (int i = 0; i < pkPositions.length; i++) {
			if (i > 0) {
				buf.append(" AND ");
			}
			int pos = pkPositions[i] + 1;
			buf.append(getSubstitutionColumnName(key[pos]));
			buf.append("=");
			if (needsQuote(pos)) {
				buf.append("'");
				buf.append(getStringData(row, key[pos]));
				buf.append("'");
			} else {
				buf.append(getStringData(row, key[pos]));
			}
		}
		buf.append(";");
		return buf.toString();
	}

	public ReflectionPreparedStatement generateInsertPreparedStatement(int row) {
		return this.generateInsertPreparedStatement(row, null, false);
	}

	public ReflectionPreparedStatement generateInsertPreparedStatement(int row,
			Map<String, String> columnNameSubstitutionMap,
			boolean withOutSchemaAlias) {
		StringRecordData[] newData;
		this.columnNameSubstitutionMap = columnNameSubstitutionMap;
		changeStatus(row, HeaderType.INSERT);
		newData = getStringRecordRow(row);

		return new ReflectionPreparedStatement(meta, newData, null,
				pkPositions, tableNameE, schemaNameE,
				columnNameSubstitutionMap, withOutSchemaAlias);
	}

	public String generateInsertScript(int row) {
		StringBuilder buf = new StringBuilder();
		buf.append("INSERT INTO ");
		if (!StringUtil.isEmpty(getSchemaNameE())) {
			buf.append(getSchemaNameE()).append(".");
		}
		buf.append(getTableNameE());
		buf.append(" (");
		for (int i = 1; i < key.length; i++) {
			buf.append(getSubstitutionColumnName(key[i]));
			buf.append(",");
		}
		buf.setLength(buf.length() - 1);
		buf.append(") ");
		buf.append(StringUtil.LINE_SEPARATOR);
		buf.append(" VALUES (");
		for (int i = 1; i < key.length; i++) {
			if (StringUtil.isEmpty(getStringData(row, key[i]))) {
				buf.append("NULL");
			} else {
				if (needsQuote(i)) {
					buf.append("'");
					buf.append(getStringData(row, key[i]));
					buf.append("'");
				} else {
					buf.append(getStringData(row, key[i]));
				}
			}
			buf.append(",");
		}
		buf.setLength(buf.length() - 1);
		buf.append(");");
		return buf.toString();
	}

	public void setTableNameE(String tableNameE) {
		this.tableNameE = tableNameE;
		if (hasResultSetMetaData()) {
			meta.resetTableName(tableNameE);
		}
	}

	public String getTableName() {
		return StringUtil.nvl(tableName);
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public ReflectionPreparedStatement[] reflect() throws SQLException {
		if (log.isInfoEnabled()) {
			log.info("reflect start");
		}
		StringRecordData[] newData;
		StringRecordData[] oldData;

		ArrayList<ReflectionPreparedStatement> retList = new ArrayList<ReflectionPreparedStatement>();
		StringBuffer buff = new StringBuffer();
		if (log.isDebugEnabled()) {
			log.debug("fBackUpMap:" + fBackUpMap);
		}

		for (Iterator ite = fBackUpMap.entrySet().iterator(); ite.hasNext();) {
			Map.Entry entry = (Entry) ite.next();
			int index = ((Integer) entry.getKey()).intValue();
			newData = getStringRecordRow(index);
			oldData = (StringRecordData[]) entry.getValue();
			retList.add(new ReflectionPreparedStatement(meta, newData, oldData,
					pkPositions, tableNameE, schemaNameE,
					this.columnNameSubstitutionMap, false));
			System.out.println("nlen/olen=" + newData.length + "/"
					+ oldData.length);
		}
		for (int i = getRowCount() - 1; i >= 0; i--) {
			newData = getStringRecordRow(i);
			HeaderData header = (HeaderData) newData[0];
			if (header.delete()) {
				retList.add(new ReflectionPreparedStatement(meta, newData,
						null, pkPositions, tableNameE, schemaNameE,
						this.columnNameSubstitutionMap, false));
			}
		}
		return retList.toArray(new ReflectionPreparedStatement[retList.size()]);
	}

	public ReflectionPreparedStatement[] makeBlob(int[] targetIndexes)
			throws SQLException {

		if (log.isInfoEnabled()) {
			log.info("make blob start");
		}
		StringRecordData[] oldData;

		ArrayList<ReflectionPreparedStatement> retList = new ArrayList<ReflectionPreparedStatement>();
		for (int i = 0; i < targetIndexes.length; i++) {
			oldData = getStringRecordRow(targetIndexes[i]);
			retList.add(new ReflectionPreparedStatement(meta, null, oldData,
					pkPositions, tableNameE, schemaNameE,
					this.columnNameSubstitutionMap, false));
		}

		return retList.toArray(new ReflectionPreparedStatement[retList.size()]);
	}

	public void setNamedInformationFromIDBTable(IDBTable fIDBTable,
			String currentSchemaName) {
		setTableNameE(fIDBTable.getName());
		setTableName(fIDBTable.getComment());
		setPkPositions(fIDBTable.getPkPositions());
		IDBSchema schema = (IDBSchema) fIDBTable.getParent();
		if (schema != null) {
			// if (!schema.getName().equalsIgnoreCase(currentSchemaName))
			setSchemaNameE(schema.getName());
		}
	}

	public void setNamedInformationFromIDBTable(DBTableValue dbTable,
			String currentSchemaName) {
		setTableNameE(dbTable.getName());
		setTableName(dbTable.getComment());
		setPkPositions(dbTable.getPkPositions());
		DBSchemaValue schema = (DBSchemaValue) dbTable.getParent();
		if (schema != null) {
			// if (!schema.getName().equalsIgnoreCase(currentSchemaName))
			setSchemaNameE(schema.getName());
		}
	}

	public void setMeta(ResultSetMetaCopy resultSetMetaCopy) {
		this.meta = resultSetMetaCopy;
	}

	public String getGoogleStringValue(int iRow, RDHMetaColumn columnHeader) {
		return getGoogleStringValue(iRow, columnHeader, false, false);
	}

	public String getGoogleStringValue(int iRow, RDHMetaColumn columnHeader,
			boolean withOutNull, boolean addDate) {
		SQLDataType dataType = columnHeader.getType();
		String str = getStringRecordRow(iRow)[columnHeader.getIndex()]
				.getString();
		switch (dataType.getGoogleDataType()) {
		case BOOLEAN:
			Boolean b = getBooleanData(iRow, columnHeader.getIndex());
			if (b == null) {
				return String.valueOf(Boolean.FALSE);
			}
			return String.valueOf(b);
		case DATE:
			Date date = null;
			if (StringUtil.isEmpty(str)) {
				if (withOutNull) {
					date = new Date();
				}
			} else {
				// new Date(2008, 0, 15, 14, 30, 45)
				date = getDateData(iRow, columnHeader.getIndex());
			}
			if (date == null) {
				return "null";
			}
			return String.format("new Date(%d, %d, %d)", 1900 + date.getYear(),
					date.getMonth(), date.getDate());
		case DATE_TIME:
			// new Date(2008, 0, 15, 14, 30, 45)
			Date dateTime = null;
			if (StringUtil.isEmpty(str)) {
				if (withOutNull) {
					dateTime = new Date();
				}
			} else {
				// new Date(2008, 0, 15, 14, 30, 45)
				dateTime = getDateData(iRow, columnHeader.getIndex());
			}

			if (dateTime == null) {
				return "null";
			}
			Calendar cal = Calendar.getInstance();
			cal.setTime(dateTime);
			return String.format("new Date(%d, %d, %d,%d, %d, %d)",
					cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
					cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR),
					cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
		case TIME_OF_DAY:
			String time = getStringRecordRow(iRow)[columnHeader.getIndex()]
					.getString();
			if (time == null) {
				return "null";
			}
			String[] times = time.split(":");
			if (addDate) {
				cal = Calendar.getInstance();
				cal.set(Calendar.HOUR, Integer.parseInt(times[0], 10));
				cal.set(Calendar.MINUTE, Integer.parseInt(times[1], 10));
				if (times.length == 3) {
					cal.set(Calendar.SECOND, Integer.parseInt(times[2], 10));
				}
				return String.format("new Date(%d, %d, %d,%d, %d, %d)",
						cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
						cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR),
						cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
			} else {
				if (times.length == 2) {
					return String.format("[%d, %d, 0]",
							Integer.parseInt(times[0], 10),
							Integer.parseInt(times[1], 10));
				} else {
					return String.format("[%d, %d, %d]",
							Integer.parseInt(times[0], 10),
							Integer.parseInt(times[1], 10),
							Integer.parseInt(times[2], 10));
				}
			}
		case NUMBER:
			String number = getStringRecordRow(iRow)[columnHeader.getIndex()]
					.getString();
			return number;
		default:
			String string = getStringRecordRow(iRow)[columnHeader.getIndex()]
					.getString();
			string = StringUtil.resetCRLF(string, false,
					LineSeparatorEnum.REG_LF);
			string = string.replaceAll("'", "\\\\'");

			return String.format("'%s'", string);
		}
	}

	private String createGoogleDataTable(String tableName,
			List<RDHMetaColumn> headerList, CommentInfo com_info, int height,
			String title) {
		List<String> tableBodyList = new ArrayList<String>();

		tableBodyList.add(String
				.format("    var %s = new google.visualization.DataTable();",
						tableName));

		for (RDHMetaColumn header : headerList) {
			SQLDataType type = header.getType();
			tableBodyList.add(String.format("      %s.addColumn('%s','%s');",
					tableName, type.getGoogleDataType().getType(),
					header.getName()));
		}
		int rowMax = getRowCount();
		tableBodyList.add(String.format("      %s.addRows(%d);", tableName,
				rowMax));
		for (int iRow = 0; iRow < rowMax; iRow++) {
			for (RDHMetaColumn header : headerList) {
				String str = getGoogleStringValue(iRow, header);
				tableBodyList.add(String.format(
						"      %s.setCell(%d, %d, %s);", tableName, iRow,
						header.getIndex() - 1, str));
			}
		}
		for (RDHMetaColumn header : headerList) {
			int colIdx = header.getIndex() - 1;
			if (header.getType().isNumerics()) {
				if (com_info != null && com_info.supportBarGraph
						&& header.isPercent()) {
					tableBodyList
							.add(String
									.format("      var fmt_%s_%02d = new google.visualization.BarFormat({width: 70});",
											tableName, colIdx));
					tableBodyList.add(String.format(
							"      fmt_%s_%02d.format(%s, %d);", tableName,
							colIdx, tableName, colIdx));
				}
			}
		}
		tableBodyList
				.add(String
						.format("    var table = new google.visualization.Table(document.getElementById('%s_div')); ",
								tableName));
		if (getRowCount() < 40) {
			tableBodyList
					.add(String
							.format("    table.draw(%s, {allowHtml: true,showRowNumber: true, width: 600,  is3D: true, title: '%s'}); ",
									tableName, height, title));
		} else {
			tableBodyList
					.add(String
							.format("    table.draw(%s, {allowHtml: true,showRowNumber: true, width: 600, height: %d, is3D: true, title: '%s'}); ",
									tableName, height, title));
		}
		tableBodyList.add("  } ");

		return StringUtil.join(tableBodyList, StringUtil.LINE_SEPARATOR);
	}

	private void createPieChart(PieChartInfo piChartInfo,
			List<String> lineList, int fncNum) {

		lineList.add(String.format(" function drawVisualization%02d() {",
				fncNum));
		lineList.add(String.format("  var dataTable%02d = [", fncNum));
		if (piChartInfo.topCout > 0) {
			TreeMap<Double, Set<String>> topMap = new TreeMap<Double, Set<String>>(
					new Comparator<Double>() {
						@Override
						public int compare(Double o1, Double o2) {
							return ((Comparable) o1).compareTo(o2) * -1;
						}
					});

			double total = 0;
			for (int i = 0; i < getRowCount(); i++) {
				String[] row = getRow(i);
				try {
					Double d = new Double(row[piChartInfo.valueIndex]);
					Set<String> set = topMap.get(d);
					if (set == null) {
						set = new HashSet<String>();
						topMap.put(d, set);
					}
					set.add(row[piChartInfo.titleIndex]);
					total += d.doubleValue();
				} catch (Exception e) {
				}
			}
			for (Double d : topMap.keySet()) {
				String str = topMap.get(d) + "";
				str = str.replaceAll("'", "\\\\'");
				lineList.add(String.format("['%s', %s],", str,
						String.valueOf(d)));
			}
		} else {
			int maxRow = getRowCount();
			for (int i = 0; i < maxRow; i++) {
				StringRecordData[] row = getStringRecordRow(i);

				// new Date(2008, 0, 15, 14, 30, 45)
				// if(headerList.get(piChartInfo.titleIndex))
				String str = row[piChartInfo.titleIndex].getString();
				str = str.replaceAll("'", "\\\\'");
				lineList.add(String.format("['%s', %s],", str,
						row[piChartInfo.valueIndex].getString()));
			}
		}
	}

	private void createLineChart(TimeChartInfo timeChartInfo,
			List<String> lineList, int fncNum) {
		String tableName = String.format("dataTable%02d", fncNum);
		lineList.add(String.format(" function drawVisualization%02d() {",
				fncNum));
		lineList.add(String.format(
				"  var %s = new google.visualization.DataTable();", tableName));

		if (log.isDebugEnabled()) {
			log.debug("info.valueIndexes.length["
					+ timeChartInfo.valueIndexes.length + "]");
		}
		int rowCount = getRowCount();

		List<RDHMetaColumn> columnList = getColumnList(false);
		RDHMetaColumn dateColumn = columnList.get(timeChartInfo.dateIndex - 1);
		System.out.println("dateColumn=" + dateColumn.getName() + "/"
				+ dateColumn.getIndex());
		boolean needsConvDate = false;
		if (dateColumn.getType().isNumerics()) {
			needsConvDate = true;
			lineList.add(String.format("    %s.addColumn('date', '%s');",
					tableName, dateColumn.getName()));
		} else {
			if (dateColumn.getType().isTime()) {
				lineList.add(String.format(
						"    %s.addColumn('datetime', '%s');", tableName,
						dateColumn.getName()));
			} else {
				lineList.add(String.format("    %s.addColumn('%s', '%s');",
						tableName, dateColumn.getType().getGoogleDataType()
								.getType(), dateColumn.getName()));
			}
		}
		for (int i = 0; i < timeChartInfo.valueIndexes.length; i++) {
			RDHMetaColumn column = columnList
					.get(timeChartInfo.valueIndexes[i] - 1);
			lineList.add(String.format("    %s.addColumn('%s', '%s');",
					tableName, column.getType().getGoogleDataType().getType(),
					column.getName()));
		}

		int rowMax = getRowCount();
		lineList.add(String.format("      %s.addRows(%d);", tableName, rowMax));
		switch (timeChartInfo.mode) {
		case TimeChartInfo.MODE_TIME:
		case TimeChartInfo.MODE_DAY:
			for (int iRow = 0; iRow < rowCount; iRow++) {
				if (needsConvDate) {
					Calendar cal = Calendar.getInstance();
					try {
						int div = getIntData(iRow, dateColumn.getIndex());
						cal.add(Calendar.DAY_OF_MONTH, div);
					} catch (NumberFormatException e) {
					}
					String str = String.format("new Date(%d, %d, %d)",
							cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
							cal.get(Calendar.DAY_OF_MONTH));
					lineList.add(String.format("      %s.setCell(%d, %d, %s);",
							tableName, iRow, 0, str));
				} else {
					String str = getGoogleStringValue(iRow, dateColumn, true,
							true);
					lineList.add(String.format("      %s.setCell(%d, %d, %s);",
							tableName, iRow, 0, str));
				}
				for (int i = 0; i < timeChartInfo.valueIndexes.length; i++) {
					RDHMetaColumn header = columnList
							.get(timeChartInfo.valueIndexes[i] - 1);
					String str = getGoogleStringValue(iRow, header, true, false);
					lineList.add(String.format("      %s.setCell(%d, %d, %s);",
							tableName, iRow, header.getIndex() - 1, str));
				}
			}
			break;
		case TimeChartInfo.MODE_MONTH:
			break;
		case TimeChartInfo.MODE_YEAR:
			break;
		}

	}

	public boolean writeGoogleHtml(Writer writer, int height, String title,
			CommentInfo com_info) {

		PrintWriter out = null;
		try {
			out = new PrintWriter(new BufferedWriter(writer));
			out.println(PRE_HTML);
			String spreadSheetTableName = "sp_dataTable";
			out.println(createGoogleDataTable(spreadSheetTableName,
					getColumnList(false), com_info, height, title));

			out.println();

			if (com_info != null) {
				int fncNum = 0;
				for (ChartInfo chartInfo : com_info.getChartList()) {
					List<String> lineList = new ArrayList<String>();
					if (chartInfo instanceof PieChartInfo) {
						createPieChart((PieChartInfo) chartInfo, lineList,
								fncNum);
						lineList.add(" ];");
						lineList.add("google.visualization.drawChart({");
						lineList.add(String.format(
								"  'containerId': 'chart%02d_div',", fncNum));
						lineList.add(String.format(
								"  'dataTable': dataTable%02d,", fncNum));
						lineList.add("  'chartType': 'PieChart',");
						lineList.add(" 'options': {");
						lineList.add(" 'alternatingRowStyle': true,");
						lineList.add(" 'showRowNumber' : false,");
						lineList.add(" 'is3D': true,");
						lineList.add(String.format(" 'title' : '%s'",
								chartInfo.title));
						lineList.add(" }");
						lineList.add("  });");
						lineList.add(" }");
						lineList.add(String
								.format(" google.setOnLoadCallback(drawVisualization%02d);",
										fncNum));
					} else if (chartInfo instanceof TimeChartInfo) {
						createLineChart((TimeChartInfo) chartInfo, lineList,
								fncNum);

						lineList.add(String
								.format("var chart = new google.visualization.AnnotatedTimeLine (document.getElementById('chart%02d_div'));",
										fncNum));
						lineList.add(String
								.format("chart.draw(dataTable%02d, {displayAnnotations: true, annotationsWidth: 25, displayExactValues:false, title:'%s'});",
										fncNum, chartInfo.title));
						lineList.add("  }");
						lineList.add(String
								.format(" google.setOnLoadCallback(drawVisualization%02d);",
										fncNum));
					}
					fncNum++;
					out.println(StringUtil.join(lineList,
							StringUtil.LINE_SEPARATOR));
					out.println();
				}
			}
			out.println(" </script> ");
			out.println(" </head> ");
			out.println(" <body> ");

			out.println(String.format("   <div id='%s_div'></div> ",
					spreadSheetTableName));
			if (com_info != null) {
				int maxNum = com_info.getChartList().size();
				for (int i = 0; i < maxNum; i++) {
					out.println(String
							.format("   <div id='chart%02d_div'  style='width: 640px; height: 320px;'></div> ",
									i));
				}
			}
			out.println("  </body> ");
			out.println("</html>");

			out.println();

			return true;
		} catch (Exception e) {
			log.fatal(StringUtil.EMPTY_STRING, e);
			return false;
		} finally {
			if (out != null) {
				out.flush();
				out.close();
			}
		}

	}
}
