//
// Object.h
//

#pragma once

#include "Raym/Log.h"
#define RAYM_MEMORY_CHECK
//#define RAYM_MUTEX_CHECK

#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <stdlib.h>
#include <Raym/Runtime.h>

#define SET_METHOD(P1, P2)  \
    if ((P1) != NULL)       \
    {                       \
        (P1)->release();    \
    }                       \
    (P1) = (P2);            \
    if ((P1) != NULL)       \
    {                       \
        (P1)->retain();     \
    }

#define RELEASE(P1)         \
    if ((P1) != NULL)       \
    {                       \
        (P1)->release();    \
        (P1) = NULL;        \
    }

#define isKindOfClass(CLS, OBJ) \
    (strcmp(#CLS, (OBJ)->className()) == 0)


#ifdef _WIN32

#define RAYM_LOCK_CREATE    InitializeCriticalSection(&_cs)
#define RAYM_LOCK_DESTROY   DeleteCriticalSection(&_cs)
#define RAYM_COND_CREATE    InitializeConditionVariable(&_cond)
#define RAYM_COND_DESTROY   pthread_cond_destroy(&_cond)

#define DEFINE_STATIC_MUTEX(variable)       \
class STATIC_MUTEX_variable                 \
{                                           \
private:                                    \
    CRITICAL_SECTION _cs;                   \
                                            \
public:                                     \
    STATIC_MUTEX_variable()                 \
    {                                       \
        InitializeCriticalSection(&_cs);    \
    }                                       \
    ~STATIC_MUTEX_variable()                \
    {                                       \
        DeleteCriticalSection(&_cs);        \
    }                                       \
    void lock()                             \
    {                                       \
        EnterCriticalSection(&_cs);         \
    }                                       \
    void unlock()                           \
    {                                       \
        LeaveCriticalSection(&_cs);         \
    }                                       \
};                                          \
static STATIC_MUTEX_variable variable;

#else

#define RAYM_LOCK_CREATE    {                                   \
    pthread_mutexattr_t attr;                                   \
    pthread_mutexattr_init(&attr);                              \
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  \
    pthread_mutex_init(&_lock, &attr);                          \
}
#define RAYM_LOCK_DESTROY   pthread_mutex_destroy(&_lock)
#define RAYM_COND_CREATE    pthread_cond_init(&_cond, NULL)
#define RAYM_COND_DESTROY   pthread_cond_destroy(&_cond)

#define DEFINE_STATIC_MUTEX(variable)       \
class STATIC_MUTEX_variable                 \
{                                           \
private:                                    \
    pthread_mutex_t _lock;                  \
                                            \
public:                                     \
    STATIC_MUTEX_variable()                 \
    {                                       \
        pthread_mutexattr_t attr;                                   \
        pthread_mutexattr_init(&attr);                              \
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  \
        pthread_mutex_init(&_lock, &attr);                          \
    }                                       \
    ~STATIC_MUTEX_variable()                \
    {                                       \
        pthread_mutex_destroy(&_lock);        \
    }                                       \
    void lock()                             \
    {                                       \
        pthread_mutex_lock(&_lock);         \
    }                                       \
    void unlock()                           \
    {                                       \
        pthread_mutex_unlock(&_lock);         \
    }                                       \
};                                          \
static STATIC_MUTEX_variable variable;
#endif

namespace Raym
{

#ifdef RAYM_MEMORY_CHECK
extern int global_raym_count_;
extern int global_raym_init_count_;
extern int global_raym_retain_count_;
extern int global_raym_autorelease_count_;
extern int global_raym_release_count_;
#endif

typedef unsigned int    uint;
typedef unsigned char   uchar;

class String;

class Object
{
protected:
    Object();
    virtual ~Object();

    int                 _retainCount;

#ifdef RAYM_MUTEX_CHECK
public:
#endif
#ifdef _WIN32
    CRITICAL_SECTION    _cs;
    CONDITION_VARIABLE  _cond;
#else
    pthread_mutex_t     _lock;
    pthread_cond_t      _cond;
#endif

public:
    //
    static Object *alloc();

    //
    Object *init();
    Object *retain();
    Object *autorelease();
    Object *autorelease(bool rootPool);
    void release();

    virtual String *description();

//    virtual const char *className() = 0;
    virtual const char *className();

    friend void RaymLock(Object *);
    friend void RaymUnlock(Object *);
    friend void RaymCondWait(Object *);
    friend void RaymCondTimedWait(Object *, int msec);
    friend void RaymCondSignal(Object *);
    friend void RaymCondBroadcast(Object *);
};

#ifndef RAYM_MUTEX_CHECK
inline void RaymLock(Object *obj)
{
#ifdef _WIN32
    EnterCriticalSection(&(obj->_cs));
#else
#endif
}
#else
#ifdef _WIN32
#define RaymLock(obj) \
    DebugLog3("before lock: %s %d", __FILE__, __LINE__); \
    EnterCriticalSection(&(obj->_cs)); \
    DebugLog3("after lock: %s %d", __FILE__, __LINE__);
#else
#endif
#endif

#ifndef RAYM_MUTEX_CHECK
inline void RaymUnlock(Object *obj)
{
#ifdef _WIN32
    LeaveCriticalSection(&(obj->_cs));
#else
#endif
}
#else
#ifdef _WIN32
#define RaymUnlock(obj) \
    DebugLog3("before unlock: %s %d", __FILE__, __LINE__); \
    LeaveCriticalSection(&(obj->_cs)); \
    DebugLog3("after unlock: %s %d", __FILE__, __LINE__);
#else
#endif
#endif

#ifndef RAYM_MUTEX_CHECK
inline void RaymCondWait(Object *obj)
{
#ifdef _WIN32
    SleepConditionVariableCS(&(obj->_cond), &(obj->_cs), INFINITE);
#else
#endif
}
#else
#ifdef _WIN32
#define RaymCondWait(obj) \
    DebugLog3("before wait: %s %d", __FILE__, __LINE__); \
    SleepConditionVariableCS(&(obj->_cond), &(obj->_cs), INFINITE); \
    DebugLog3("after wait: %s %d", __FILE__, __LINE__);
#else
#endif
#endif

#ifndef RAYM_MUTEX_CHECK
inline void RaymCondTimedWait(Object *obj, int msec)
{
#ifdef _WIN32
    SleepConditionVariableCS(&(obj->_cond), &(obj->_cs), (DWORD)msec);
#else
#endif
}
#else
#ifdef _WIN32
#define RaymCondTimedWait(obj, msec) \
    DebugLog3("before wait: %s %d", __FILE__, __LINE__); \
    SleepConditionVariableCS(&(obj->_cond), &(obj->_cs), (DWORD)msec); \
    DebugLog3("after wait: %s %d", __FILE__, __LINE__);
#else
#endif
#endif

#ifndef RAYM_MUTEX_CHECK
inline void RaymCondSignal(Object *obj)
{
#ifdef _WIN32
    WakeConditionVariable(&(obj->_cond));
#else
#endif
}
#else
#ifdef _WIN32
#define RaymCondSignal(obj) \
    DebugLog3("before signal: %s %d", __FILE__, __LINE__); \
    WakeConditionVariable(&(obj->_cond)); \
    DebugLog3("after signal: %s %d", __FILE__, __LINE__);
#else
#endif
#endif

inline void RaymCondBroadcast(Object *obj)
{
#ifdef _WIN32
    WakeAllConditionVariable(&(obj->_cond));
#else
#endif
}


//#define RaymCriticalSection(OBJ, BLOCK) RaymLock(OBJ); BLOCK; RaymUnlock(OBJ);

} // Raym

