/*
 * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2019 IBM Corporation. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.databaseaccess;

import java.sql.*;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;

public class OutputParameterForCallableStatement extends BindCallCustomParameter {
    // this attribute is provided by the caller
    protected boolean isCursor;

    // these attributes are generated by prepare method
    protected int jdbcType;
    protected String typeName;
    protected boolean isTypeNameRequired;

    protected transient DatabasePlatform dbplatform = null;

    public OutputParameterForCallableStatement(DatabaseField field) {
        super(field);
    }

    public OutputParameterForCallableStatement(DatabaseField field, AbstractSession session) {
        this(field, session, false);
    }

    public OutputParameterForCallableStatement(DatabaseField field, AbstractSession session, boolean isCursor) {
        this(field);
        this.isCursor = isCursor;
        prepare(session);
    }

    public OutputParameterForCallableStatement(OutputParameterForCallableStatement outParameter) {
        super(outParameter.obj);
        this.isCursor = outParameter.isCursor;
        this.jdbcType = outParameter.jdbcType;
        this.typeName = outParameter.typeName;
        this.isTypeNameRequired = outParameter.isTypeNameRequired;
    }

    protected OutputParameterForCallableStatement() {
    }

    public void setIsCursor(boolean isCursor) {
        this.isCursor = isCursor;
    }

    // make sure to call prepare() method after this
    public boolean isCursor() {
        return isCursor;
    }

    public boolean isTypeNameRequired() {
        return isTypeNameRequired;
    }

    public int getJdbcType() {
        return jdbcType;
    }

    public String getTypeName() {
        return typeName;
    }

    public DatabaseField getOutputField() {
        return (DatabaseField)obj;
    }

    public void prepare(AbstractSession session) {
        dbplatform = session.getPlatform();
        if (isCursor()) {
            jdbcType = dbplatform.getCursorCode();// Oracle code for cursors
        } else {
            jdbcType = dbplatform.getJDBCType(getOutputField());
            if (obj instanceof ObjectRelationalDatabaseField) {
                isTypeNameRequired = true;
                typeName = ((ObjectRelationalDatabaseField)obj).getSqlTypeName();
            } else {
                isTypeNameRequired = dbplatform.requiresTypeNameToRegisterOutputParameter();
                if (isTypeNameRequired) {
                    typeName = dbplatform.getJdbcTypeName(jdbcType);
                }
            }
        }
    }

    @Override
    public void set(DatabasePlatform platform, PreparedStatement statement, int parameterIndex, AbstractSession session) throws SQLException {
        if (isTypeNameRequired) {
            platform.registerOutputParameter((CallableStatement)statement, parameterIndex, jdbcType, typeName);
        } else {
            platform.registerOutputParameter((CallableStatement)statement, parameterIndex, jdbcType);
        }
    }

    @Override
    public void set(DatabasePlatform platform, CallableStatement statement, String parameterName, AbstractSession session) throws SQLException {
        if (isTypeNameRequired) {
            platform.registerOutputParameter(statement, parameterName, jdbcType, typeName);
        } else {
            platform.registerOutputParameter(statement, parameterName, jdbcType);
        }
    }

    public String toString() {
        return "=> " + getOutputField().getNameDelimited(dbplatform);
    }
}
