/*
 * 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;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.modules.eql.EQLDReq;
import com.queplix.core.modules.eql.EQLObject;
import com.queplix.core.modules.eql.EQLReq;
import com.queplix.core.modules.eql.EQLReqOp;
import com.queplix.core.modules.eql.EQLReqSubOp;
import com.queplix.core.modules.eql.EQLReqSubOpMember;
import com.queplix.core.modules.eql.EQLReqSubOpMemberEnum;
import com.queplix.core.modules.eql.EQLReqSubOpMemberField;
import com.queplix.core.modules.eql.EQLReqSubOpMemberUnknown;
import com.queplix.core.modules.eql.EQLReqWhere;
import com.queplix.core.modules.eql.EQLSql;
import com.queplix.core.modules.eql.aggs.CountAggFunc;
import com.queplix.core.modules.eql.aggs.MaxAggFunc;
import com.queplix.core.modules.eql.aggs.MinAggFunc;
import com.queplix.core.modules.eql.conds.EqCond;
import com.queplix.core.modules.eql.conds.GtCond;
import com.queplix.core.modules.eql.conds.GtEqCond;
import com.queplix.core.modules.eql.conds.InCond;
import com.queplix.core.modules.eql.conds.IsNotNullCond;
import com.queplix.core.modules.eql.conds.IsNullCond;
import com.queplix.core.modules.eql.conds.LikeCond;
import com.queplix.core.modules.eql.conds.LtCond;
import com.queplix.core.modules.eql.conds.LtEqCond;
import com.queplix.core.modules.eql.conds.NoneCond;
import com.queplix.core.modules.eql.conds.NotEqCond;
import com.queplix.core.modules.eql.conds.NotInCond;
import com.queplix.core.modules.eql.conds.NotLikeCond;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.eql.funcs.DateDiffFunc;
import com.queplix.core.modules.eql.funcs.FTSFunc;
import com.queplix.core.modules.eql.funcs.IsNullFunc;
import com.queplix.core.modules.eql.funcs.LowerFunc;
import com.queplix.core.modules.eql.funcs.SoundexFunc;
import com.queplix.core.modules.eql.funcs.UpperFunc;
import com.queplix.core.modules.eql.ops.DivOp;
import com.queplix.core.modules.eql.ops.MinusOp;
import com.queplix.core.modules.eql.ops.MultOp;
import com.queplix.core.modules.eql.ops.NoneOp;
import com.queplix.core.modules.eql.ops.PlusOp;

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

public abstract class SQLSelectBuilder
        extends SQLBuilder {

// -------------------- VARIABLES --------------------------

    protected EQLReq req;

    protected final StringBuffer selectClause = new StringBuffer();
    protected final StringBuffer hint = new StringBuffer();
    protected final StringBuffer fromClause = new StringBuffer();
    protected final StringBuffer whereClause = new StringBuffer();
    protected final StringBuffer groupByClause = new StringBuffer();
    protected final StringBuffer orderClause = new StringBuffer();

// ------------------ PUBLIC METHODS -----------------------

    /**
     * Build select Sql query
     *
     * @param req EQLReq object
     * @return new EQLSql object
     * @throws EQLException
     */
    public final EQLSql buildSelectSql(EQLReq req)
            throws EQLException {

        this.req = req;

        // init main sql clauses
        begin();
        addSelectHint();
        int added = addSelectClause();
        if(added == 0) {
            DEBUG("Nothing added!");

        } else {
            addFromClause();
            addWhereClause();
            addGroupByClause();
            addOrderClause();
            String mainSql = end();

            // init count sql
            String countSql = getCountSql(mainSql);

            // set sql and sql parameters
            eqlSql.setMainSql(mainSql);
            eqlSql.setCountSql(countSql);
            eqlSql.setSqlParameters(getSqlParameters());
        }

        // build dataset EQLSql objects
        for(int i = 0; i < req.getDReqSize(); i++) {
            EQLDReq eqlDReq = req.getDReq(i);
            SQLEQLAgent agent = (SQLEQLAgent) EQLAgentFactory.getInstance()
                    .getEQLAgent(eqlDReq);
            EQLSql datasetEQLSql = agent.getSelectSQLBuilder(getSession())
                    .buildSelectSql(eqlDReq);
            eqlSql.addDatasetEqlSql(datasetEQLSql);
        }

        return eqlSql;
    }

// -------------- PROTECTED METHODS ---------------

    /**
     * Start query building
     *
     * @throws EQLException
     */
    protected void begin()
            throws EQLException {
        // empty
    }

    /**
     * End query building
     *
     * @return final sql
     * @throws EQLException
     */
    protected String end()
            throws EQLException {
        return constructSql(req);
    }

// -------------- PROTECTED ABSTRACT METHODS ---------------

    /**
     * Add select hints
     *
     * @throws EQLException
     */
    protected abstract void addSelectHint()
            throws EQLException;

    /**
     * Add select clause
     *
     * @return number of added fields (0 - nothing added)
     * @throws EQLException
     */
    protected abstract int addSelectClause()
            throws EQLException;

    /**
     * Add from clause
     *
     * @throws EQLException
     */
    protected abstract void addFromClause()
            throws EQLException;

    /**
     * Add where clause
     *
     * @throws EQLException
     */
    protected abstract void addWhereClause()
            throws EQLException;

    /**
     * Add group by clause
     *
     * @throws EQLException
     */
    protected abstract void addGroupByClause()
            throws EQLException;

    /**
     * Add order clause
     *
     * @throws EQLException
     */
    protected abstract void addOrderClause()
            throws EQLException;

    /**
     * Constrcut sql for counting
     *
     * @param mainSql main sql
     * @return count sql
     * @throws EQLException
     */
    protected abstract String getCountSql(String mainSql)
            throws EQLException;

    //
    // Conditions
    //

    protected abstract String getSQLCond(EQLReqWhere reqWhere)
            throws EQLException;

    protected abstract String getSQLCond(NoneCond cond)
            throws EQLException;

    protected abstract String getSQLCond(EqCond cond)
            throws EQLException;

    protected abstract String getSQLCond(GtCond cond)
            throws EQLException;

    protected abstract String getSQLCond(LtCond cond)
            throws EQLException;

    protected abstract String getSQLCond(GtEqCond cond)
            throws EQLException;

    protected abstract String getSQLCond(LtEqCond cond)
            throws EQLException;

    protected abstract String getSQLCond(NotEqCond cond)
            throws EQLException;

    protected abstract String getSQLCond(IsNullCond cond)
            throws EQLException;

    protected abstract String getSQLCond(IsNotNullCond cond)
            throws EQLException;

    protected abstract String getSQLCond(InCond cond)
            throws EQLException;

    protected abstract String getSQLCond(NotInCond cond)
            throws EQLException;

    protected abstract String getSQLCond(LikeCond cond)
            throws EQLException;

    protected abstract String getSQLCond(NotLikeCond cond)
            throws EQLException;

    //
    // Sub Operands
    //

    protected abstract String getSQLSubOperand(NoneOp subOp)
            throws EQLException;

    protected abstract String getSQLSubOperand(PlusOp subOp)
            throws EQLException;

    protected abstract String getSQLSubOperand(MinusOp subOp)
            throws EQLException;

    protected abstract String getSQLSubOperand(MultOp subOp)
            throws EQLException;

    protected abstract String getSQLSubOperand(DivOp subOp)
            throws EQLException;

    //
    // Operand Members
    //

    protected abstract String getSQLOperandMember(EQLReqOp memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(
            EQLReqSubOpMemberField memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(
            EQLReqSubOpMemberUnknown memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(
            EQLReqSubOpMemberEnum memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(EQLReq memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(SoundexFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(LowerFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(UpperFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(DateDiffFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(IsNullFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(FTSFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(CountAggFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(MinAggFunc memberField)
            throws EQLException;

    protected abstract String getSQLOperandMember(MaxAggFunc memberField)
            throws EQLException;

// ----------------- PROTECTED METHODS ---------------------

    /**
     * Construct full select Sql query
     *
     * @param req EQLReq object
     * @return sql query
     * @throws EQLException
     */
    protected final String constructSql(EQLReq req)
            throws EQLException {

        StringBuffer sql = new StringBuffer();

        // add SELECT clause
        sql.append("\nSELECT ");
        sql.append(hint.toString());
        sql.append("\n");
        sql.append(selectClause.toString());

        // add FROM clause
        if(fromClause.length() > 0) {
            sql.append("\nFROM\n ");
            sql.append(fromClause.toString());
        }

        // add WHERE clause
        if(whereClause.length() > 0) {
            sql.append("\nWHERE\n");
            sql.append(whereClause.toString());
        }
        
        // add GROUP BY clause
        if (groupByClause.length() > 0) {
            sql.append("\nGROUP BY\n");
            sql.append(groupByClause.toString());
        }

        // add ORDER BY clause
        if(orderClause.length() > 0) {
            sql.append("\nORDER BY\n");
            sql.append(orderClause.toString());
        }

        return sql.toString();
    }

    /**
     * Call getSQLCond() by condition type of <code>o</code>
     *
     * @param o EQL condition object
     * @return SQL string
     * @throws EQLException
     */
    protected final String _getSQLCond(java.io.Serializable o)
            throws EQLException {

        if(o instanceof EQLReqWhere) {
            return getSQLCond((EQLReqWhere) o);
        } else if(o instanceof EqCond) {
            return getSQLCond((EqCond) o);
        } else if(o instanceof LikeCond) {
            return getSQLCond((LikeCond) o);
        } else if(o instanceof NotLikeCond) {
            return getSQLCond((NotLikeCond) o);
        } else if(o instanceof NotEqCond) {
            return getSQLCond((NotEqCond) o);
        } else if(o instanceof GtCond) {
            return getSQLCond((GtCond) o);
        } else if(o instanceof LtCond) {
            return getSQLCond((LtCond) o);
        } else if(o instanceof GtEqCond) {
            return getSQLCond((GtEqCond) o);
        } else if(o instanceof LtEqCond) {
            return getSQLCond((LtEqCond) o);
        } else if(o instanceof IsNullCond) {
            return getSQLCond((IsNullCond) o);
        } else if(o instanceof IsNotNullCond) {
            return getSQLCond((IsNotNullCond) o);
        } else if(o instanceof InCond) {
            return getSQLCond((InCond) o);
        } else if(o instanceof NotInCond) {
            return getSQLCond((NotInCond) o);
        } else if(o instanceof NoneCond) {
            return getSQLCond((NoneCond) o);
        } else {
            throw new GenericSystemException(
                    "Can't call method 'getSQLCond(" + o.getClass() + ")'");
        }
    }

    /**
     * Call getSQLSubOperand() by operation type of <code>o</code>
     *
     * @param o EQL request sub operation object
     * @return SQL string
     * @throws EQLException
     */
    protected final String _getSQLSubOperand(EQLReqSubOp o)
            throws EQLException {

        if(o instanceof NoneOp) {
            return getSQLSubOperand((NoneOp) o);
        } else if(o instanceof PlusOp) {
            return getSQLSubOperand((PlusOp) o);
        } else if(o instanceof MinusOp) {
            return getSQLSubOperand((MinusOp) o);
        } else if(o instanceof MultOp) {
            return getSQLSubOperand((MultOp) o);
        } else if(o instanceof DivOp) {
            return getSQLSubOperand((DivOp) o);
        } else {
            throw new GenericSystemException(
                    "Can't call method 'getSQLSubOperand(" + o.getClass()
                            + ")'");
        }
    }

    /**
     * Call getSQLOperandMember() by member type of <code>o</code>
     *
     * @param o EQL request sub operation member
     * @return SQL string
     * @throws EQLException
     */
    protected final String _getSQLOperandMember(EQLReqSubOpMember o)
            throws EQLException {

        if(o instanceof EQLObject) {
            return _getSQLValue((EQLObject) o);
        } else if(o instanceof EQLReqOp) {
            return getSQLOperandMember((EQLReqOp) o);
        } else if(o instanceof EQLReqSubOpMemberField) {
            return getSQLOperandMember((EQLReqSubOpMemberField) o);
        } else if(o instanceof EQLReqSubOpMemberUnknown) {
            return getSQLOperandMember((EQLReqSubOpMemberUnknown) o);
        } else if(o instanceof EQLReqSubOpMemberEnum) {
            return getSQLOperandMember((EQLReqSubOpMemberEnum) o);
        } else if(o instanceof EQLReq) {
            return getSQLOperandMember((EQLReq) o);
        } else if(o instanceof SoundexFunc) {
            return getSQLOperandMember((SoundexFunc) o);
        } else if(o instanceof LowerFunc) {
            return getSQLOperandMember((LowerFunc) o);
        } else if(o instanceof UpperFunc) {
            return getSQLOperandMember((UpperFunc) o);
        } else if(o instanceof DateDiffFunc) {
            return getSQLOperandMember((DateDiffFunc) o);
        } else if(o instanceof IsNullFunc) {
            return getSQLOperandMember((IsNullFunc) o);
        } else if(o instanceof FTSFunc) {
            return getSQLOperandMember((FTSFunc) o);
        } else if(o instanceof CountAggFunc) {
            return getSQLOperandMember((CountAggFunc) o);
        } else if(o instanceof MinAggFunc) {
            return getSQLOperandMember((MinAggFunc) o);
        } else if(o instanceof MaxAggFunc) {
            return getSQLOperandMember((MaxAggFunc) o);
        } else {
            throw new GenericSystemException(
                    "Can't call method 'getSQLOperandMember(" + o.getClass()
                            + ")'");
        }
    }
}
