/*
 * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "awt_Object.h"
#include "ObjectList.h"

#ifdef DEBUG
static BOOL reportEvents = FALSE;
#endif


/************************************************************************
 * AwtObject fields
 */

jfieldID AwtObject::pDataID;
jfieldID AwtObject::destroyedID;
jfieldID AwtObject::targetID;
jclass AwtObject::wObjectPeerClass;
jmethodID AwtObject::getPeerForTargetMID;
jfieldID AwtObject::createErrorID;


/************************************************************************
 * AwtObject methods
 */

AwtObject::AwtObject()
{
    theAwtObjectList.Add(this);
    m_peerObject = NULL;
    m_callbacksEnabled = TRUE;
}

AwtObject::~AwtObject()
{
}

void AwtObject::Dispose()
{
    AwtToolkit::GetInstance().PostMessage(WM_AWT_DELETEOBJECT, (WPARAM)this, (LPARAM)0);
}

void AwtObject::_Dispose(jobject self)
{
    TRY_NO_VERIFY;

    CriticalSection::Lock l(AwtToolkit::GetInstance().GetSyncCS());

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jobject selfGlobalRef = env->NewGlobalRef(self);

    // value 0 of lParam means that we should not attempt to enter the
    // SyncCall critical section, as it was entered someshere earlier
    AwtToolkit::GetInstance().SendMessage(WM_AWT_DISPOSE, (WPARAM)selfGlobalRef, (LPARAM)0);

    CATCH_BAD_ALLOC;
}

void AwtObject::_Dispose(PDATA pData)
{
    TRY_NO_VERIFY;

    CriticalSection::Lock l(AwtToolkit::GetInstance().GetSyncCS());

    AwtToolkit::GetInstance().SendMessage(WM_AWT_DISPOSEPDATA, (WPARAM)pData, (LPARAM)0);

    CATCH_BAD_ALLOC;
}
/*
 * Return the peer associated with some target.  This information is
 * maintained in a hashtable at the java level.
 */
jobject AwtObject::GetPeerForTarget(JNIEnv *env, jobject target)
{
    jobject result =
        env->CallStaticObjectMethod(AwtObject::wObjectPeerClass,
                                    AwtObject::getPeerForTargetMID,
                                    target);

    DASSERT(!safe_ExceptionOccurred(env));
    return result;
}

/* Execute a callback to the associated Java peer. */
void
AwtObject::DoCallback(const char* methodName, const char* methodSig, ...)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    /* don't callback during the create & initialization process */
    if (m_peerObject != NULL && m_callbacksEnabled) {
        va_list args;
        va_start(args, methodSig);
#ifdef DEBUG
        if (reportEvents) {
            jstring targetStr =
                (jstring)JNU_CallMethodByName(env, NULL, GetTarget(env),
                                              "getName",
                                              "()Ljava/lang/String;").l;
            DASSERT(!safe_ExceptionOccurred(env));
            LPCWSTR targetStrW = JNU_GetStringPlatformChars(env, targetStr, NULL);
            printf("Posting %s%s method to %S\n", methodName, methodSig, targetStrW);
            JNU_ReleaseStringPlatformChars(env, targetStr, targetStrW);
        }
#endif
        /* caching would do much good here */
        JNU_CallMethodByNameV(env, NULL, GetPeer(env),
                              methodName, methodSig, args);
        {
            jthrowable exc = safe_ExceptionOccurred(env);
            if (exc) {
                env->DeleteLocalRef(exc);
                env->ExceptionDescribe();
                env->ExceptionClear();
            }
        }
        DASSERT(!safe_ExceptionOccurred(env));
        va_end(args);
    }
}

void AwtObject::SendEvent(jobject event)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

#ifdef DEBUG
    if (reportEvents) {
        jstring eventStr = JNU_ToString(env, event);
        DASSERT(!safe_ExceptionOccurred(env));
        jstring targetStr =
            (jstring)JNU_CallMethodByName(env, NULL, GetTarget(env),"getName",
                                          "()Ljava/lang/String;").l;
        DASSERT(!safe_ExceptionOccurred(env));
        LPCWSTR eventStrW = JNU_GetStringPlatformChars(env, eventStr, NULL);
        LPCWSTR targetStrW = JNU_GetStringPlatformChars(env, targetStr, NULL);
        printf("Posting %S to %S\n", eventStrW, targetStrW);
        JNU_ReleaseStringPlatformChars(env, eventStr, eventStrW);
        JNU_ReleaseStringPlatformChars(env, targetStr, targetStrW);
    }
#endif
    /* Post event to the system EventQueue. */
    JNU_CallMethodByName(env, NULL, GetPeer(env), "postEvent",
                         "(Ljava/awt/AWTEvent;)V", event);
    {
        jthrowable exc = safe_ExceptionOccurred(env);
        if (exc) {
            env->DeleteLocalRef(exc);
            env->ExceptionDescribe();
        }
    }
    DASSERT(!safe_ExceptionOccurred(env));
}

//
// (static)
// Switches to Windows thread via SendMessage and synchronously
// calls AwtObject::WinThreadExecProc with the given command id
// and parameters.
//
// Useful for writing code that needs to be synchronized with
// what's happening on the Windows thread.
//
LRESULT AwtObject::WinThreadExec(
    jobject                             peerObject,
    UINT                                cmdId,
    LPARAM                              param1,
    LPARAM                              param2,
    LPARAM                              param3,
    LPARAM                              param4 )
{
    DASSERT( peerObject != NULL);

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    // since we pass peerObject to another thread we must
    //   make a global ref
    jobject peerObjectGlobalRef = env->NewGlobalRef(peerObject);

    ExecuteArgs         args;
    LRESULT         retVal;

    // setup arguments
    args.cmdId = cmdId;
    args.param1 = param1;
    args.param2 = param2;
    args.param3 = param3;
    args.param4 = param4;

    // call WinThreadExecProc on the toolkit thread
    retVal = AwtToolkit::GetInstance().SendMessage(WM_AWT_EXECUTE_SYNC,
                                                   (WPARAM)peerObjectGlobalRef,
                                                   (LPARAM)&args);
    return retVal;
}

LRESULT AwtObject::WinThreadExecProc(ExecuteArgs * args)
{
    DASSERT(FALSE); // no default handler
    return 0L;
}

/************************************************************************
 * WObjectPeer native methods
 */

extern "C" {

JNIEXPORT void JNICALL
Java_sun_awt_windows_WObjectPeer_initIDs(JNIEnv *env, jclass cls) {
    TRY;

    AwtObject::wObjectPeerClass = (jclass)env->NewGlobalRef(cls);
    AwtObject::pDataID = env->GetFieldID(cls, "pData", "J");
    AwtObject::destroyedID = env->GetFieldID(cls, "destroyed", "Z");
    AwtObject::targetID = env->GetFieldID(cls, "target",
                                              "Ljava/lang/Object;");

    AwtObject::getPeerForTargetMID =
        env->GetStaticMethodID(cls, "getPeerForTarget",
                         "(Ljava/lang/Object;)Lsun/awt/windows/WObjectPeer;");

    AwtObject::createErrorID = env->GetFieldID(cls, "createError", "Ljava/lang/Error;");

    DASSERT(AwtObject::pDataID != NULL);
    DASSERT(AwtObject::destroyedID != NULL);
    DASSERT(AwtObject::targetID != NULL);
    DASSERT(AwtObject::getPeerForTargetMID != NULL);
    DASSERT(AwtObject::createErrorID != NULL);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */
