/*
 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.value;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet;
import org.h2.util.StatementBuilder;

/**
 * Implementation of the RESULT_SET data type.
 */
public class ValueResultSet extends Value {

    private final ResultSet result;

    private ValueResultSet(ResultSet rs) {
        this.result = rs;
    }

    /**
     * Create a result set value for the given result set.
     * The result set will be wrapped.
     *
     * @param rs the result set
     * @return the value
     */
    public static ValueResultSet get(ResultSet rs) {
        ValueResultSet val = new ValueResultSet(rs);
        return val;
    }

    /**
     * Create a result set value for the given result set. The result set will
     * be fully read in memory. The original result set is not closed.
     *
     * @param rs the result set
     * @param maxrows the maximum number of rows to read (0 to just read the
     *            meta data)
     * @return the value
     */
    public static ValueResultSet getCopy(ResultSet rs, int maxrows) {
        try {
            ResultSetMetaData meta = rs.getMetaData();
            int columnCount = meta.getColumnCount();
            SimpleResultSet simple = new SimpleResultSet();
            simple.setAutoClose(false);
            ValueResultSet val = new ValueResultSet(simple);
            for (int i = 0; i < columnCount; i++) {
                String name = meta.getColumnLabel(i + 1);
                int sqlType = meta.getColumnType(i + 1);
                int precision = meta.getPrecision(i + 1);
                int scale = meta.getScale(i + 1);
                simple.addColumn(name, sqlType, precision, scale);
            }
            for (int i = 0; i < maxrows && rs.next(); i++) {
                Object[] list = new Object[columnCount];
                for (int j = 0; j < columnCount; j++) {
                    list[j] = rs.getObject(j + 1);
                }
                simple.addRow(list);
            }
            return val;
        } catch (SQLException e) {
            throw DbException.convert(e);
        }
    }

    @Override
    public int getType() {
        return Value.RESULT_SET;
    }

    @Override
    public long getPrecision() {
        return Integer.MAX_VALUE;
    }

    @Override
    public int getDisplaySize() {
        // it doesn't make sense to calculate it
        return Integer.MAX_VALUE;
    }

    @Override
    public String getString() {
        try {
            StatementBuilder buff = new StatementBuilder("(");
            result.beforeFirst();
            ResultSetMetaData meta = result.getMetaData();
            int columnCount = meta.getColumnCount();
            for (int i = 0; result.next(); i++) {
                if (i > 0) {
                    buff.append(", ");
                }
                buff.append('(');
                buff.resetCount();
                for (int j = 0; j < columnCount; j++) {
                    buff.appendExceptFirst(", ");
                    int t = DataType.getValueTypeFromResultSet(meta, j + 1);
                    Value v = DataType.readValue(null, result, j+1, t);
                    buff.append(v.getString());
                }
                buff.append(')');
            }
            result.beforeFirst();
            return buff.append(')').toString();
        } catch (SQLException e) {
            throw DbException.convert(e);
        }
    }

    @Override
    protected int compareSecure(Value v, CompareMode mode) {
        return this == v ? 0 : super.toString().compareTo(v.toString());
    }

    @Override
    public boolean equals(Object other) {
        return other == this;
    }

    @Override
    public int hashCode() {
        return 0;
    }

    @Override
    public Object getObject() {
        return result;
    }

    @Override
    public ResultSet getResultSet() {
        return result;
    }

    @Override
    public void set(PreparedStatement prep, int parameterIndex) {
        throw throwUnsupportedExceptionForType("PreparedStatement.set");
    }

    @Override
    public String getSQL() {
        return "";
    }

    @Override
    public Value convertPrecision(long precision, boolean force) {
        if (!force) {
            return this;
        }
        SimpleResultSet rs = new SimpleResultSet();
        rs.setAutoClose(false);
        return ValueResultSet.get(rs);
    }

}
