/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_connectivity.hxx"
#include "file/fcode.hxx"
#include <osl/diagnose.h>
#include "connectivity/sqlparse.hxx"
#include <i18npool/mslangid.hxx>
#include <tools/debug.hxx>
#include <tools/string.hxx>
#include "TConnection.hxx"
#include <com/sun/star/sdb/SQLFilterOperator.hpp>
#include <comphelper/types.hxx>
#include <com/sun/star/sdb/SQLFilterOperator.hpp>
#include <rtl/logfile.hxx>

using namespace ::comphelper;
using namespace connectivity;
using namespace connectivity::file;
//using namespace ::com::sun::star::uno;
//using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdb;
//using namespace ::com::sun::star::container;
//using namespace ::com::sun::star::beans;
//using namespace ::com::sun::star::sdbcx;

TYPEINIT0(OCode);
TYPEINIT1(OOperand, OCode);
TYPEINIT1(OOperandRow, OOperand);
TYPEINIT1(OOperandAttr, OOperandRow);
TYPEINIT1(OOperandParam, OOperandRow);
TYPEINIT1(OOperandValue, OOperand);
TYPEINIT1(OOperandConst, OOperandValue);
TYPEINIT1(OOperandResult, OOperandValue);
TYPEINIT1(OStopOperand, OOperandValue);

TYPEINIT1(OOperator, OCode);
TYPEINIT1(OBoolOperator,OOperator);
TYPEINIT1(OOp_NOT, OBoolOperator);
TYPEINIT1(OOp_AND, OBoolOperator);
TYPEINIT1(OOp_OR, OBoolOperator);
TYPEINIT1(OOp_ISNULL, OBoolOperator);
TYPEINIT1(OOp_ISNOTNULL, OOp_ISNULL);
TYPEINIT1(OOp_LIKE, OBoolOperator);
TYPEINIT1(OOp_NOTLIKE, OOp_LIKE);
TYPEINIT1(OOp_COMPARE, OBoolOperator);
TYPEINIT1(ONumOperator, OOperator);
TYPEINIT1(ONthOperator, OOperator);
TYPEINIT1(OBinaryOperator, OOperator);
TYPEINIT1(OUnaryOperator, OOperator);

//------------------------------------------------------------------
DBG_NAME(OCode )
OCode::OCode()
{
	DBG_CTOR(OCode ,NULL);
}
// -----------------------------------------------------------------------------
OCode::~OCode()
{
	DBG_DTOR(OCode,NULL);
}

//------------------------------------------------------------------
OEvaluateSet* OOperand::preProcess(OBoolOperator* /*pOp*/, OOperand* /*pRight*/)
{
	return NULL;
}
// -----------------------------------------------------------------------------
OOperandRow::OOperandRow(sal_uInt16 _nPos, sal_Int32 _rType)
	: OOperand(_rType)
	, m_nRowPos(_nPos)
{}
//------------------------------------------------------------------
void OOperandRow::bindValue(const OValueRefRow& _pRow)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandRow::OOperandRow" );
	OSL_ENSURE(_pRow.isValid(),"NO EMPTY row allowed!");
	m_pRow = _pRow;
	OSL_ENSURE(m_pRow.isValid() && m_nRowPos < m_pRow->get().size(),"Invalid RowPos is >= vector.size()");
	(m_pRow->get())[m_nRowPos]->setBound(sal_True);
}
// -----------------------------------------------------------------------------
void OOperandRow::setValue(const ORowSetValue& _rVal)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandRow::setValue" );
	OSL_ENSURE(m_pRow.isValid() && m_nRowPos < m_pRow->get().size(),"Invalid RowPos is >= vector.size()");
	(*(m_pRow->get())[m_nRowPos]) = _rVal;
}
//------------------------------------------------------------------
const ORowSetValue& OOperandRow::getValue() const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandRow::getValue" );
	OSL_ENSURE(m_pRow.isValid() && m_nRowPos < m_pRow->get().size(),"Invalid RowPos is >= vector.size()");
	return (m_pRow->get())[m_nRowPos]->getValue();
}

// -----------------------------------------------------------------------------
void OOperandValue::setValue(const ORowSetValue& _rVal)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandValue::setValue" );
	m_aValue = _rVal;
}
// -------------------------------------------------------------------------
sal_Bool OOperandAttr::isIndexed() const
{
	return sal_False;
}
//------------------------------------------------------------------
OOperandParam::OOperandParam(OSQLParseNode* pNode, sal_Int32 _nPos)
	: OOperandRow(static_cast<sal_uInt16>(_nPos), DataType::VARCHAR)		 // Standard-Typ
{
	OSL_ENSURE(SQL_ISRULE(pNode,parameter),"Argument ist kein Parameter");
	OSL_ENSURE(pNode->count() > 0,"Fehler im Parse Tree");
	OSQLParseNode *pMark = pNode->getChild(0);

	String aParameterName;
	if (SQL_ISPUNCTUATION(pMark,"?"))
		aParameterName = '?';
	else if (SQL_ISPUNCTUATION(pMark,":"))
		aParameterName = pNode->getChild(1)->getTokenValue();
	else
	{
		OSL_ASSERT("Fehler im Parse Tree");
	}

    // Parameter-Column aufsetzen mit defult typ, kann zu einem spaeteren Zeitpunkt ueber DescribeParameter
	// genauer spezifiziert werden

	// Identitaet merken (hier eigentlich nicht erforderlich, aber aus
	// Symmetriegruenden ...)

	// todo
	//	OColumn* pColumn = new OFILEColumn(aParameterName,eDBType,255,0,SQL_FLAGS_NULLALLOWED);
	//	rParamColumns->AddColumn(pColumn);

	// der Wert wird erst kurz vor der Auswertung gesetzt
}


//------------------------------------------------------------------
const ORowSetValue& OOperandValue::getValue() const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandValue::getValue" );
	return m_aValue;
}

//------------------------------------------------------------------
OOperandConst::OOperandConst(const OSQLParseNode& rColumnRef, const rtl::OUString& aStrValue)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandConst::OOperandConst" );
	switch (rColumnRef.getNodeType())
	{
		case SQL_NODE_STRING:
			m_aValue	= aStrValue;
			m_eDBType	= DataType::VARCHAR;
			m_aValue.setBound(sal_True);
			return;
		case SQL_NODE_INTNUM:
		case SQL_NODE_APPROXNUM:
		{
			m_aValue	= aStrValue.toDouble();
			m_eDBType	= DataType::DOUBLE;
			m_aValue.setBound(sal_True);
			return;
		}
        default:
            break;
	}

	if (SQL_ISTOKEN(&rColumnRef,TRUE))
	{
		m_aValue = 1.0;
		m_eDBType = DataType::BIT;
	}
	else if (SQL_ISTOKEN(&rColumnRef,FALSE))
	{
		m_aValue = 0.0;
		m_eDBType = DataType::BIT;
	}
	else
	{
		OSL_ASSERT("Parse Error");
	}
	m_aValue.setBound(sal_True);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Implementation of the operators

//------------------------------------------------------------------
sal_uInt16 OOperator::getRequestedOperands() const {return 2;}

//------------------------------------------------------------------
sal_Bool OBoolOperator::operate(const OOperand*, const OOperand*) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OBoolOperator::operate" );
	return sal_False;
}


//------------------------------------------------------------------
void OBoolOperator::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OBoolOperator::Exec" );
	OOperand  *pRight	= rCodeStack.top();
	rCodeStack.pop();
	OOperand  *pLeft	= rCodeStack.top();
	rCodeStack.pop();

	rCodeStack.push(new OOperandResultBOOL(operate(pLeft, pRight)));
	if (IS_TYPE(OOperandResult,pLeft))
		delete pLeft;
	if (IS_TYPE(OOperandResult,pRight))
		delete pRight;
}
//------------------------------------------------------------------
sal_Bool OOp_NOT::operate(const OOperand* pLeft, const OOperand* ) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_AND::operate" );
	return !pLeft->isValid();
}
//------------------------------------------------------------------
void OOp_NOT::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_ISNULL::Exec" );
	OOperand* pOperand = rCodeStack.top();
	rCodeStack.pop();

	rCodeStack.push(new OOperandResultBOOL(operate(pOperand)));
	if (IS_TYPE(OOperandResult,pOperand))
		delete pOperand;
}
//------------------------------------------------------------------
sal_uInt16 OOp_NOT::getRequestedOperands() const 
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_NOT::getRequestedOperands" );
    return 1;
}

//------------------------------------------------------------------
sal_Bool OOp_AND::operate(const OOperand* pLeft, const OOperand* pRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_AND::operate" );
	return pLeft->isValid() && pRight->isValid();
}

//------------------------------------------------------------------
sal_Bool OOp_OR::operate(const OOperand* pLeft, const OOperand* pRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_OR::operate" );
	return pLeft->isValid() || pRight->isValid();
}

//------------------------------------------------------------------
sal_uInt16 OOp_ISNULL::getRequestedOperands() const 
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_ISNULL::getRequestedOperands" );
    return 1;
}

//------------------------------------------------------------------
void OOp_ISNULL::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_ISNULL::Exec" );
	OOperand* pOperand = rCodeStack.top();
	rCodeStack.pop();

	rCodeStack.push(new OOperandResultBOOL(operate(pOperand)));
	if (IS_TYPE(OOperandResult,pOperand))
		delete pOperand;
}

//------------------------------------------------------------------
sal_Bool OOp_ISNULL::operate(const OOperand* pOperand, const OOperand*) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_ISNULL::operate" );
	return pOperand->getValue().isNull();
}

//------------------------------------------------------------------
sal_Bool OOp_ISNOTNULL::operate(const OOperand* pOperand, const OOperand*) const
{
	return !OOp_ISNULL::operate(pOperand);
}

//------------------------------------------------------------------
sal_Bool OOp_LIKE::operate(const OOperand* pLeft, const OOperand* pRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_ISNULL::operate" );
	sal_Bool bMatch;
	ORowSetValue aLH(pLeft->getValue());
	ORowSetValue aRH(pRight->getValue());

	if (aLH.isNull() || aRH.isNull())
		bMatch = sal_False;
	else
	{
		bMatch = match(aRH.getString(), aLH.getString(), cEscape);
	}
	return bMatch;
}

//------------------------------------------------------------------
sal_Bool OOp_NOTLIKE::operate(const OOperand* pLeft, const OOperand* pRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_NOTLIKE::operate" );
	return !OOp_LIKE::operate(pLeft, pRight);
}

//------------------------------------------------------------------
sal_Bool OOp_COMPARE::operate(const OOperand* pLeft, const OOperand* pRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_COMPARE::operate" );
	ORowSetValue aLH(pLeft->getValue());
	ORowSetValue aRH(pRight->getValue());

	if (aLH.isNull() || aRH.isNull()) // if (!aLH.getValue() || !aRH.getValue())
		return sal_False;

	sal_Bool bResult = sal_False;
	sal_Int32 eDBType = pLeft->getDBType();

	// Vergleich (je nach Datentyp):
	switch (eDBType)
	{
		case DataType::CHAR:
		case DataType::VARCHAR:
        case DataType::LONGVARCHAR:
		{
			rtl::OUString sLH = aLH, sRH = aRH;
            sal_Int32 nRes = rtl_ustr_compareIgnoreAsciiCase_WithLength
                (
                 sLH.pData->buffer,
                 sLH.pData->length,
                 sRH.pData->buffer,
                 sRH.pData->length );
			switch(aPredicateType)
			{
				case SQLFilterOperator::EQUAL:			bResult = (nRes == 0); break;
				case SQLFilterOperator::NOT_EQUAL:			bResult = (nRes != 0); break;
				case SQLFilterOperator::LESS:				bResult = (nRes <  0); break;
				case SQLFilterOperator::LESS_EQUAL:		bResult = (nRes <= 0); break;
				case SQLFilterOperator::GREATER:			bResult = (nRes >  0); break;
				case SQLFilterOperator::GREATER_EQUAL:	bResult = (nRes >= 0); break;
				default:						bResult = sal_False;
			}
		} break;
		case DataType::TINYINT:
		case DataType::SMALLINT:
		case DataType::INTEGER:
		case DataType::DECIMAL:
		case DataType::NUMERIC:
		case DataType::REAL:
		case DataType::DOUBLE:
		case DataType::BIT:
		case DataType::TIMESTAMP:
		case DataType::DATE:
		case DataType::TIME:
		{
			double n = aLH ,m = aRH;

			switch (aPredicateType)
			{
				case SQLFilterOperator::EQUAL:			bResult = (n == m); break;
				case SQLFilterOperator::LIKE:				bResult = (n == m); break;
				case SQLFilterOperator::NOT_EQUAL:			bResult = (n != m); break;
				case SQLFilterOperator::NOT_LIKE:			bResult = (n != m); break;
				case SQLFilterOperator::LESS:				bResult = (n < m); break;
				case SQLFilterOperator::LESS_EQUAL:		bResult = (n <= m); break;
				case SQLFilterOperator::GREATER:			bResult = (n > m); break;
				case SQLFilterOperator::GREATER_EQUAL:	bResult = (n >= m); break;
				default:						bResult = sal_False;
			}
		} break;
		default:
			bResult = aLH == aRH;
	}
	return bResult;
}

//------------------------------------------------------------------
void ONumOperator::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "ONumOperator::Exec" );

	OOperand  *pRight	= rCodeStack.top();
	rCodeStack.pop();
	OOperand  *pLeft	= rCodeStack.top();
	rCodeStack.pop();

	rCodeStack.push(new OOperandResultNUM(operate(pLeft->getValue(), pRight->getValue())));
	if (IS_TYPE(OOperandResult,pLeft))
		delete pLeft;
	if (IS_TYPE(OOperandResult,pRight))
		delete pRight;
}
//------------------------------------------------------------------
double OOp_ADD::operate(const double& fLeft,const double& fRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_ADD::operate" );
	return fLeft + fRight;
}

//------------------------------------------------------------------
double OOp_SUB::operate(const double& fLeft,const double& fRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_SUB::operate" );
	return fLeft - fRight;
}

//------------------------------------------------------------------
double OOp_MUL::operate(const double& fLeft,const double& fRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_MUL::operate" );
	return fLeft * fRight;
}

//------------------------------------------------------------------
double OOp_DIV::operate(const double& fLeft,const double& fRight) const
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOp_DIV::operate" );
	return fLeft / fRight;
}
// -----------------------------------------------------------------------------
OEvaluateSet* OOperandAttr::preProcess(OBoolOperator* /*pOp*/, OOperand* /*pRight*/)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OOperandAttr::preProcess" );
	return NULL;
}
//------------------------------------------------------------------
void ONthOperator::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "ONthOperator::Exec" );
	::std::vector<ORowSetValue> aValues;
	::std::vector<OOperand*> aOperands;
	OOperand* pOperand;
	do
	{
		OSL_ENSURE(!rCodeStack.empty(),"Stack must be none empty!");
		pOperand	= rCodeStack.top();
		rCodeStack.pop();
		if ( !IS_TYPE(OStopOperand,pOperand) )
			aValues.push_back( pOperand->getValue() );
		aOperands.push_back( pOperand );
	}
	while ( !IS_TYPE(OStopOperand,pOperand) );

	rCodeStack.push(new OOperandResult(operate(aValues)));

	::std::vector<OOperand*>::iterator aIter = aOperands.begin();
	::std::vector<OOperand*>::iterator aEnd = aOperands.end();
	for (; aIter != aEnd; ++aIter)
	{
		if (IS_TYPE(OOperandResult,*aIter))
			delete *aIter;
	}
}
//------------------------------------------------------------------
void OBinaryOperator::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OBinaryOperator::Exec" );
	OOperand  *pRight	= rCodeStack.top();
	rCodeStack.pop();
	OOperand  *pLeft	= rCodeStack.top();
	rCodeStack.pop();

	if ( !rCodeStack.empty() && IS_TYPE(OStopOperand,rCodeStack.top()) )
		rCodeStack.pop();

	rCodeStack.push(new OOperandResult(operate(pLeft->getValue(),pRight->getValue())));
	if (IS_TYPE(OOperandResult,pRight))
		delete pRight;
	if (IS_TYPE(OOperandResult,pLeft))
		delete pLeft;
}
//------------------------------------------------------------------
void OUnaryOperator::Exec(OCodeStack& rCodeStack)
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "file", "Ocke.Janssen@sun.com", "OUnaryOperator::Exec" );
	OSL_ENSURE(!rCodeStack.empty(),"Stack is empty!");
	OOperand* pOperand = rCodeStack.top();
	rCodeStack.pop();

	rCodeStack.push(new OOperandResult(operate(pOperand->getValue())));
	if (IS_TYPE(OOperandResult,pOperand))
		delete pOperand;
}
// -----------------------------------------------------------------------------
sal_uInt16 OUnaryOperator::getRequestedOperands() const {return 1;}



