//-< CLASS.H >-------------------------------------------------------*--------*
// GigaBASE                  Version 1.0         (c) 1999  GARRET    *     ?  *
// (Post Relational Database Management System)                      *   /\|  *
//                                                                   *  /  \  *
//                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
//                          Last update: 10-Dec-98    K.A. Knizhnik  * GARRET *
//-------------------------------------------------------------------*--------*
// Metaclass information
//-------------------------------------------------------------------*--------*

#ifndef __CLASS_H__
#define __CLASS_H__

#include "stdtp.h"
#include "sync.h"

#ifdef USE_STD_STRING
#include <string>
#endif

BEGIN_GIGABASE_NAMESPACE

#ifndef dbDatabaseOffsetBits
#define dbDatabaseOffsetBits 32 // 37 - up to 1 terabyte
#endif

#ifndef dbDatabaseOidBits
#define dbDatabaseOidBits 32
#endif

#if dbDatabaseOidBits > 32
typedef nat8 oid_t;  // It will work only for 64-bit OS
#else
typedef nat4 oid_t;
#endif

#if dbDatabaseOffsetBits > 32
typedef nat8 offs_t; // It will work only for 64-bit OS
#else
typedef nat4 offs_t;
#endif

enum dbIndexType {
    HASHED  = 1, // hash table
    INDEXED = 2, // B-tree
    CASE_INSENSITIVE = 4, // Index is case insensetive

    DB_FIELD_CASCADE_DELETE = 8,   // Used by OWNER macro, do not set it explicitly
    UNIQUE = 16, // should be used in conjunction with HASHED or INDEXED - unique constraint 

    AUTOINCREMENT = 32, // field is assigned automaticall incremented value

    DB_FIELD_INHERITED_MASK = ~(HASHED|INDEXED)
};

#define KEY(x, index) \
    *dbDescribeField(new dbFieldDescriptor(STRLITERAL(#x), (char*)&x-(char*)this, \
                                           sizeof(x), index), x)

#define FIELD(x) KEY(x, 0)

// user defined raw binary field
typedef int (*dbUDTComparator)(void*, void*, size_t);

#define UDT(x, index, comparator) \
    *dbDescribeRawField(new dbFieldDescriptor(STRLITERAL(#x), (char*)&x-(char*)this, \
                                                sizeof(x), index), (dbUDTComparator)comparator)

#define RAWFIELD(x) UDT(x, 0, &memcmp)
#define RAWKEY(x, index) UDT(x, index, &memcmp)


#define RELATION(x,inverse) \
    *dbDescribeField(new dbFieldDescriptor(STRLITERAL(#x), (char*)&x-(char*)this, \
                                             sizeof(x), 0, #inverse), x)

#define INDEXED_RELATION(x,inverse) \
    *dbDescribeField(new dbFieldDescriptor(STRLITERAL(#x), (char*)&x-(char*)this, \
                                             sizeof(x), INDEXED, #inverse), x)
 
#define OWNER(x,member) \
    *dbDescribeField(new dbFieldDescriptor(STRLITERAL(#x), (char*)&x-(char*)this, \
                                             sizeof(x), DB_FIELD_CASCADE_DELETE, \
                                             #member), x)

#define METHOD(x) \
    *dbDescribeMethod(new dbFieldDescriptor(STRLITERAL(#x)), &self::x)

#define SUPERCLASS(x) \
    x::dbDescribeComponents(NULL)->adjustOffsets((char*)((x*)this)-(char*)this)

#define TYPE_DESCRIPTOR(fields) \
    dbFieldDescriptor* dbDescribeComponents(dbFieldDescriptor*) { \
	return &fields; \
    } \
    static dbTableDescriptor dbDescriptor

#define CLASS_DESCRIPTOR(name, fields) \
    typedef name self; \
    dbFieldDescriptor* dbDescribeComponents(dbFieldDescriptor*) { \
	return &fields; \
    } \
    static dbTableDescriptor dbDescriptor

#define REGISTER_IN(table, database) \
    dbTableDescriptor* dbGetTableDescriptor(table*) \
      { return &table::dbDescriptor; }              \
    static dbFieldDescriptor* dbDescribeComponentsOf##table() \
      { return ((table*)0)->dbDescribeComponents(NULL); }     \
    dbTableDescriptor table::dbDescriptor(_T(#table), database, sizeof(table), \
					  &dbDescribeComponentsOf##table)
#define REGISTER(table) REGISTER_IN(table, NULL)

#define DETACHED_TABLE ((dbDatabase*)-1)

#define REGISTER_UNASSIGNED(table) REGISTER_IN(table, DETACHED_TABLE)


class dbDatabase;
class dbAnyArray;
class dbTableDescriptor;
class dbAnyMethodTrampoline;
class dbTable;

class GIGABASE_DLL_ENTRY dbFieldDescriptor {
  public:
    dbFieldDescriptor* next; // next file within scope
    dbFieldDescriptor* prev; // previous field within scope

    dbFieldDescriptor* nextField;

    dbFieldDescriptor* nextHashedField;
    dbFieldDescriptor* nextIndexedField;
    dbFieldDescriptor* nextInverseField;

    int                fieldNo;
    char_t*            name;
    char_t*            longName;
    char_t*            refTableName;
    dbTableDescriptor* refTable;
    dbTableDescriptor* defTable;
    dbFieldDescriptor* inverseRef;
    char_t*            inverseRefName;
    int                type;
    int                appType;
    int                indexType;
    int                dbsOffs;
    int                appOffs;
    dbFieldDescriptor* components;
    void*              trampoline;
    oid_t              hashTable;
    oid_t              bTree;
    size_t             dbsSize;
    size_t             appSize;
    size_t             alignment;
    dbUDTComparator    comparator;

    enum FieldAttributes {
	ComponentOfArray   = 0x01,
	HasArrayComponents = 0x02,
	OneToOneMapping    = 0x04,
	Updated            = 0x08
    };
    int                attr;

    int                oldDbsType;
    int                oldDbsOffs;
    int                oldDbsSize;

    dbAnyMethodTrampoline* method;

    void (*arrayAllocator)(dbAnyArray* array, void* data, size_t length);

    size_t calculateRecordSize(byte* base, size_t offs);
    size_t calculateNewRecordSize(byte* base, size_t offs);
    size_t convertRecord(byte* dst, byte* src, size_t offs);

    int    sizeWithoutOneField(dbFieldDescriptor* field,
			       byte* base, size_t& size);

    size_t copyRecordExceptOneField(dbFieldDescriptor* field,
				    byte* dst, byte* src, size_t offs);

    size_t storeRecordFields(byte* dst, byte* src, size_t offs, bool insert);

    void markUpdatedFields(byte* dst, byte* src);

    void fetchRecordFields(byte* dst, byte* src);

    dbFieldDescriptor* find(const char_t* name);

    dbFieldDescriptor& operator, (dbFieldDescriptor& field) {
	dbFieldDescriptor* tail = field.prev;
	tail->next = this;
	prev->next = &field;
	field.prev = prev;
	prev = tail;
	return *this;
    }

    dbFieldDescriptor& adjustOffsets(long offs);

    dbFieldDescriptor(char_t* name, int offs, int size, int indexType,
		      char_t* inverse = NULL,
		      dbFieldDescriptor* components = NULL);

    dbFieldDescriptor(char_t* name);
    ~dbFieldDescriptor();
};

class GIGABASE_DLL_ENTRY dbTableDescriptor {
    friend class dbCompiler;
    friend class dbDatabase;
    friend class dbTable;
    friend class dbAnyCursor;
    friend class dbSubSql;
    friend class dbParallelQueryContext;
    friend class dbServer;
    friend class dbAnyContainer;
    friend class dbColumnBinding;
    friend class dbFieldDescriptor;
  protected:
    dbTableDescriptor*  next;
    static dbTableDescriptor* chain;

    dbTableDescriptor*  nextDbTable; // next table in the database

    char_t*             name;
    oid_t               tableId;
    dbFieldDescriptor*  columns;
    dbFieldDescriptor*  hashedFields;
    dbFieldDescriptor*  indexedFields;
    dbFieldDescriptor*  inverseFields;
    dbFieldDescriptor*  firstField;
    dbFieldDescriptor** nextFieldLink;
    dbDatabase*         db;
    bool                fixedDatabase;
    bool                isStatic;
    dbTableDescriptor*  cloneOf;
    size_t              appSize;
    size_t              fixedSize;
    size_t              nFields;
    size_t              nColumns;

    oid_t               firstRow;
    oid_t               lastRow;
    size_t              nRows;
    int4                autoincrementCount;

    typedef dbFieldDescriptor* (*describeFunc)();
    describeFunc        describeComponentsFunc;

    size_t              totalNamesLength();

    int calculateFieldsAttributes(dbFieldDescriptor* fieldsList,
				  char_t const* prefix, int offs,
				  int indexMask, int& attr);

    dbFieldDescriptor* buildFieldsList(dbTable* table, char_t const* prefix,
				       int prefixLen, int& attr);
    dbFieldDescriptor* findSymbol(char_t const* name);
    dbTableDescriptor* clone();

  public:
    static int initialAutoincrementCount;

    dbFieldDescriptor* find(char_t const* name);

    char_t* getName() { 
	return name;
    }

    bool equal(dbTable* table);

    bool match(dbTable* table);

    void checkRelationship();

    dbDatabase* getDatabase() { return db; }

    void storeInDatabase(dbTable* table);

    void setFlags();

    static void cleanup();

    dbTableDescriptor(dbTable* table);
    dbTableDescriptor(char_t*            tableName, 
		      dbDatabase*        db, 
		      size_t             objSize,
		      describeFunc       func,
		      dbTableDescriptor* original = NULL);
    ~dbTableDescriptor();
};

struct dbVarying {
    nat4 size; // number of elements in the array
    int4 offs; // offset from the beginning of the record
};

struct dbField {
    enum FieldTypes {
	tpBool,
	tpInt1,
	tpInt2,
	tpInt4,
	tpInt8,
	tpReal4,
	tpReal8,
	tpString,
	tpReference,
	tpArray,
	tpMethodBool,
	tpMethodInt1,
	tpMethodInt2,
	tpMethodInt4,
	tpMethodInt8,
	tpMethodReal4,
	tpMethodReal8,
	tpMethodString,
	tpMethodReference,
	tpStructure,
	tpRawBinary,
	tpStdString,
	tpUnknown
    };

    dbVarying name;
    dbVarying tableName; // only for references: name of referenced table
    dbVarying inverse;   // only for relations: name of inverse reference field
    int4      type;
    int4      offset;
    nat4      size;
    oid_t     hashTable;
    oid_t     bTree;
};


class dbRecord {
  public:
    nat4   size;
    oid_t  next;
    oid_t  prev;
};

class dbTable : public dbRecord {
  public:
    dbVarying name;
    dbVarying fields;
    nat4      fixedSize;
    nat4      nRows;
    nat4      nColumns;
    oid_t     firstRow;
    oid_t     lastRow;
#ifdef AUTOINCREMENT_SUPPORT
    nat4      count;
#endif
};

inline dbFieldDescriptor* dbDescribeRawField(dbFieldDescriptor* fd, dbUDTComparator comparator)
{
    fd->type = fd->appType = dbField::tpRawBinary;
    fd->alignment = 1;
    fd->comparator = comparator;
    return fd;
}

inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, int1&)
{
    fd->type = fd->appType = dbField::tpInt1;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, int2&)
{
    fd->type = fd->appType = dbField::tpInt2;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, int4&)
{
    fd->type = fd->appType = dbField::tpInt4;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, int8&)
{
    fd->type = fd->appType = dbField::tpInt8;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, nat1&)
{
    fd->type = fd->appType = dbField::tpInt1;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, nat2&)
{
    fd->type = fd->appType = dbField::tpInt2;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, nat4&)
{
    fd->type = fd->appType = dbField::tpInt4;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, nat8&)
{
    fd->type = fd->appType = dbField::tpInt8;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, bool&)
{
    fd->type = fd->appType = dbField::tpBool;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, real4&)
{
    fd->type = fd->appType = dbField::tpReal4;
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, real8&)
{
    fd->type = fd->appType = dbField::tpReal8;
    return fd;
}

#ifdef USE_STD_STRING
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, STD_STRING&)
{
    fd->type = dbField::tpString;
    fd->appType = dbField::tpStdString;
    fd->dbsSize = sizeof(dbVarying);
    fd->alignment = 4;
    fd->components = new dbFieldDescriptor(STRLITERAL("[]"));
    fd->components->type = fd->components->appType = dbField::tpInt1 + (sizeof(char_t) - 1);
    fd->components->dbsSize = fd->components->appSize = fd->components->alignment = sizeof(char_t); 
    return fd;
}
#endif

inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, char_t const*&)
{
    fd->type = fd->appType = dbField::tpString;
    fd->dbsSize = sizeof(dbVarying);
    fd->alignment = 4;
    fd->components = new dbFieldDescriptor(STRLITERAL("[]"));
    fd->components->type = fd->components->appType = dbField::tpInt1 + (sizeof(char_t) - 1);
    fd->components->dbsSize = fd->components->appSize = fd->components->alignment = sizeof(char_t); 
    return fd;
}
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, char_t*&)
{
    fd->type = fd->appType = dbField::tpString;
    fd->dbsSize = sizeof(dbVarying);
    fd->alignment = 4;
    fd->components = new dbFieldDescriptor(STRLITERAL("[]"));
    fd->components->type = fd->components->appType = dbField::tpInt1 + (sizeof(char_t) - 1);
    fd->components->dbsSize = fd->components->appSize = fd->components->alignment = sizeof(char_t);
    return fd;
}


template<class T>
inline dbFieldDescriptor* dbDescribeField(dbFieldDescriptor* fd, T& x)
{
    fd->type = fd->appType = dbField::tpStructure;
    fd->components = x.dbDescribeComponents(fd);
    return fd;
}


class GIGABASE_DLL_ENTRY dbAnyMethodTrampoline {
  public:
    dbFieldDescriptor* cls;

    virtual void invoke(byte* data, void* result) = 0;
    virtual dbAnyMethodTrampoline* optimize() = 0;

    dbAnyMethodTrampoline(dbFieldDescriptor* fd) { cls = fd; }
    virtual~dbAnyMethodTrampoline();
};

#if defined(__APPLE__) || defined(__VACPP_MULTI__) || defined(__IBMCPP__) || \
    (__SUNPRO_CC == 0x530 && __SUNPRO_CC_COMPAT == 5)
template<class T, class R>
class dbMethodTrampoline : public dbAnyMethodTrampoline {
  public:
    typedef R (T::*mfunc)();

    mfunc              method;
    dbFieldDescriptor* cls;
    bool               optimized;

    void invoke(byte* data, void* result) {
	if (optimized) { 
	    *(R*)result = (((T*)(data + cls->dbsOffs))->*method)();
	} else { 
	    T rec;
	    cls->components->fetchRecordFields((byte*)&rec, data);
	    *(R*)result = (rec.*method)();
	}
    }
    dbAnyMethodTrampoline* optimize() { 
	optimized = true;
	return this;
    }

    dbMethodTrampoline(dbFieldDescriptor* fd, mfunc f)
    : dbAnyMethodTrampoline(fd), method(f), cls(fd), optimized(false) {}
};

#else

template<class T, class R>
class dbMethodTrampoline : public dbAnyMethodTrampoline {
  public:
    typedef R (T::*mfunc)();
    mfunc method;

    void invoke(byte* data, void* result) {
	T rec;
	cls->components->fetchRecordFields((byte*)&rec, data);
	*(R*)result = (rec.*method)();
    }
    dbAnyMethodTrampoline* optimize();

    dbMethodTrampoline(dbFieldDescriptor* fd, mfunc f)
    : dbAnyMethodTrampoline(fd), method(f) {}
};

template<class T, class R>
class dbMethodFastTrampoline : public dbMethodTrampoline<T,R> {
  public:
    void invoke(byte* data, void* result) {
	*(R*)result = (((T*)(data + cls->dbsOffs))->*method)();
    }
    dbMethodFastTrampoline(dbMethodTrampoline<T,R>* mt)
    : dbMethodTrampoline<T,R>(mt->cls, mt->method) {
	delete mt;
    }
};

template<class T, class R>
inline dbAnyMethodTrampoline* dbMethodTrampoline<T,R>::optimize() {
    return new dbMethodFastTrampoline<T,R>(this);
}

#endif
template<class T, class R>
inline dbFieldDescriptor* dbDescribeMethod(dbFieldDescriptor* fd, R (T::*p)())
{
    R ret;
    dbDescribeField(fd, ret);
    assert(fd->type <= dbField::tpReference);
    fd->type += dbField::tpMethodBool;
    fd->method = new dbMethodTrampoline<T,R>(fd, p);
    return fd;
}

END_GIGABASE_NAMESPACE

#endif


