/* Copyright (c) 2001 IBK-Landquart-Switzerland. All rights reserved.
 *
 * Module      :  ZSql.cpp
 * Application :  IBK Open Class Library
 * Purpose     :  Virtual base class for universal sql database interface
 *
 * Date        Description                                 Who
 * --------------------------------------------------------------------------
 * 2001.05.29  First implementation                        P.Koch, IBK
 *
 * OPEN SOURCE LICENSE
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * 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 IBK at info@ibk-software.ch.
 */

#define ZC_BUILDING_ZBASE

#include <ZTrace.hpp>
#include <ZDynLink.hpp>
#include <ZSql.hpp>
#include <string.h>

ZEXCLASSIMPLEMENT (ZSqlError, ZException);

class Dll:public ZBase
{
public:
  Dll (const ZString & aName, ZDynamicLinkLibrary * aDll,
       Dll * aPrev):iName (aName), iDll (aDll), iPrev (aPrev)
  {
  }                             // Dll

   ~Dll ()
  {
    delete iDll;
    if (iPrev)
      delete iPrev;
  }                             // ~Dll

  ZString iName;                // name of library
  ZDynamicLinkLibrary *iDll;    // library handle
  Dll *iPrev;                   // previous library
};                              // DLL

static Dll *dlls = 0;

///////////////////////////////// ZSqlLink /////////////////////////////////

ZExport (ZSqlLink *) ZSqlLink::connect (const char *aDatabase,    // db name (ORACLE, DB2, ...)
                                        const char *aConnection,  // connection/datastore name
                                        const char *aUsername,    // users name
                                        const char *aPassword,    // user password
                                        int aMaxCursor)     // cursor pool size
{
  ZFUNCTRACE_DEVELOP
    ("ZSqlLink::connect(const char*, const char*, const char*, const char*, int)");
#if ZC_WIN || ZC_OS2
#if ZC_IBM
  ZString dllName (ZString ("Z") + aDatabase + "I");
#endif
#if ZC_MICROSOFT
  ZString dllName (ZString ("Z") + aDatabase + "M");
#endif
#if ZC_BORLAND
  ZString dllName (ZString ("Z") + aDatabase + "B");
#endif
#if ZC_GNU
  ZString dllName (ZString ("Z") + aDatabase + "G");
#endif
  dllName.lowerCase ();
#endif // ZC_WIN || ZC_OS2
#if ZC_UNIXFAM
  ZString db (aDatabase);
  ZString dllName (ZString ("libZ")
                   + db.subString (1, 1).upperCase ()
                   + db.subString (2).lowerCase ()
                   + "G.so." + ZString (ZC_MAJOR_VERSION));
#endif // ZC_UNIXFAM

  // check if allready loaded
  Dll *d = dlls;
  while (d)
      {
        if (d->iName == dllName)
          break;
        d = d->iPrev;
      }                         // while

  if (!d)
      {
        ZDynamicLinkLibrary *dll =
          new ZDynamicLinkLibrary (dllName.constBuffer ());
        d = dlls = new Dll (dllName, dll, dlls);
      }                         // if
  ZSqlLink *(*connectTo) (const char *, const char *, const char *, int);
  connectTo = (ZSqlLink * (*)(const char *, const char *, const char *, int))
    d->iDll->procAddress (
#if ZC_WIN || ZC_OS2
#if ZC_IBM
                            "ZSqlConnectTo__FPCcN21i"
#endif
#if ZC_BORLAND
                            "@ZSqlConnectTo$qpxct1t1i"
#endif
#if ZC_MICROSOFT
                            "?ZSqlConnectTo@@YAPAVZSqlLink@@PBD00H@Z"
#endif
#if ZC_GNU
                            "ZSqlConnectTo__FPCcN21i"
#endif
#endif // ZC_WIN || ZC_OS2
#if ZC_UNIXFAM
                            "ZSqlConnectTo__FPCcN20i"
#endif // ZC_UNIXFAM
    );
  return (*connectTo) (aConnection, aUsername, aPassword, aMaxCursor);
}                               // connect

ZExport0 ZSqlLink::ZSqlLink (int aMaxCursor)
{
  ZFUNCTRACE_DEVELOP ("ZSqlLink::ZSqlLink(int aMaxCursor)");
  iMaxCursor = aMaxCursor;
  iCsrCount = 0;
  iCsrPool = new ZSqlCursor *[aMaxCursor];
}                               // ZSqlLink

ZExport0 ZSqlLink::~ZSqlLink ()
{
  ZFUNCTRACE_DEVELOP ("ZSqlLink::~ZSqlLink()");
  for (int i = 0; i < iCsrCount; i++)
    delete iCsrPool[i];
  delete[]iCsrPool;
}                               // ~ZSqlLink

ZExport (ZString) ZSqlLink::literal (const char *s)
{
  ZFUNCTRACE_DEVELOP ("ZSqlLink::literal(const char *s)");
  ZString str ("'");
  while (*s)
      {
        if (*s == '\'')
          str += *s;
        str += *s++;
      }                         // while
  return str + "'";
}                               // literal

ZExport (int) ZSqlLink::getCursor (ZSqlCursor ** csr)
{
  ZFUNCTRACE_DEVELOP ("ZSqlLink::getCursor(ZSqlCursor **csr)");

  int ret, prio, curr;
  int old = -1;
  ZSqlCursor *c;

  // search for active cursor
  for (curr = 0; curr < iCsrCount; curr++)
      {
        c = iCsrPool[curr];
        if (csr == c->iOwner && *csr == c)
            {
              ret = c->newParseRequired () || c->iInv;
              goto gotcursor;
            }                   // if
        if (!c->iPrio)
          old = curr;           // remember least busy cursor
      }                         // for

  // check if new cursor may be opened
  if (iCsrCount < iMaxCursor)
      {
        iCsrPool[iCsrCount] = allocCursor ();
        curr = iCsrCount;
        iCsrPool[curr]->iPrio = iCsrCount++;
      }
  else
      {
        if (old < 0)
          ZTHROWEXC ("Internal error in ZSql.cpp");
        curr = old;
        iCsrPool[curr]->newParseRequired ();    // for DB2 cleanup!
      }                         // if

  // save owner signature and return SqlCursor ptr
  ret = 1;
  c = *csr = iCsrPool[curr];
  c->iOwner = csr;

gotcursor:
  c->iInv = zFalse;
  // mark this cursor most busy
  if (c->iPrio < iCsrCount - 1)
      {
        prio = c->iPrio;
        c->iPrio = iCsrCount;
        for (curr = 0; curr < iCsrCount; curr++)
            {
              c = iCsrPool[curr];
              if (c->iPrio >= prio)
                c->iPrio--;
            }                   // for
      }                         // if
  return ret;
}                               // getCursor

ZExport (ZSqlLink &) ZSqlLink::invCursor (ZSqlCursor ** csr)
{
  ZFUNCTRACE_DEVELOP ("ZSqlLink::invCursor(ZSqlCursor **csr)");
  for (int curr = 0; curr < iCsrCount; curr++)
      {
        ZSqlCursor *c = iCsrPool[curr];
        if (csr == c->iOwner && *csr == c)
            {
              c->iInv = zTrue;
              break;
            }                   // if
      }                         // for
  return *this;
}                               // ZSqlLink::invCursor

///////////////////////////////// ZSqlCursor /////////////////////////////////

ZExport (int) ZSqlCursor::newParseRequired ()
{
  ZFUNCTRACE_DEVELOP ("ZSqlCursor::newParseRequired()");
  return 0;
}                               // newParseRequired

#define DEFSHIFTLEFTNUM(VarType) \
ZExport(ZSqlCursor&) ZSqlCursor::operator<<(VarType val)\
{\
    ZFUNCTRACE_DEVELOP("ZSqlCursor::operator<<(VarType val)");\
    bindValue(val);\
    return *this;\
}
DEFSHIFTLEFTNUM (const ZSqlInt8 &)
DEFSHIFTLEFTNUM (const ZSqlInt16 &)
DEFSHIFTLEFTNUM (const ZSqlInt32 &)
DEFSHIFTLEFTNUM (const ZSqlFloat32 &)
DEFSHIFTLEFTNUM (const ZSqlFloat64 &) DEFSHIFTLEFTNUM (const ZSqlChar &)
#define DEFSHIFTLEFTBASIC(VarType,DestType) \
ZExport(ZSqlCursor&) ZSqlCursor::operator<<(VarType val)\
{\
    ZFUNCTRACE_DEVELOP("ZSqlCursor::operator<<(VarType val)");\
    bindValue(DestType(val));\
    return *this;\
}
DEFSHIFTLEFTBASIC (char, ZSqlInt8)
DEFSHIFTLEFTBASIC (signed char, ZSqlInt8)
DEFSHIFTLEFTBASIC (unsigned char, ZSqlInt8)
DEFSHIFTLEFTBASIC (short, ZSqlInt16)
DEFSHIFTLEFTBASIC (unsigned short, ZSqlInt16)
DEFSHIFTLEFTBASIC (long, ZSqlInt32)
DEFSHIFTLEFTBASIC (unsigned long, ZSqlInt32)
DEFSHIFTLEFTBASIC (int, ZSqlInt32)
DEFSHIFTLEFTBASIC (unsigned int, ZSqlInt32)
DEFSHIFTLEFTBASIC (float, ZSqlFloat32)
DEFSHIFTLEFTBASIC (double, ZSqlFloat64)
DEFSHIFTLEFTBASIC (char *, ZSqlChar)
DEFSHIFTLEFTBASIC (signed char *, ZSqlChar)
DEFSHIFTLEFTBASIC (unsigned char *, ZSqlChar)
//DEFSHIFTLEFTBASIC(const ZString&,ZSqlChar)
ZExport (ZSqlCursor &) ZSqlCursor::operator<< (const ZString & val)
{
  ZFUNCTRACE_DEVELOP ("ZSqlCursor::operator<<(const ZString& val)");
  bindValue (ZSqlChar ((const char *) val));
  return *this;
}                               // operator<<


#define DEFSHIFTRIGHTSQL(VarType) \
ZExport(ZSqlCursor&) ZSqlCursor::operator>>(VarType& val)\
{\
    ZFUNCTRACE_DEVELOP("ZSqlCursor::operator>>(VarType& val)");\
    fetchValue(val);\
    return *this;\
}
DEFSHIFTRIGHTSQL (ZSqlInt8)
DEFSHIFTRIGHTSQL (ZSqlInt16)
DEFSHIFTRIGHTSQL (ZSqlInt32)
DEFSHIFTRIGHTSQL (ZSqlFloat32)
DEFSHIFTRIGHTSQL (ZSqlFloat64) DEFSHIFTRIGHTSQL (ZSqlChar)
#define DEFSHIFTRIGHTNUM(VarType,DestType) \
ZExport(ZSqlCursor&) ZSqlCursor::operator>>(VarType& val)\
{\
    ZFUNCTRACE_DEVELOP("ZSqlCursor::operator>>(VarType& val)");\
    DestType dst;\
    fetchValue(dst);\
    val=dst;\
    return *this;\
}
DEFSHIFTRIGHTNUM (char, ZSqlInt8)
DEFSHIFTRIGHTNUM (signed char, ZSqlInt8)
DEFSHIFTRIGHTNUM (unsigned char, ZSqlInt8)
DEFSHIFTRIGHTNUM (short, ZSqlInt16)
DEFSHIFTRIGHTNUM (unsigned short, ZSqlInt16)
DEFSHIFTRIGHTNUM (long, ZSqlInt32)
DEFSHIFTRIGHTNUM (unsigned long, ZSqlInt32)
DEFSHIFTRIGHTNUM (int, ZSqlInt32)
DEFSHIFTRIGHTNUM (unsigned int, ZSqlInt32)
DEFSHIFTRIGHTNUM (float, ZSqlFloat32) DEFSHIFTRIGHTNUM (double, ZSqlFloat64)
#define DEFSHIFTRIGHTCHAR(VarType) \
ZExport(ZSqlCursor&) ZSqlCursor::operator>>(VarType *val)\
{\
    ZFUNCTRACE_DEVELOP("ZSqlCursor::operator>>(VarType *val)");\
    ZSqlChar str;\
    fetchValue(str);\
    strcpy((char*)val,str.constBuffer());\
    return *this;\
}
DEFSHIFTRIGHTCHAR (char)
DEFSHIFTRIGHTCHAR (signed char)
DEFSHIFTRIGHTCHAR (unsigned char)
ZExport (ZSqlCursor &) ZSqlCursor::operator>> (ZString & val)
{
  ZFUNCTRACE_DEVELOP ("ZSqlCursor::operator>>(ZString& val)");
  ZSqlChar str;
  fetchValue (str);
  val = ZString ((char *) str);
  return *this;
}                               // ZSqlCursor& operator>>(ZString&)
