/*!
  @file           IFR_Parameter.cpp
  @author         D039759
  @ingroup        IFR_DataConv
  @brief          Handling of parameters and hostvars
  @see

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2001-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
*/
//----------------------------------------------------------------------
#include "precom/lcstream.h"
#include "Interfaces/Runtime/IFR_Parameter.h"

//----------------------------------------------------------------------
static inline IFR_size_t string_nlen(char *s, IFR_size_t max)
{
    for(IFR_size_t i=0; i<max; ++i) {
        if(*(s+i) == 0) {
            return i;
        }
    }
    return max;
}

//----------------------------------------------------------------------
static inline IFR_size_t compute_input_datalength(IFR_size_t datalength,
                                                  IFR_Length *lengthindicator,
                                                  char      *data)
{
    if(lengthindicator) {
        if(*lengthindicator==IFR_NTS) {
            return string_nlen(data, datalength);
        } else if(*lengthindicator < 0) {
            return 0;
        } else if (*lengthindicator >= 0) {
            return  *lengthindicator;
        } 
    } 
    return string_nlen(data, datalength);
}

//----------------------------------------------------------------------
const char*
IFR_Parameter::getHosttypeString() const
{
    return IFR_HostTypeToString(m_hosttype);
}

//----------------------------------------------------------------------
const IFR_Length
IFR_Parameter::getBytesLength() const
{
    switch(m_hosttype) {
    case IFR_HOSTTYPE_PARAMETER_NOTSET:
        return -1;
    case IFR_HOSTTYPE_BINARY:
    case IFR_HOSTTYPE_ASCII:
    case IFR_HOSTTYPE_UCS2_SWAPPED:
    case IFR_HOSTTYPE_UCS2:
    case IFR_HOSTTYPE_UTF8:
    case IFR_HOSTTYPE_STREAM:
        return m_byteslength;

    case IFR_HOSTTYPE_UINT1:
        return sizeof(IFR_UInt1);
    case IFR_HOSTTYPE_INT1:
        return sizeof(IFR_Int1);
    case IFR_HOSTTYPE_UINT2:
        return sizeof(IFR_UInt2);
    case IFR_HOSTTYPE_INT2:
        return sizeof(IFR_Int2);
    case IFR_HOSTTYPE_UINT4:
        return sizeof(IFR_UInt4);
    case IFR_HOSTTYPE_INT4:
        return sizeof(IFR_Int4);
    case IFR_HOSTTYPE_UINT8:
        return sizeof(IFR_UInt8);
    case IFR_HOSTTYPE_INT8:
        return sizeof(IFR_Int8);
    case IFR_HOSTTYPE_DOUBLE:
        return sizeof(double);
    case IFR_HOSTTYPE_FLOAT:
        return sizeof(float);
    case IFR_HOSTTYPE_ODBCDATE:
        return sizeof(SQL_DATE_STRUCT);
    case IFR_HOSTTYPE_ODBCTIME:
        return sizeof(SQL_TIME_STRUCT);
    case IFR_HOSTTYPE_ODBCTIMESTAMP:
        return sizeof(SQL_TIMESTAMP_STRUCT);
    case IFR_HOSTTYPE_GUID:
        return sizeof(GUID);
    default:
        return -1;
    }
}

//----------------------------------------------------------------------
IFR_SQLType
IFR_Parameter::getPreferredSQLType() const
{
    switch(m_hosttype) {
    case IFR_HOSTTYPE_BINARY:
        return IFR_SQLTYPE_CHB;
    case IFR_HOSTTYPE_ASCII:
        return IFR_SQLTYPE_CHA;
    case IFR_HOSTTYPE_UCS2_SWAPPED:
    case IFR_HOSTTYPE_UCS2:
    case IFR_HOSTTYPE_UTF8:
        return IFR_SQLTYPE_CHA;
    case IFR_HOSTTYPE_UINT1:
    case IFR_HOSTTYPE_INT1:
        return IFR_SQLTYPE_FIXED;
    case IFR_HOSTTYPE_UINT2:
        return IFR_SQLTYPE_FIXED;
    case IFR_HOSTTYPE_INT2:
        return IFR_SQLTYPE_SMALLINT;
    case IFR_HOSTTYPE_UINT4:
        return IFR_SQLTYPE_FIXED;
    case IFR_HOSTTYPE_INT4:
        return IFR_SQLTYPE_INTEGER;
    case IFR_HOSTTYPE_UINT8:
    case IFR_HOSTTYPE_INT8:
        return IFR_SQLTYPE_FIXED;
    case IFR_HOSTTYPE_DOUBLE:
        return IFR_SQLTYPE_FLOAT;
    case IFR_HOSTTYPE_FLOAT:
        return IFR_SQLTYPE_FLOAT;
    case IFR_HOSTTYPE_ODBCDATE:
        return IFR_SQLTYPE_DATE;
    case IFR_HOSTTYPE_ODBCTIME:
        return IFR_SQLTYPE_TIME;
    case IFR_HOSTTYPE_ODBCTIMESTAMP:
        return IFR_SQLTYPE_TIMESTAMP;
    case IFR_HOSTTYPE_ODBCNUMERIC:
        return IFR_SQLTYPE_FLOAT;
    case IFR_HOSTTYPE_GUID:
        return IFR_SQLTYPE_CHB;
    case IFR_HOSTTYPE_PARAMETER_NOTSET:
    default:
        return IFR_SQLTYPE_CHA;
    }
}

//----------------------------------------------------------------------
IFR_Bool
IFR_Parameter::isABAPTable(IFR_Int4 abapTabId)
{
    if(m_hosttype != IFR_HOSTTYPE_STREAM) {
        return false;
    }

    SQLStreamDesc *streamdesc = (SQLStreamDesc *) asChar();
    if(streamdesc && streamdesc->StreamParam->Stream.hStream.ABAPTabId == abapTabId) {
        return true;
    } else {
        return false;
    }
}

//----------------------------------------------------------------------
IFR_size_t
IFR_Parameter::getPreferredLength() const
{
    switch(m_hosttype) {
    // more than 8008 bytes are not supported, and may lead
    // to unexpected results.
    case IFR_HOSTTYPE_BINARY:
        return m_byteslength<=0 || m_byteslength>8008 ? 8008 : m_byteslength;
    case IFR_HOSTTYPE_ASCII:
        return m_byteslength<=0 || m_byteslength>8008 ? 8008 : m_byteslength;
    case IFR_HOSTTYPE_UCS2:
    case IFR_HOSTTYPE_UCS2_SWAPPED:
        return m_byteslength<=0 || m_byteslength>8008 ? 4004 : m_byteslength/2;
    case IFR_HOSTTYPE_UTF8:
        return m_byteslength<=0 || m_byteslength>8008 ? 4004 : m_byteslength;
    case IFR_HOSTTYPE_UINT1:
    case IFR_HOSTTYPE_INT1:
        return 3;  
    case IFR_HOSTTYPE_UINT2:
        return 5;
    case IFR_HOSTTYPE_INT2:
        return 5;
    case IFR_HOSTTYPE_UINT4:
        return 10;
    case IFR_HOSTTYPE_INT4:
        return 10;
    case IFR_HOSTTYPE_UINT8:
        return 38;
    case IFR_HOSTTYPE_INT8:
        return 38;
    case IFR_HOSTTYPE_DOUBLE:
        return 9;
    case IFR_HOSTTYPE_FLOAT:
        return 16;
    /* !!! for these types, a length supplied is graceful ignored,
           if it is <> 0.
     */
    case IFR_HOSTTYPE_ODBCDATE:
    case IFR_HOSTTYPE_ODBCTIME:
    case IFR_HOSTTYPE_ODBCTIMESTAMP:
        return 50;
    case IFR_HOSTTYPE_ODBCNUMERIC:
        return 38;
    case IFR_HOSTTYPE_GUID:
        return 16;
    case IFR_HOSTTYPE_PARAMETER_NOTSET:
    default:
        return 1;
    }

}

//----------------------------------------------------------------------
IFR_StringEncoding
IFR_Parameter::getEncoding(IFR_HostType hosttype)
{
    switch(hosttype) {
    case IFR_HOSTTYPE_BINARY:
    case IFR_HOSTTYPE_ASCII:
    case IFR_HOSTTYPE_BLOB:
    case IFR_HOSTTYPE_ASCII_LOB:
        return IFR_StringEncodingAscii;
    case IFR_HOSTTYPE_UCS2:
    case IFR_HOSTTYPE_UCS2_LOB:
        return IFR_StringEncodingUCS2;
    case IFR_HOSTTYPE_UCS2_SWAPPED:
    case IFR_HOSTTYPE_UCS2_SWAPPED_LOB:
        return IFR_StringEncodingUCS2Swapped;
    case IFR_HOSTTYPE_UTF8:
        return IFR_StringEncodingUTF8;
    default:
        return IFR_StringEncodingType::Unknown; // 0
    }
}

//----------------------------------------------------------------------
IFR_Bool
IFR_Parameter::sqlTraceParameter(IFR_TraceStream& s, IFR_UInt2 parameterindex, IFR_Bool nodata)
{
    // Format is:               0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF
    // "I   T          AT L     I                  D                  P"
    if(m_hosttype == IFR_HOSTTYPE_PARAMETER_NOTSET) {
        char buffer[80];
        sp77sprintf(buffer, 80, "%-3.1d *** NOT SET ***", (IFR_Int4)parameterindex);
        s << buffer;
        return false;
    } else {
        const char *hosttype_str = IFR_HostTypeToString(m_hosttype);
        char buffer[80];
        sp77sprintf(buffer, 80, "%-3.1d %-10.10s %c%c %-10d",
                    (IFR_Int4)parameterindex,
                    hosttype_str, 
                    m_addrbound?'A':' ', 
                    m_terminate?'T':' ',
                    m_byteslength);
        s << buffer;
        if(!nodata) {
#ifndef BIT64
            sp77sprintf(buffer, 80, " 0x%-8.8p 0x%-8.8p 0x%-8.8p",
                        (void *)m_lengthindicator,
                        (void *)m_data,
                        (void *)m_posindicator);
            
#else
            sp77sprintf(buffer, 80, " 0x%-16.16p 0x%-16.16p 0x%-16.16p" ,
                        (void *)m_lengthindicator,
                        (void *)m_data,
                        (void *)m_posindicator);
            
#endif
            s << buffer;
        }
        return true;
    }
}

//----------------------------------------------------------------------
IFR_Bool
IFR_Parameter::sqlTraceParameter(IFR_TraceStream& s, const char * parametertag, IFR_Bool nodata)
{
    // Format is:               0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFF
    // "I   T          AT L     I                  D                  P"
    if(m_hosttype == IFR_HOSTTYPE_PARAMETER_NOTSET) {
        char buffer[80];
        sp77sprintf(buffer, 80, "%-3.3s *** NOT SET ***", parametertag);
        s << buffer;
        return false;
    } else {
        const char *hosttype_str = IFR_HostTypeToString(m_hosttype);
        char buffer[80];
        sp77sprintf(buffer, 80, "%-3.3s %-10.10s %c%c %-10d",
                    parametertag,
                    hosttype_str, 
                    m_addrbound?'A':' ', 
                    m_terminate?'T':' ',
                    m_byteslength);
        s << buffer;
        if(!nodata) {
#ifndef BIT64
            sp77sprintf(buffer, 80, " 0x%-8.8p 0x%-8.8p 0x%-8.8p",
                        (void *)m_lengthindicator,
                        (void *)m_data,
                        (void *)m_posindicator);
            
#else
            sp77sprintf(buffer, 80, " 0x%-16.16p 0x%-16.16p 0x%-16.16p" ,
                        (void *)m_lengthindicator,
                        (void *)m_data,
                        (void *)m_posindicator);
            
#endif
            s << buffer;
        }
        return true;
    }
}


//----------------------------------------------------------------------
static const char *get_abaptype(unsigned char abap_type)
{
    switch(abap_type) {
#define RETURN_TEXT(X) case X: return #X
        
        RETURN_TEXT(ABTYPC);
        RETURN_TEXT(ABTYPDATE);
        RETURN_TEXT(ABTYPP);
        RETURN_TEXT(ABTYPTIME);
        RETURN_TEXT(ABTYPX);
        RETURN_TEXT(ABTYPTABH);
        RETURN_TEXT(ABTYPNUM);
        RETURN_TEXT(ABTYPFLOAT);
        RETURN_TEXT(ABTYPINT);
        RETURN_TEXT(ABTYPINT2);
        RETURN_TEXT(ABTYPINT1);
        RETURN_TEXT(ABTYPW);
        RETURN_TEXT(ABTYP1);
        RETURN_TEXT(ABTYP2);
        RETURN_TEXT(ABTYPSTRUC1);
        RETURN_TEXT(ABTYPSTRUC2);
        RETURN_TEXT(ABTYPREF);
        RETURN_TEXT(ABTYPOBJ1);
        RETURN_TEXT(ABTYPOBJ2);
        RETURN_TEXT(ABTYPnull);
    case 100:
        return "ABTYPWYDE";
    default:
        return "***INVALID TYPE***";
    }
#undef RETURN_TEXT    
}

//----------------------------------------------------------------------
static void 
trace_stream(IFR_TraceStream& s, 
             void *streamdata)
{
    const char *prefix = "                                         ";
    SQLStreamDesc *streamdesc = (SQLStreamDesc *) streamdata;
    SQL_LC_StreamParm *streamparam = streamdata ? streamdesc->StreamParam : 0;
    if(streamparam == 0) {
        s << "NULL STREAM PARAMETER";
    } else {
        OmsTypeABAPTabHandle *tabhandle = &(streamparam->ABAPTab.hABAPTab);
        if(tabhandle == 0) {
            s << "NULL ABAP TAB HANDLE";
        } else {
            s <<           "ROW SIZE  : " << tabhandle->rowSize << endl
              << prefix << "COL COUNT : " << tabhandle->colCount << endl
              << prefix << "ROW COUNT : " << tabhandle->rowCount << endl
              << prefix << "ABAP TABID: " << tabhandle->ABAPTabId << endl;
            for(int i=0; i<tabhandle->colCount; ++i) {
                s << prefix 
                  << "COL[" << (i+1) << "]=(INOUT=" << (IFR_Int4)(tabhandle->colDesc[i].inout) 
                  << ", TYPE=" << get_abaptype(tabhandle->colDesc[i].abap_type) 
                  << ", DEC=" << tabhandle->colDesc[i].dec
                  << ", LENGTH=" << tabhandle->colDesc[i].length
                  << ", OFS=" << tabhandle->colDesc[i].offset
                  << ")";
                if(i != tabhandle->colCount-1) {
                    s << endl;
                }
            }
        }
    }
}


//----------------------------------------------------------------------.
void
IFR_Parameter:: sqlTraceParameterData(IFR_TraceStream& s,
                                      IFR_UInt2        index, 
                                      IFR_Retcode      conversionRC,
                                      IFR_Int4         offset,
                                      IFR_Int4         rowsize,
                                      unsigned char   *parameterdata,
                                      IFR_Length      *lengthindicator,
                                      IFR_Bool         isinput)
{
    if(sqlTraceParameter(s, index, true)) {
        IFR_Bool nodatapresent=false;
        IFR_Length *used_lengthindicator = lengthindicator?lengthindicator:getLengthIndicator(offset, rowsize);
        if(used_lengthindicator) {
            switch(*used_lengthindicator) {
            case IFR_DATA_AT_EXEC:
                s << " DATA AT EXEC";
                nodatapresent = true;
                break;
            case IFR_NULL_DATA:
                s << " NULL        ";
                nodatapresent = true;
                break;
            case IFR_NTS:
                s << " NTS         ";
                break;
            case IFR_DEFAULT_PARAM:
                s << " DEFAULT     ";
                nodatapresent = true;
                break;
            case IFR_NO_TOTAL:
                s << " NO TOTAL    ";
                break;
            case IFR_IGNORE:
                s << " IGNORE      ";
                nodatapresent = true;
                break;
            default: {
                char buf[16];
                sp77sprintf(buf, 16, " %-10d  ", (IFR_Int4)(*used_lengthindicator));
                s << buf;
            }
            }
        } else {
            s << " NULL PTR    ";
        }
        
        if((conversionRC==IFR_OK || conversionRC==IFR_DATA_TRUNC || isinput) && !nodatapresent) {
            IFR_Int4 tracelength = 
                compute_input_datalength(m_byteslength, used_lengthindicator,
                                         (char *)data(offset, rowsize, parameterdata));
            
            if (conversionRC == IFR_DATA_TRUNC) {
                tracelength = m_byteslength;
                if (shouldTerminated() && tracelength != 0) {
                    if (m_hosttype == IFR_HOSTTYPE_UCS2 || 
                        m_hosttype == IFR_HOSTTYPE_UCS2_SWAPPED) {          
                        tracelength-=2;
                    } else {
                        tracelength--;
                    }
                }
            }

            switch(m_hosttype) {
            case IFR_HOSTTYPE_ASCII:
            case IFR_HOSTTYPE_RAWHEX:
                s << "'" << inputlength(MIN(IFR_MAX_TRACELENGTH, tracelength)) 
                  << inputencoding(IFR_StringEncodingAscii) 
                  << data(offset, rowsize, parameterdata);
                if (tracelength > IFR_MAX_TRACELENGTH) 
                    s << "..."; 
                else 
                    s << "'";
                break;
            case IFR_HOSTTYPE_UTF8:
                s << "'" << inputlength(MIN(IFR_MAX_TRACELENGTH, tracelength)) << inputencoding(IFR_StringEncodingUTF8) 
                  << data(offset, rowsize, parameterdata);
                if (tracelength > IFR_MAX_TRACELENGTH) s << "...";
                else s << "'";
                break;
            case IFR_HOSTTYPE_UCS2:
                s << "'" << inputlength(MIN(IFR_MAX_TRACELENGTH, tracelength)) << inputencoding(IFR_StringEncodingUCS2) 
                  << data(offset, rowsize, parameterdata);
                if (tracelength > IFR_MAX_TRACELENGTH) s << "...";
                else s << "'";
                break;
            case IFR_HOSTTYPE_UCS2_SWAPPED:
                s << "'" << inputlength(MIN(IFR_MAX_TRACELENGTH, tracelength)) << inputencoding(IFR_StringEncodingUCS2Swapped) 
                  << data(offset, rowsize, parameterdata);
                if (tracelength > IFR_MAX_TRACELENGTH) s << "...";
                else s << "'";
                break;
            case IFR_HOSTTYPE_BINARY:
                s << hex << inputlength(MIN(IFR_MAX_TRACELENGTH/2, tracelength)) << data(offset, rowsize, parameterdata);
                if (tracelength > IFR_MAX_TRACELENGTH/2) 
                    s << "...";
                break;
            case IFR_HOSTTYPE_INT1:
                s << (IFR_Int4) asInt1(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_UINT1:
                s << (IFR_UInt4) asUInt1(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_INT2:
                s << asInt2(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_UINT2:
                s << asUInt2(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_INT4:
                s << asInt4(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_UINT4:
                s << asUInt4(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_INT8:
                s << asInt8(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_UINT8:
                s << asUInt8(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_DOUBLE:
                s << asDouble(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_FLOAT: {
                double d = asFloat(offset, rowsize, parameterdata);
                s << d;
                break;
            }
            case IFR_HOSTTYPE_ODBCDATE:
                s << asSQLDate(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_ODBCTIME:
                s << asSQLTime(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_ODBCTIMESTAMP:
                s << asSQLTimestamp(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_ODBCNUMERIC:
                s << asSQLNumeric(offset, rowsize, parameterdata);
                break;
            case IFR_HOSTTYPE_STREAM:
                trace_stream(s, (void *) data(offset, rowsize, parameterdata));
                break;
            case IFR_HOSTTYPE_BLOB:
            case IFR_HOSTTYPE_ASCII_LOB:
            case IFR_HOSTTYPE_UTF8_LOB:
            case IFR_HOSTTYPE_UCS2_LOB:
            case IFR_HOSTTYPE_UCS2_SWAPPED_LOB: {
                if(parameterdata && lengthindicator) {
                    // in this case we really have data for the parameter, we have to trace it
                    s << "*** DATA TRACE NOT IMPLEMENTED ***";
                } else {
                    IFR_LOB::sqlTrace(s, asLOBData(offset, rowsize));
                }
            }
                break;
            default:
                s << "*** NOT IMPLEMENTED ***";
            }
        }
    }
    
    switch(conversionRC) {
    case IFR_DATA_TRUNC:
        s << " *** TRUNCATED ***";
        break;
    case IFR_OVERFLOW:
        s << " ***OVERFLOW ***";
    }

    return;
}



