
import java.util.HashSet;
import java.util.Vector;

import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.sql.compile.Visitable;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.impl.sql.compile.AllResultColumn;
import org.apache.derby.impl.sql.compile.ColumnReference;
import org.apache.derby.impl.sql.compile.FromBaseTable;
import org.apache.derby.impl.sql.compile.ParameterNode;
import org.apache.derby.impl.sql.compile.ResultColumn;
import org.apache.derby.impl.sql.compile.SelectNode;
import org.apache.derby.impl.sql.compile.TableName;

public class MyInsertParser implements Visitor {

    Vector<MyField> vf = new Vector<MyField>();
    Vector<MyTable> vt = new Vector<MyTable>();
    boolean bSelectIsNow = false, bColRef = false, bBaseTab = false,
            bAllResCol = false, bHasSelect = false, bFromField = false;
    short shCt = 0, shCtResultColumnSelect = 0, shCtResultColumnInsert = 0;
    String sUpdTableName = "";
    private HashSet _visitedNodes = new HashSet();

    public MyInsertParser() {
    }

    public Vector<MyField> getData() {
        return vf;
    }

    public void endVisit() {
        if (shCtResultColumnInsert > 0) {
            return;
        }
        addColumn4All(sUpdTableName, false);
    }

    public void close() {
        vf = null;
        vt = null;
        _visitedNodes = null;
    }

    public boolean visitChildrenFirst(Visitable node) {
        return false;
    }

    public Visitable visit(Visitable node) throws StandardException {
        if (!_visitedNodes.contains(node)) {
            _visitedNodes.add(node);
            manageNode(node);
            node.accept(this);
        }
        return node;
    }

    private void manageNode(Visitable node) {
// nodo select se la insert viene fatta da una query
        if (node instanceof SelectNode) {
            bSelectIsNow = true;
            bHasSelect = true;
            bFromField = true;
            return;
        }
// nodo che identifica che tutti i campi della/e tabelle sono inclusi nella select
        if (node instanceof AllResultColumn) {
            bAllResCol = true;
            return;
        }
// colonne risultato vale sia per la select che per i campi della tabella master
        shCt++;
        if (node instanceof ResultColumn) {
            addColumn((ResultColumn) node);
            if (bSelectIsNow) {
                shCtResultColumnSelect++;
            } else {
                shCtResultColumnInsert++;
            }
            shCt = 0;
            return;
        }
// identifica il nome della colonna se per il campo e' stato usato un alias ed in
// questo caso e' preceduto da un nodo ResultColumn, oppure identifica una colonna
// inclusa in una where e in questo caso NON e' preceduto da un nodo ResultColumn
        if (node instanceof ColumnReference) {
            setColumnReference((ColumnReference) node);
            return;
        }
// identifica un parametro dello statement
        if (node instanceof ParameterNode) {
            setParameterNode((ParameterNode) node);
            return;
        }
// tabella per insert (master)
        if (node instanceof TableName) {
            verifySelect();
            bSelectIsNow = false;
            bBaseTab = true;
            setTableName((TableName) node);
            return;
        }
// tabella/e incluse nella query
        if (node instanceof FromBaseTable) {
            setBaseTableName((FromBaseTable) node);
            bFromField = false;
            return;
        }
    }

    private void addColumn(ResultColumn inNode) {
        int iInd = findNode(inNode.getVirtualColumnId());
        if (iInd > -1) {
            vf.elementAt(iInd).setName(inNode.getName());
            return;
        }
        MyField mf = new MyField(inNode.getVirtualColumnId(),
                bSelectIsNow, bBaseTab, bFromField);
        if (inNode.getType() != null) {
            mf.setType('c');
        }
        if (inNode.getName() != null) {
            mf.setName(inNode.getName());
        }
        if (!bSelectIsNow && sUpdTableName.length() > 0) {
            mf.setTableName(sUpdTableName);
        }
        vf.addElement(mf);
    }

    private void addColumn4All(String inTableName, boolean inFromField) {
        MyField mf = new MyField(-1, bSelectIsNow, bBaseTab, inFromField);
        mf.setAllFields(true);
        if (inTableName.length() > 0) {
            mf.setTableName(inTableName);
        }
        vf.addElement(mf);
    }

    private int findNode(int inColId) {
        for (int i = 0; i < vf.size(); i++) {
            if (vf.elementAt(i).getColId() == inColId) {
                if (bSelectIsNow && vf.elementAt(i).isSelect() ||
                        !bSelectIsNow && !vf.elementAt(i).isSelect()) {
                    return i;
                }
            }
        }
        return -1;
    }

    private void setTableName(TableName inNode) {
        sUpdTableName = inNode.getFullTableName();
        for (int i = 0; i < vf.size(); i++) {
            if (vf.elementAt(i).getTableName() == null &&
                    !vf.elementAt(i).isSelect()) {
                vf.elementAt(i).setTableName(inNode.getFullTableName());
            }
            if (!vf.elementAt(i).isSelect()) {
                vf.elementAt(i).setBaseTable(true);
            }
        }
    }

    private void setBaseTableName(FromBaseTable inNode) {
        try {
            vt.addElement(
                    new MyTable(
                    inNode.getTableNameField().getFullTableName(),
                    inNode.getCorrelationName()));
            for (int i = 0; i < vf.size(); i++) {
                if (vf.elementAt(i).isSelect()) {
                    if (vf.elementAt(i).getTableName() == null ||
                            vf.elementAt(i).getTableName().equals(inNode.getCorrelationName())) {
                        vf.elementAt(i).setTableName(inNode.getTableNameField().getFullTableName());
                    }
                }
            }
        } catch (Throwable t) {
            System.out.println("setBaseTableName - Errore - " + t);
        }
    }

    private void setColumnReference(ColumnReference inNode) {
        if (shCt == 1) {
            vf.elementAt(vf.size() - 1).setTableName(inNode.getTableName());
            vf.elementAt(vf.size() - 1).setName(inNode.getColumnName());
            return;
        }
        MyField mf = new MyField(-1, bSelectIsNow, bBaseTab, bFromField);
        if (inNode.getColumnName() != null) {
            mf.setName(inNode.getColumnName());
        }
        mf.setType('c');
        if (inNode.getTableName() != null) {
            for (int i = 0; i < vt.size(); i++) {
                if (vt.elementAt(i).getAlias().equals(inNode.getTableName())) {
                    mf.setTableName(vt.elementAt(i).getTableName());
                }
            }
        } else {
            if (vt.size() == 1) {
                mf.setTableName(vt.elementAt(0).getTableName());
            }
        }
        vf.addElement(mf);
        shCt = 10;
    }

    private void verifySelect() {
// non ci sono query nello statement
        if (!bHasSelect) {
            return;
        }
// non c'e' selezione di tutte le colonne
        if (!bAllResCol) {
            return;
        }
// se c'e' solo selezione di tutte le colonne aggiunge un campo per ogni tabella
// inclusa nella query
        if (shCtResultColumnSelect == 0) {
            for (int i = 0; i < vt.size(); i++) {
                addColumn4All(vt.elementAt(i).getTableName(), true);
            }
            return;
        }
// se c'e' una situazione mista cerca di capire quali tabelle hanno dei campi specifici
// inclusi
        for (int i = 0; i < vt.size(); i++) {
            if (hasFieldsSelected(vt.elementAt(i).getTableName())) {
                continue;
            }
            addColumn4All(vt.elementAt(i).getTableName(), true);
        }
    }

    private boolean hasFieldsSelected(String inTableName) {
        for (int i = 0; i < vf.size(); i++) {
            if (vf.elementAt(i).getTableName() == null) {
                continue;
            }
            if (!vf.elementAt(i).isFromField()) {
                continue;
            }
            if (vf.elementAt(i).getTableName().equals(inTableName)) {
                return true;
            }
        }
        return false;
    }

    private void setParameterNode(ParameterNode inNode) {
        if (shCt == 11) {
            vf.elementAt(vf.size() - 1).setType('p');
        }
    }

    public boolean stopTraversal() {
        return false;
    }

    public boolean skipChildren(Visitable node) throws StandardException {
        return false;
    }
}
