package cl.altiuz.reports.zmq;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

import cl.altiuz.reports.ReportsException;
import cl.altiuz.reports.bean.DataBean;
import cl.altiuz.reports.config.ARConfig;
import cl.altiuz.reports.config.ContextConfig;
import cl.altiuz.reports.config.Index;
import cl.altiuz.reports.connector.ConnectorConfig;

public class Connector implements cl.altiuz.reports.connector.Connector {

	private static final Logger LOG = Logger.getLogger(Connector.class
			.getName());

	private static final int NO_FLAGS = 0;

	private static final String DOC_NAME = "doc_name"; //$NON-NLS-1$
	private static final String DOC_LEN = "doc_len"; //$NON-NLS-1$
	private static final String DOC_OFF = "doc_off"; //$NON-NLS-1$
	private static final String COMP_OFF = "comp_off"; //$NON-NLS-1$
	private static final String COMP_LEN = "comp_len"; //$NON-NLS-1$
	private static final String COMP_TYPE = "comp_type"; //$NON-NLS-1$

	private static final String ONLY_LAST_ITEM = "_last";
	private String username;
	private String password;

	static {
		try {
			Class.forName("com.ibm.db2.jcc.DB2Driver"); //$NON-NLS-1$
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private static DataSource dataSource = null;
	private ConnectorConfig cfg;
	private String server;
	private int port;
	private int[] offlineStorage;
	private String loadDateField;
	private String odFolderName;
	private String dateFormat;

	public Connector() {
		// dummy 4 testing

	}

	public Connector(final ContextConfig contextConfig) throws Throwable {
		try {
			ctx = ZMQ.context(1);
			if (contextConfig == null) {
				throw new ReportsException(
						ReportsException.ERR_NO_CONTEXTS_AVAILABLE);
			}
			this.cfg = contextConfig.getConnectorConfig();
			this.server = cfg.getServer();
			this.username = cfg.getUsername();
			this.password = cfg.getPassword();
			this.port = cfg.getPort();
			this.offlineStorage = cfg.getOfflineStorage();
			this.loadDateField = contextConfig.getLoadDateField();
			this.odFolderName = contextConfig.getOdfolder();
			if (dataSource == null) {
				synchronized (Connector.class) {
					if (dataSource == null) {
						final BasicDataSource bDataSource = new BasicDataSource();
						bDataSource.setUrl("jdbc:db2://" + server + ":" + port //$NON-NLS-1$ //$NON-NLS-2$
								+ "/ARCHIVE"); //$NON-NLS-1$
						bDataSource.setUsername(username);
						bDataSource.setPassword(password);
						// bDataSource.setAccessToUnderlyingConnectionAllowed(true);

						bDataSource.setMaxActive(Integer.parseInt(ARConfig
								.getMainConfig().getPoolSize()));
						bDataSource.setMinIdle(5);

						dataSource = bDataSource;
					}
				}
			}
			// this.dataHandler = cfg.getDataHandler();
			this.dateFormat = contextConfig.getLoadDateFieldFormat();
			try {
				if (!existFolder()) {
					throw new ReportsException(ReportsException.ERR_GENERIC,
							"Folder not found: " + odFolderName); //$NON-NLS-1$
				}
			} catch (SQLException e) {
				throw new ReportsException(ReportsException.ERR_EXCEPTION_IO,
						"Folder not found: " + e.getMessage()); //$NON-NLS-1$
			}
		} catch (Throwable e) {
			e.printStackTrace();
			throw e;
		}
		// validate(odFolderName);
		// checkParams();

	}

	protected Connection getConnection() throws SQLException {
		return dataSource.getConnection();

	}

	protected boolean existFolder() throws SQLException {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		boolean result = false;

		try {
			conn = getConnection();
			ps = conn
					.prepareStatement("SELECT COUNT(*) from ARSFOL where NAME = ?"); //$NON-NLS-1$
			ps.setString(1, odFolderName);
			rs = ps.executeQuery();
			if (rs.next()) {
				result = rs.getInt(1) > 0;
			}

		} finally {
			if (rs != null) {
				rs.close();
			}
			if (ps != null) {
				ps.close();
			}
			if (conn != null) {
				conn.close();
			}
		}
		return result;
	}

	private static final String sqlTable = "SELECT DISTINCT(SEG.TABLE_NAME) from ARSSEG SEG, ARSAG2FOL AG, ARSFOL FOL where FOL.NAME = ? AND FOL.FID = AG.FID AND AG.AGID=SEG.AGID"; //$NON-NLS-1$

	private static final Map<String, Map<String, String>> appSpecs = new HashMap<String, Map<String, String>>();

	private static final Map<String, DataBean[]> cache = new HashMap<String, DataBean[]>();

	private static final String DATE_TIMESTAMP = "49";

	private static final String DATE_ONLY = "4e";

	private static final String SQL_APPSPEC = "select fixed_view from arsapp where agid = (select agid from arsseg where table_name=?)";

	private synchronized Map<String, String> getAppSpec(final String table)
			throws IOException, SQLException {
		Map<String, String> appSpec = null;
		if (appSpecs.containsKey(table)) {
			appSpec = appSpecs.get(table);
		} else {
			Connection conn = null;
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				conn = getConnection();
				ps = conn.prepareStatement(SQL_APPSPEC);
				ps.setString(1, table);
				rs = ps.executeQuery();
				Clob clob;
				if (rs.next()) {
					final ResultSetMetaData meta = rs.getMetaData();

					appSpec = new HashMap<String, String>();
					boolean start = false;

					Reader reader;
					switch (meta.getColumnType(1)) {
					case Types.LONGVARCHAR:
						reader = new StringReader(rs.getString(1));
						break;
					case Types.CLOB:
						clob = rs.getClob(1);
						reader = clob.getCharacterStream();
						break;
					default:
						throw new IllegalArgumentException(
								"Unknown data type: "
										+ meta.getColumnTypeName(1));
					}
					final BufferedReader bReader = new BufferedReader(reader);
					String line;
					line = bReader.readLine();
					if (line != null) {

						String[] val;
						do {
							if (start
									&& (line.startsWith("CC")
											|| line.startsWith("RECDELIM")
											|| line.startsWith("CDPG")
											|| line.startsWith("LRECL") || line
											.startsWith("RECFM"))) {
								val = line.split("=");
								if (val.length == 2) {
									appSpec.put(val[0], val[1]);
								}
							}
							if ("[@_FIXED_@]".equals(line)) {
								start = true;
							}
							line = bReader.readLine();
						} while (line != null);
					}

					appSpecs.put(table, appSpec);
					// System.out.println(appSpec);

				}
			} finally {
				try {
					if (rs != null) {
						rs.close();
					}
					if (ps != null) {
						ps.close();
					}
					if (conn != null) {
						conn.close();
					}
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return appSpec;
	}

	private static final TimeZone DEFAULT_TZ = TimeZone.getDefault();

	private static long toLongArsDate(final Date date) {
		return (date.getTime() + DEFAULT_TZ.getOffset(date.getTime())) / 1000;
	}

	private static short toShortArsDate(final Date date) {
		return (short) (1 + date.getTime() / 24 / 60 / 60 / 1000);
	}

	private static Date fromArsDate(final long arsDate) {
		return new Date(arsDate * 1000 - DEFAULT_TZ.getOffset(arsDate * 1000));
	}

	private static Date fromArsDate(final short arsDate) {
		return fromArsDate((arsDate - 1) * 24 * 60 * 60);
	}

	private static String translateDateFormat(String odDateFormat) {
		return odDateFormat.replaceAll("%d", "dd").replaceAll("%m", "MM") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
				.replaceAll("%Y", "yyyy").replaceAll("%H", "HH") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
				.replaceAll("%M", "mm"); //$NON-NLS-1$ //$NON-NLS-2$
	}

	private static final String SQL_FIELDS = "select arsfolfld.name, arsfolfld.type,arsfolfld.qual, arsfolfldusr.datefmt,arsfolfldusr.dspl_datefmt, arsagfld.name as agField from arsfol inner join arsfolfld on arsfol.fid=arsfolfld.fid inner join arsfolfldusr on arsfol.fid=arsfolfldusr.fid and arsfolfld.field=arsfolfldusr.field inner join arsag2fol on arsfolfld.FID=arsag2fol.fid  and arsfolfld.field=arsag2fol.folder_field inner join arsagfld on arsag2fol.agid=arsagfld.agid and arsag2fol.appgrp_field1=arsagfld.field  where arsfol.name=?"; //$NON-NLS-1$
	private static Map<String, Map<String, Map<String, String>>> folderFields = new HashMap<String, Map<String, Map<String, String>>>();

	private void checkFolderFields(String foldername) {
		if (!folderFields.containsKey(foldername)) {
			Connection conn = null;
			PreparedStatement ps = null;
			ResultSet rs = null;
			try {
				conn = getConnection();
				ps = conn.prepareStatement(SQL_FIELDS);
				ps.setString(1, foldername);
				rs = ps.executeQuery();
				Map<String, String> field;
				final Map<String, Map<String, String>> folder = new HashMap<String, Map<String, String>>();
				if (rs.next()) {
					String data;
					do {
						field = new HashMap<String, String>(5);
						field.put("type", rs.getString("type")); //$NON-NLS-1$ //$NON-NLS-2$
						field.put("qual", rs.getString("qual")); //$NON-NLS-1$ //$NON-NLS-2$
						data = rs.getString("datefmt");
						if (data != null && data.length() != 0) {
							field.put("dateFormat", data); //$NON-NLS-1$ //$NON-NLS-2$
						}

						data = rs.getString("dspl_datefmt");
						if (data != null && data.length() != 0) {
							field.put("displayFormat", data); //$NON-NLS-1$ //$NON-NLS-2$
						}
						field.put("appGrpField", rs.getString("agField"));
						data = rs.getString("name").toUpperCase();
						System.out.println(data + ": " + field);

						folder.put(data, field); //$NON-NLS-1$

					} while (rs.next());
					folderFields.put(foldername, folder);
				}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				try {
					if (rs != null) {
						rs.close();
					}
					if (ps != null) {
						ps.close();
					}
					if (conn != null) {
						conn.close();
					}
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	private DataBean getBean(final String folder, final ResultSet rs,
			final String table, final int cols, final String[] names,
			boolean fillBinary) throws Exception {

		final DataBean bean = new DataBean();

		Map<String, String> indexes = new HashMap<String, String>();
		String fieldName = null;
		Map<String, String> field = null;
		Map<String, Map<String, String>> fields = folderFields.get(folder);
		for (int k = 1; k <= cols - 10; k++) {
			// System.out.println(names[k - 1]);
			for (Map.Entry<String, Map<String, String>> tmpField : fields
					.entrySet()) {
				if (names[k - 1].equals(tmpField.getValue().get("appGrpField"))) {
					fieldName = tmpField.getKey();
					field = tmpField.getValue();
					break;
				}
			}
			// field = .get(names[k - 1]);
			// System.out.println("Checking field:" + fieldName + " / " +
			// field);
			//LOG.info("FIELD: " + field);
			if (field == null) {
				System.err.println("Unknown field: " + names[k - 1]);
			} else {
				String type = field.get("type");
				if ((DATE_TIMESTAMP.equals(type) && "43".equals(field
						.get("qual"))) || DATE_ONLY.equals(type)) {
					Date date;
					if (DATE_ONLY.equals(type)) {
						// 4e: date only
						date = fromArsDate(Short.parseShort(rs.getString(k)));
					} else {
						date = fromArsDate(Long.parseLong(rs.getString(k)));
					}
					SimpleDateFormat df = new SimpleDateFormat(field
							.get("displayFormat").replace("%d", "dd")
							.replace("%b", "MMM").replace("%m", "MM")
							.replace("%Y", "yyyy").replace("%H", "hh")
							.replace("%M", "mm").replace("%S", "ss")
							.replace("%", ""));
					String strDate = df.format(date);
					// System.out.println("Fecha " + type + ": " + names[k - 1]
					// + " / " + strDate);
					indexes.put(fieldName, strDate);
				} else {
					indexes.put(fieldName, rs.getString(k));
				}
			}
		}
		// System.out.println(indexes);
		bean.setIndexes(indexes);

		bean.setId(getId(table, rs));
		long dt = rs.getLong(loadDateField);
		bean.setLoadDate(fromArsDate(dt));

		if (fillBinary) {
			InputStream is = rs.getBinaryStream(COMP_TYPE);
			int compType = is.read();

			byte[] data = getByte(table, rs.getString(DOC_NAME),
					rs.getInt(DOC_OFF), rs.getInt(DOC_LEN),
					rs.getInt(COMP_OFF), rs.getInt(COMP_LEN), (char) compType);
			if (data == null) {
				throw new IllegalArgumentException(
						"No document received from server"); //$NON-NLS-1$
			}
			bean.setFileData(data);
		}
		return bean;
	}

	private static final char HYPHEN = '-';

	private String getId(final String table, final ResultSet rs)
			throws SQLException {
		StringBuffer sb = new StringBuffer(table);
		sb.append(HYPHEN);
		sb.append(rs.getString(DOC_NAME));
		sb.append(HYPHEN);
		sb.append(rs.getString(DOC_OFF));
		sb.append(HYPHEN);
		sb.append(rs.getString(DOC_LEN));
		sb.append(HYPHEN);
		sb.append(rs.getString(COMP_OFF));
		sb.append(HYPHEN);
		sb.append(rs.getString(COMP_LEN));

		return sb.toString();
	}

	private static String cacheRoot = Messages.getString("Connector.arscache"); //$NON-NLS-1$
	private static final String ENDPOINT = Messages
			.getString("Connector.endpoint"); //$NON-NLS-1$
	private Context ctx;

	private static final String OPERATOR_SUFFIX = "_op";

	private byte[] getByte(final String table, final String name,
			final int doc_off, final int doc_len, final int comp_off,
			final int comp_len, final char compressionType) throws Exception {
		File file = new File(cacheRoot, table.substring(0, 3) + "/DOC/" + name); //$NON-NLS-1$
		// Context ctx = ZMQ.context(1);
		Socket req = ctx.socket(ZMQ.REQ);
		req.connect(ENDPOINT);

		// TODO Crear POJO en vez de Map
		Map<String, String> params = new HashMap<String, String>();
		params.put("path", file.getAbsolutePath());
		params.put("dOff", String.valueOf(doc_off));
		params.put("dLen", String.valueOf(doc_len));
		params.put("cOff", String.valueOf(comp_off));
		params.put("clen", String.valueOf(comp_len));
		params.put("cType", String.valueOf(compressionType));

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		oos.writeObject(params);
		oos.close();
		params.clear();
		baos.close();

		LOG.info("Sending Request");
		req.send(baos.toByteArray(), NO_FLAGS);
		LOG.info("Request sent");
		byte[] data = req.recv();
		LOG.info("Response received");
		req.close();
		// ctx.term();
		return data;
	}

	private String getWhereClause(final Map<String, String> searchCriteria) {
		StringBuffer where = new StringBuffer();
		int count = 1;
		int op;
		final Map<String, String> filteredCriteria = new HashMap<String, String>();

		for (Map.Entry<String, String> criteria : searchCriteria.entrySet()) {
			String param = criteria.getKey();
			if (!param.equals(ONLY_LAST_ITEM)
					&& !param.endsWith(OPERATOR_SUFFIX)
					&& (!param.endsWith("2"))) {
				filteredCriteria.put(param, criteria.getValue());
			}
		}
		for (String param : filteredCriteria.keySet()) {
			where.append(param);
			if (searchCriteria.keySet().contains(param + OPERATOR_SUFFIX)) {
				op = Integer.valueOf(searchCriteria
						.get(param + OPERATOR_SUFFIX));
				switch (op) {
				case OPERATOR_EQUALS:
					where.append(" = ?"); //$NON-NLS-1$
					break;
				case OPERATOR_LIKE:
					where.append(" LIKE ?"); //$NON-NLS-1$
					break;
				case OPERATOR_BETWEEN:
					where.append(" BETWEEN ? AND ?"); //$NON-NLS-1$
					break;
				default:
					throw new IllegalArgumentException(
							"Operator not supported: " + op);
				}
			} else {
				where.append(" = ?"); //$NON-NLS-1$
			}
			if (count++ < filteredCriteria.size()) {
				where.append(" AND "); //$NON-NLS-1$
			}

		}
		// System.out.println(where);
		return where.toString();

	}

	private static Map<String, List<String>> folderTableCache = new HashMap<String, List<String>>();
	private static Map<String, Long> folderTtlCache = new HashMap<String, Long>();

	private static final long CACHE_TTL = 60 * 1000;

	protected List<String> getTables(final String folder) throws SQLException {
		List<String> tables = null;

		if (folderTableCache.containsKey(folder)) {
			if (folderTtlCache.containsKey(folder)
					&& folderTtlCache.get(folder) + CACHE_TTL < System
							.currentTimeMillis()) {
				synchronized (folderTableCache) {
					if (folderTtlCache.containsKey(folder)
							&& folderTtlCache.get(folder) + CACHE_TTL < System
									.currentTimeMillis()) {
						//						System.out.println("Cache expired, refreshing"); //$NON-NLS-1$
						tables = refreshFolderTables(folder);
					} else {
						tables = folderTableCache.get(folder);
					}
				}
			} else {
				tables = folderTableCache.get(folder);
			}
		} else {
			synchronized (folderTableCache) {
				if (folderTableCache.containsKey(folder)) {
					tables = folderTableCache.get(folder);
				} else {
					//System.out.println("Initializing cache"); //$NON-NLS-1$
					tables = refreshFolderTables(folder);
				}
			}
		}
		return tables;
	}

	private List<String> refreshFolderTables(final String folder)
			throws SQLException {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;

		ArrayList<String> tables = null;
		try {
			conn = getConnection();
			ps = conn.prepareStatement(sqlTable);
			ps.setString(1, folder);
			rs = ps.executeQuery();

			if (rs.next()) {
				tables = new ArrayList<String>();
				do {
					tables.add(rs.getString(1));
				} while (rs.next());
			}

		} finally {
			if (rs != null) {
				rs.close();
			}
			if (ps != null) {
				ps.close();
			}
			if (conn != null) {
				conn.close();
			}
		}
		folderTableCache.put(folder, tables);
		folderTtlCache.put(folder, System.currentTimeMillis());
		return tables;
	}

	public int getHitCount(Map<String, String> searchCriteria, String folder) {
		DataBean[] data = getDocuments(searchCriteria, folder, false);
		return data == null ? 0 : data.length;
	}

	public DataBean[] getDocList(Map<String, String> searchCriteria,
			String folder) {
		return getDocuments(searchCriteria, folder, false);
	}

	public DataBean[] getDocuments(final Map<String, String> searchCriteria,
			final String folder) {
		return getDocuments(searchCriteria, folder, true);
	}	

	private DataBean[] getDocuments(final Map<String, String> searchCriteria,
			final String folder, boolean fillBinary) {

		long start = System.currentTimeMillis();
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		DataBean[] result = null;
		checkFolderFields(folder);
		StringBuffer cacheKey = new StringBuffer(folder);
		for (Entry<String, String> entry : searchCriteria.entrySet()) {
			cacheKey.append(entry.getKey());
			cacheKey.append(entry.getValue());
		}

		if (cache.containsKey(cacheKey.toString())) {
			result = cache.get(cacheKey.toString());
		} else {
			try {
				conn = getConnection();
				List<String> tables = getTables(folder);
				String whereClause = getWhereClause(searchCriteria);
				if (searchCriteria.containsKey(ONLY_LAST_ITEM)
						&& Boolean.valueOf(searchCriteria.get(ONLY_LAST_ITEM))) {
					whereClause += " ORDER BY " + loadDateField
							+ " DESC FETCH FIRST 1 ROWS ONLY";
				}
				LOG.info("SQL PREDICATE: " + whereClause);
				Map<String, String> tableSpec;
				for (String table : tables) {
					//LOG.info("TABLE: " + table);
					if (appSpecs.containsKey(table)) {
						tableSpec = appSpecs.get(table);
					} else {
						tableSpec = getAppSpec(table);
					}
					// System.out.println("table: " + table);
					final String sql = "select * from " + table + " where "
							+ whereClause;
					LOG.info("SQL QUERY: " + sql);
					// System.out.println(sql);
					ps = conn.prepareStatement(sql);
					// System.out.println(table);
					int i = 1;
					Map<String, String> field;
					String type;
					for (Entry<String, String> param : searchCriteria
							.entrySet()) {
						LOG.info("PARAM: [" + param.getKey() + " / "
								+ param.getValue() + "]");
						field = folderFields.get(folder).get(
								param.getKey().toUpperCase());
						if (field != null) {
							type = field.get("type"); //$NON-NLS-1$
							if ((DATE_TIMESTAMP.equals(type) && "43"
									.equals(field.get("qual"))) || DATE_ONLY.equals(type)) { //$NON-NLS-1$ //$NON-NLS-2$
								SimpleDateFormat sdf = new SimpleDateFormat(
										translateDateFormat(field
												.get("displayFormat"))); //$NON-NLS-1$
								Date date = sdf.parse(param.getValue());
								if (DATE_ONLY.equals(type)) { //$NON-NLS-1$
									ps.setLong(i++, toShortArsDate(date));
								} else if (DATE_TIMESTAMP.equals(type)) { //$NON-NLS-1$
									ps.setLong(i++, toLongArsDate(date));
								}
							} else {
								ps.setObject(i++, param.getValue());
							}
						}
					}

					rs = ps.executeQuery();

					if (rs.next()) {
						List<DataBean> beans = new ArrayList<DataBean>();

						int cols = rs.getMetaData().getColumnCount();
						String[] names = new String[cols];
						for (int j = 1; j <= cols; j++) {
							names[j - 1] = rs.getMetaData().getColumnName(j);
						}
						// int count = 1;
						try {
							DataBean bean;
							do {
								bean = getBean(folder, rs, table, cols, names,
										fillBinary);
								// TODO Fix missing method
								// bean.setAppSpec(tableSpec);

								beans.add(bean);
								// System.out.println(count++);
							} while (rs.next());

						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						result = new DataBean[beans.size()];
						int l = 0;
						for (DataBean bean : beans) {
							result[l++] = bean;
						}
					}
				}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				try {
					if (rs != null) {
						rs.close();
					}
					if (ps != null) {
						ps.close();
					}
					if (conn != null) {
						conn.close();
					}
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (result != null) {
					// TODO Enable cache
					// cache.put(cacheKey.toString(), result);
				}
			}
		}
		// StatsCollector.push(folder, System.currentTimeMillis() - start);
		return result;
	}

	public DataBean getDocument(Map<String, String> searchCriteria,
			String folder) {
		DataBean[] beans = getDocuments(searchCriteria, folder);
		if (beans == null || beans.length != 1) {
			throw new IllegalArgumentException("Invalid results count");
		}
		return beans[0];
	}

	public String[] getIndexes() {
		Map<String, Map<String, String>> fields = folderFields
				.get(odFolderName);
		String[] indexes = new String[fields.size()];
		int count = 0;
		for (String name : fields.keySet()) {
			indexes[count++] = name;
		}
		return indexes;
	}

	public List<Index> getIndexesInfo() {
		checkFolderFields(odFolderName);

		Map<String, Map<String, String>> fields = folderFields
				.get(odFolderName);

		List<Index> indexes = new ArrayList<Index>(fields.size());
		Map<String, String> values;
		for (Map.Entry<String, Map<String, String>> field : fields.entrySet()) {
			values = field.getValue();
			indexes.add(new Index(field.getKey(), values.get("qual").charAt(0),
					values.get("displayFormat")));
		}
		return indexes;
	}

	public String getDisplayName() {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		String result = null;

		try {
			conn = getConnection();
			ps = conn
					.prepareStatement("SELECT DESCRIPTION from ARSFOL where NAME = ?"); //$NON-NLS-1$
			ps.setString(1, odFolderName);
			rs = ps.executeQuery();
			if (rs.next()) {
				result = rs.getString(1);
			}

		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (rs != null) {

					rs.close();
				}
				if (ps != null) {
					ps.close();
				}
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return result;
	}

	public DataBean getDocument(String docId, String folder) {
		throw new UnsupportedOperationException();
	}
}
