/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.modules.eql.parser.generic;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.modules.config.utils.EntitySchema;
import com.queplix.core.modules.config.utils.db.DBEntitySchema;
import com.queplix.core.modules.eql.EQLERes;
import com.queplix.core.modules.eql.EQLReq;
import com.queplix.core.modules.eql.EQLRes;
import com.queplix.core.modules.eql.EQLResRecord;
import com.queplix.core.modules.eql.EQLSession;
import com.queplix.core.modules.eql.EQLSql;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eql.jxb.eqlagent.Property;
import com.queplix.core.modules.eql.parser.SQLEQLAgent;
import com.queplix.core.modules.eql.parser.SQLEQLPluggableModule;
import com.queplix.core.modules.eql.parser.SQLExecutor;
import com.queplix.core.modules.eql.parser.SQLSelectBuilder;
import com.queplix.core.modules.eql.parser.SQLUpdateBuilder;
import com.queplix.core.utils.log.AbstractLogger;

/**
 * <p>EQL agent abstract class</p>
 *
 * @author Baranov Andrey [ALB]
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:30:28 $
 */

public class EQLAgentGenericImpl
        extends AbstractLogger
        implements SQLEQLAgent {

    // --------------------------------------------------------------- fields/constants

    /**
     * Properties.
     */
    protected static final String BUILDER_SELECT_CLASS_PROPERTY
            = "builder.select.class";
    protected static final String BUILDER_UPDATE_CLASS_PROPERTY
            = "builder.update.class";
    protected static final String EXECUTOR_CLASS_PROPERTY = "executor.class";

    // Schema.
    private DBEntitySchema schema;

    // Parsers.
    private Class sqlSelectBuilderClass;
    private Class sqlUpdateBuilderClass;
    private Class sqlExecutorClass;

    // --------------------------------------------------------------- interface implementation

    /*
     * No javadoc
     * @see EQLAgent#init
     */

    public void init(EntitySchema schema, Property[] properties) {

        if(!(schema instanceof DBEntitySchema)) {
            throw new GenericSystemException(
                    "Only database entity schemas supported!");
        }

        this.schema = (DBEntitySchema) schema;

        if(properties != null) {
            try {
                for(int i = 0; i < properties.length; i++) {
                    Property p = properties[i];
                    String name = p.getName();
                    String value = p.getValue();

                    if(name.equalsIgnoreCase(BUILDER_SELECT_CLASS_PROPERTY)) {
                        sqlSelectBuilderClass = Class.forName(value);
                    } else if(name.equalsIgnoreCase(
                            BUILDER_UPDATE_CLASS_PROPERTY)) {
                        sqlUpdateBuilderClass = Class.forName(value);
                    } else if(name.equalsIgnoreCase(EXECUTOR_CLASS_PROPERTY)) {
                        sqlExecutorClass = Class.forName(value);
                    }
                }
            } catch (ClassNotFoundException ex) {
                ERROR(ex);
                throw new GenericSystemException(ex);
            }
        }
        if(sqlSelectBuilderClass == null) {
            throw new GenericSystemException("Cannot find '"
                    + BUILDER_SELECT_CLASS_PROPERTY + "' property...");
        }
        if(sqlUpdateBuilderClass == null) {
            throw new GenericSystemException("Cannot find '"
                    + BUILDER_UPDATE_CLASS_PROPERTY + "' property...");
        }
        if(sqlExecutorClass == null) {
            throw new GenericSystemException("Cannot find '"
                    + EXECUTOR_CLASS_PROPERTY + "' property...");
        }
    }

    /*
     * No javadoc
     * @see EQLAgent#getEntitySchema
     */
    public EntitySchema getEntitySchema() {
        return schema;
    }

    /*
     * No javadoc
     * @see EQLAgent#doCount
     */
    public int doCount(EQLSession eqlSession, EQLRes res)
            throws EQLException {
        return getSQLExecutor(eqlSession).doCount(
                res.getMetaData().getEQLSql());
    }

    /*
     * No javadoc
     * @see EQLAgent#doSelect
     */
    public EQLRes doSelect(EQLSession eqlSession, EQLReq req)
            throws EQLException {

        // Build main SQL.
        EQLSql eqlSql = getSelectSQLBuilder(eqlSession).buildSelectSql(req);

        // Execute SQL.
        EQLRes res = getSQLExecutor(eqlSession).doSelect(req, eqlSql);

        DEBUG("...execute EQL request completed");

        return res;
    }

    /*
     * No javadoc
     * @see EQLAgent#doUpdate
     */
    public int doUpdate(EQLSession eqlSession, EQLERes res,
                        EQLResRecord resRecord)
            throws EQLException {

        SQLUpdateBuilder sqlBuilder = getUpdateSQLBuilder(eqlSession);

        // Build SQL to update master record.
        EQLSql eqlSql;
        if(resRecord.isNew()) {
            eqlSql = sqlBuilder.buildInsertSql(res, resRecord);
        } else {
            eqlSql = sqlBuilder.buildUpdateSql(res, resRecord);
        }

        // Execute SQL query.
        int rows = 0;
        if(eqlSql != null && eqlSql.getMainSql() != null) {
            rows = getSQLExecutor(eqlSession).doUpdate(res, eqlSql);
        }

        DEBUG("...updated " + rows + " rows");

        return rows;
    }

    /*
     * No javadoc
     * @see EQLAgent#doDelete
     */
    public int doDelete(EQLSession eqlSession, EQLERes res,
                        EQLResRecord resRecord)
            throws EQLException {

        SQLUpdateBuilder sqlBuilder = getUpdateSQLBuilder(eqlSession);

        // Build SQL to delete master record.
        EQLSql eqlSql = sqlBuilder.buildDeleteSql(res, resRecord);

        // Execute SQL query.
        int rows = 0;
        if(eqlSql != null && eqlSql.getMainSql() != null) {
            rows = getSQLExecutor(eqlSession).doUpdate(res, eqlSql);
        }

        DEBUG("...deleted " + rows + " rows");

        return rows;
    }

    /*
     * No javadoc
     * @see SQLEQLAgent#getSelectSQLBuilder
     */
    public SQLSelectBuilder getSelectSQLBuilder(EQLSession session) {
        return (SQLSelectBuilder) getEqlPluggableModule(sqlSelectBuilderClass,
                session);
    }

    /*
     * No javadoc
     * @see SQLEQLAgent#getUpdateSQLBuilder
     */
    public SQLUpdateBuilder getUpdateSQLBuilder(EQLSession session) {
        return (SQLUpdateBuilder) getEqlPluggableModule(sqlUpdateBuilderClass,
                session);
    }

    /*
     * No javadoc
     * @see SQLEQLAgent#getSQLExecutor
     */
    public SQLExecutor getSQLExecutor(EQLSession session) {
        return (SQLExecutor) getEqlPluggableModule(sqlExecutorClass, session);
    }

    // --------------------------------------------------------------- private methods

    //
    // Instantiates EQL pluggable module.
    //

    private SQLEQLPluggableModule getEqlPluggableModule(Class clazz,
                                                        EQLSession session) {
        try {
            SQLEQLPluggableModule module = (SQLEQLPluggableModule) clazz
                    .newInstance();
            module.setSession(session);
            module.setSqlWrapper(schema.getSqlWrapper());

            return module;

        } catch (Exception ex) {
            ERROR(ex);
            throw new GenericSystemException(
                    "Can't instantiate object from class '" +
                            clazz + "'.", ex);
        }
    }
}