/*======================================================================
 FILE: jlibical_utils_cxx.cpp
 CREATOR: Srinivasa Boppana/George Norman

 SPDX-FileCopyrightText: 2002, Critical Path

 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0

======================================================================*/

#ifndef JLIBICAL_UTILS_CXX_H
#include "jlibical_utils_cxx.hpp"
#endif

#ifndef JLIBICAL_CONSTS_CXX_H
#include "jlibical_consts_cxx.hpp"
#endif

#ifndef ICALPARAMETER_CXX_H
#include "icalparameter_cxx.hpp"
#endif

#ifndef VCOMPONENT_CXX_H
#include "vcomponent_cxx.hpp"
#endif

#ifndef ICALPROPERTY_CXX_H
#include "icalproperty_cxx.hpp"
#endif

#ifndef ICALVALUE_CXX_H
#include "icalvalue_cxx.hpp"
#endif

#ifndef _jni_ICalTimeType_H
#include "jniICalTimeType_cxx.hpp"
#endif

#ifndef _jni_ICalTriggerType_H
#include "jniICalTriggerType_cxx.hpp"
#endif

#ifndef _jni_ICalDurationType_H
#include "jniICalDurationType_cxx.hpp"
#endif

#ifndef _jni_ICalRecurrenceType_H
#include "jniICalRecurrenceType_cxx.hpp"
#endif

#ifndef _jni_ICalPeriodType_H
#include "jniICalPeriodType_cxx.hpp"
#endif

#include <cstdio>

using namespace LibICal;

//-------------------------------------------------------
// Returns a pointer to the subject (a c++ object) for the given surrogate (a java object)
//-------------------------------------------------------
const void *getCObjectPtr(JNIEnv *env, jobject surrogate)
{
    const void *result = 0;
    jclass jcls = env->GetObjectClass(surrogate);
    jfieldID fid = env->GetFieldID(jcls, "m_Obj", "J");

    if (fid == NULL) {
        // this should never happen.
        throwException(env, JLIBICAL_ERR_CLIENT_INTERNAL);
        return (NULL);
    }

    result = (void *)env->GetLongField(surrogate, fid);
    if (result == NULL) {
        // the proxy object (java) has no subject (c++ object)
        throwException(env, JLIBICAL_ERR_CLIENT_INTERNAL);
        return (NULL);
    }

    return (result);
}

//-------------------------------------------------------
// Set the subject (a c++ object) for the given surrogate (a java object).
// Throws exception if the m_Obj field can not be found.
//-------------------------------------------------------
void setCObjectPtr(JNIEnv *env, jobject surrogate, void *subject)
{
    jclass jcls = env->GetObjectClass(surrogate);
    jfieldID fid = env->GetFieldID(jcls, "m_Obj", "J");

    if (fid == NULL) {
        throwException(env, JLIBICAL_ERR_CLIENT_INTERNAL);
        return;
    }

    env->SetLongField(surrogate, fid, (jlong)subject);
}

//-------------------------------------------------------
// Return the pointer to the subject (as a VComponent*) from the given surrogate.
// If the subject is not a VComponent type, or if the subject is NULL, then return NULL.
//-------------------------------------------------------
const VComponent *getSubjectAsVComponent(JNIEnv *env, jobject surrogateComponent, int exceptionType)
{
    const VComponent *result = (VComponent *)(getCObjectPtr(env, surrogateComponent));

    if (result == NULL) {
        throwException(env, exceptionType);
    }

    return (result);
}

//-------------------------------------------------------
// Return the pointer to the subject (as an ICalProperty*) from the given surrogate.
// If the subject is not an ICalProperty type, or if the subject is NULL, then return NULL.
//-------------------------------------------------------
const ICalProperty *getSubjectAsICalProperty(JNIEnv *env, jobject surrogateProperty, int exceptionType)
{
    const ICalProperty *result = (ICalProperty *)(getCObjectPtr(env, surrogateProperty));

    if (result == NULL) {
        throwException(env, exceptionType);
    }

    return (result);
}

//-------------------------------------------------------
// Return the pointer to the subject (as an ICalValue*) from the given surrogate.
// If the subject is not an ICalValue type, or if the subject is NULL, then return NULL.
//-------------------------------------------------------
const ICalValue *getSubjectAsICalValue(JNIEnv *env, jobject surrogateValue, int exceptionType)
{
    const ICalValue *result = (ICalValue *)(getCObjectPtr(env, surrogateValue));

    if (result == NULL) {
        throwException(env, exceptionType);
    }

    return (result);
}

//-------------------------------------------------------
// Return the pointer to the subject (as an ICalParameter*) from the given surrogate.
// If the subject is not an ICalParameter type, or if the subject is NULL, then return NULL.
//-------------------------------------------------------
const ICalParameter *getSubjectAsICalParameter(JNIEnv *env, jobject surrogateParameter, int exceptionType)
{
    const ICalParameter *result = (ICalParameter *)(getCObjectPtr(env, surrogateParameter));

    if (result == NULL) {
        throwException(env, exceptionType);
    }

    return (result);
}

//-------------------------------------------------------
// Copy the data from the src (a java ICalTimeType object)
// to the dest (a c struct icaltimetype*).
// Returns true if success.  False if exception is thrown:
//      - the src java object is not an ICalTimeType type
//      - the dest c struct is null.
//-------------------------------------------------------
bool copyObjToicaltimetype(JNIEnv *env, jobject src, icaltimetype *dest)
{
    bool result = false;

    if (dest != NULL && (bool)env->IsInstanceOf(src, env->FindClass(JLIBICAL_CLASS_ICALTIMETYPE))) {
        jni_GetAll_from_ICalTimeType(dest, env, src);
        result = true;
    } else {
        throwException(env, JLIBICAL_ERR_ILLEGAL_ARGUMENT);
    }

    return (result);
}

//-------------------------------------------------------
// Copy the data from the src (a java ICalTriggerType object)
// to the dest (a c struct icaltriggertype*).
// Returns true if success.  False if exception is thrown:
//      - the src java object is not an ICalTriggerType type
//      - the dest c struct is null.
//-------------------------------------------------------
bool copyObjToicaltriggertype(JNIEnv *env, jobject src, icaltriggertype *dest)
{
    bool result = false;

    if (dest != NULL && (bool)env->IsInstanceOf(src, env->FindClass(JLIBICAL_CLASS_ICALTRIGGERTYPE))) {
        jni_GetAll_from_ICalTriggerType(dest, env, src);
        result = true;
    } else {
        throwException(env, JLIBICAL_ERR_ILLEGAL_ARGUMENT);
    }

    return (result);
}

//-------------------------------------------------------
// Copy the data from the src (a java ICalDurationType object)
// to the dest (a c struct icaldurationtype*).
// Returns true if success.  False if exception is thrown:
//      - the src java object is not an ICalDurationType type
//      - the dest c struct is null.
//-------------------------------------------------------
bool copyObjToicaldurationtype(JNIEnv *env, jobject src, icaldurationtype *dest)
{
    bool result = false;

    if (dest != NULL && (bool)env->IsInstanceOf(src, env->FindClass(JLIBICAL_CLASS_ICALDURATIONTYPE))) {
        jni_GetAll_from_ICalDurationType(dest, env, src);
        result = true;
    } else {
        throwException(env, JLIBICAL_ERR_ILLEGAL_ARGUMENT);
    }

    return (result);
}

//-------------------------------------------------------
// Copy the data from the src (a java ICalRecurrenceType object)
// to the dest (a c struct icalrecurrencetype*).
// Returns true if success.  False if exception is thrown:
//      - the src java object is not an ICalRecurrenceType type
//      - the dest c struct is null.
//-------------------------------------------------------
bool copyObjToicalrecurrencetype(JNIEnv *env, jobject src, icalrecurrencetype *dest)
{
    bool result = false;

    if (dest != NULL && (bool)env->IsInstanceOf(src, env->FindClass(JLIBICAL_CLASS_ICALRECURRENCETYPE))) {
        jni_GetAll_from_ICalRecurrenceType(dest, env, src);
        result = true;
    } else {
        throwException(env, JLIBICAL_ERR_ILLEGAL_ARGUMENT);
    }

    return (result);
}

//-------------------------------------------------------
// Copy the data from the src (a java ICalPeriodType object)
// to the dest (a c struct icalperiodtype*).
// Returns true if success.  False if exception is thrown:
//      - the src java object is not an ICalPeriodType type
//      - the dest c struct is null.
//-------------------------------------------------------
bool copyObjToicalperiodtype(JNIEnv *env, jobject src, icalperiodtype *dest)
{
    bool result = false;

    if (dest != NULL && (bool)env->IsInstanceOf(src, env->FindClass(JLIBICAL_CLASS_ICALPERIODTYPE))) {
        jni_GetAll_from_ICalPeriodType(dest, env, src);
        result = true;
    } else {
        throwException(env, JLIBICAL_ERR_ILLEGAL_ARGUMENT);
    }

    return (result);
}

//-------------------------------------------------------
// Create a new VComponent surrogate for given subject.
// If subject is NULL, then returns NULL (will not create a
// surrogate to a NULL subject);
//-------------------------------------------------------
jobject createNewVComponentSurrogate(JNIEnv *env, VComponent *subject)
{
    auto classname = JLIBICAL_CLASS_VCOMPONENT;
    if (dynamic_cast<VAlarm *>(subject) != nullptr) {
        classname = JLIBICAL_CLASS_VALARM;
    } else if (dynamic_cast<VCalendar *>(subject) != nullptr) {
        classname = JLIBICAL_CLASS_VCALENDAR;
    } else if (dynamic_cast<VEvent *>(subject) != nullptr) {
        classname = JLIBICAL_CLASS_VEVENT;
    } else if (dynamic_cast<VQuery *>(subject) != nullptr) {
        classname = JLIBICAL_CLASS_VQUERY;
    } else if (dynamic_cast<VToDo *>(subject) != nullptr) {
        classname = JLIBICAL_CLASS_VTODO;
    } else if (dynamic_cast<VAgenda *>(subject) != nullptr) {
        classname = JLIBICAL_CLASS_VAGENDA;
    }

    return (doCreateNewSurrogate(env, env->FindClass(classname), (jlong)subject));
}

//-------------------------------------------------------
// Create a new ICalProperty surrogate for given subject.
// If subject is NULL, then returns NULL (will not create a
// surrogate to a NULL subject);
//-------------------------------------------------------
jobject createNewICalPropertySurrogate(JNIEnv *env, ICalProperty *subject)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALPROPERTY), (jlong)subject));
}

//-------------------------------------------------------
// Create a new ICalValue surrogate for given subject.
// If subject is NULL, then returns NULL (will not create a
// surrogate to a NULL subject);
//-------------------------------------------------------
jobject createNewICalValueSurrogate(JNIEnv *env, ICalValue *subject)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALVALUE), (jlong)subject));
}

//-------------------------------------------------------
// Create a new ICalParameter surrogate for given subject.
// If subject is NULL, then returns NULL (will not create a
// surrogate to a NULL subject);
//-------------------------------------------------------
jobject createNewICalParameterSurrogate(JNIEnv *env, ICalParameter *subject)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALPARAMETER), (jlong)subject));
}

//-------------------------------------------------------
// Create a new ICalTimeType object from the given source struct.
// A copy is made,  .
// If source is NULL, then returns NULL (will not create an
// object to a NULL source);
//-------------------------------------------------------
jobject createNewICalTimeType(JNIEnv *env, icaltimetype *source)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALTIMETYPE), (jlong)source));
}

//-------------------------------------------------------
// Create a new ICalTriggerType object from the given source struct.
// A copy is made,  .
// If source is NULL, then returns NULL (will not create an
// object to a NULL source);
//-------------------------------------------------------
jobject createNewICalTriggerType(JNIEnv *env, icaltriggertype *source)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALTRIGGERTYPE), (jlong)source));
}

//-------------------------------------------------------
// Create a new ICalDurationType object from the given source struct.
// A copy is made,  .
// If source is NULL, then returns NULL (will not create an
// object to a NULL source);
//-------------------------------------------------------
jobject createNewICalDurationType(JNIEnv *env, icaldurationtype *source)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALDURATIONTYPE), (jlong)source));
}

//-------------------------------------------------------
// Create a new ICalRecurrenceType object from the given source struct.
// A copy is made,  .
// If source is NULL, then returns NULL (will not create an
// object to a NULL source);
//-------------------------------------------------------
jobject createNewICalRecurrenceType(JNIEnv *env, icalrecurrencetype *source)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALRECURRENCETYPE), (jlong)source));
}

//-------------------------------------------------------
// Create a new ICalPeriodType object from the given source struct.
// A copy is made,  .
// If source is NULL, then returns NULL (will not create an
// object to a NULL source);
//-------------------------------------------------------
jobject createNewICalPeriodType(JNIEnv *env, icalperiodtype *source)
{
    return (doCreateNewSurrogate(env, env->FindClass(JLIBICAL_CLASS_ICALPERIODTYPE), (jlong)source));
}

//-------------------------------------------------------
// Create a new surrogate of the given type for the given subject.
//-------------------------------------------------------
jobject doCreateNewSurrogate(JNIEnv *env, jclass surrogateClass, jlong subject)
{
    jobject result = NULL;

    if (subject != 0) {
        jmethodID jconstructorID = env->GetMethodID(surrogateClass, "<init>", "(J)V");

        result = env->NewObject(surrogateClass, jconstructorID, subject);
    }

    return (result);
}

//-------------------------------------------------------
// For the given cpErr, create a new Exception and send it to env.
// Note: Throw does not throw anything.  It only sets the state.
//              The JVM will check this and throw an exception later.
//-------------------------------------------------------
void throwException(JNIEnv *env, int cpErr)
{
    const char *exClassName;

    if ((bool)env->ExceptionOccurred()) {
        return;
    }

    switch (cpErr) {
    case JLIBICAL_ERR_NETWORK:
        exClassName = "net.cp.jlibical/JLCNetworkException";
        break;

    case JLIBICAL_ERR_SERVER_INTERNAL:
        exClassName = "net.cp.jlibical/JLCServerInternalException";
        break;

    case JLIBICAL_ERR_CLIENT_INTERNAL:
        exClassName = "net.cp.jlibical/JLCClientInternalException";
        break;

    case JLIBICAL_ERR_ILLEGAL_ARGUMENT:
        exClassName = "net.cp.jlibical/JLCIllegalArgumentException";
        break;

    case JLIBICAL_ERR_API_NOT_INITED:
        exClassName = "net.cp.jlibical/JLCNotInitedException";
        break;

    case JLIBICAL_ERR_HOST_INVALID:
        exClassName = "net.cp.jlibical/JLCHostInvalidException";
        break;

    default:
        exClassName = "net.cp.jlibical/JLCClientInternalException";
        printf("*** JLIBICAL JNI throwException: unknown error code: %d\n", cpErr);
        break;
    }

    env->ThrowNew(env->FindClass(exClassName), "");
}
