/**
 * @copyright
 * ====================================================================
 * Copyright (c) 2003-2004 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 * @endcopyright
 *
 * @file CommitMessage.cpp
 * @brief Implementation of the class CommitMessage
 */

#include "CommitMessage.h"
#include "JNIUtil.h"
#include <apr_tables.h>
#include "svn_client.h"
#include "../include/org_tigris_subversion_javahl_CommitItemStateFlags.h"

CommitMessage::CommitMessage(jobject jcommitMessage)
{
  m_jcommitMessage = jcommitMessage;
}

CommitMessage::~CommitMessage()
{
  // Since the m_jcommitMessage is a global reference, it has to be
  // deleted to allow the Java garbage collector to reclaim the
  // object.
  if (m_jcommitMessage!= NULL)
    {
      JNIEnv *env = JNIUtil::getEnv();
      env->DeleteGlobalRef(m_jcommitMessage);
    }
}

CommitMessage *CommitMessage::makeCCommitMessage(jobject jcommitMessage)
{
  // If there is no object passed into this method, there is no need
  // for a C++ holding object.
  if (jcommitMessage == NULL)
    return NULL;

  // Sanity check, that the passed Java object implements the right
  // interface.
  JNIEnv *env = JNIUtil::getEnv();
  jclass clazz = env->FindClass(JAVA_PACKAGE"/CommitMessage");
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  if (!env->IsInstanceOf(jcommitMessage, clazz))
    {
      env->DeleteLocalRef(clazz);
      return NULL;
    }
  env->DeleteLocalRef(clazz);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  // Since the reference is longer needed then the duration of the
  // SVNClient.commtMessage, the local reference has to be converted
  // to a global reference.
  jobject myCommitMessage = env->NewGlobalRef(jcommitMessage);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  // create & return the holding object
  return new CommitMessage(myCommitMessage);
}

/**
 * Call the Java callback method to retrieve the commit message
 * @param commit_items  the array of the items of this commit
 * @returns the commit message
 */
jstring
CommitMessage::getCommitMessage(const apr_array_header_t *commit_items)
{
  JNIEnv *env = JNIUtil::getEnv();
  // create an Java array for the commit items
  jclass clazz = env->FindClass(JAVA_PACKAGE"/CommitItem");
  if (JNIUtil::isExceptionThrown())
    return NULL;

  int count = commit_items->nelts;
  jobjectArray jitems = env->NewObjectArray(count, clazz, NULL);
  if (JNIUtil::isExceptionThrown())
    return NULL;

  // Java method ids will not change during the time this library is
  // loaded, so they can be cached.

  // Get the method id for the CommitItem constructor.
  static jmethodID midConstructor = 0;
  if (midConstructor == 0)
    {
      midConstructor = env->GetMethodID(clazz, "<init>",
                                        "(Ljava/lang/String;"
                                        "IILjava/lang/String;"
                                        "Ljava/lang/String;J)V");
      if (JNIUtil::isExceptionThrown())
        return NULL;
    }

  // get the method if for the CommitMessage callback method
  static jmethodID midCallback = 0;
  if (midCallback == 0)
    {
      jclass clazz2 = env->FindClass(JAVA_PACKAGE"/CommitMessage");
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;

      midCallback = env->GetMethodID(clazz2, "getLogMessage",
                                     "([L"JAVA_PACKAGE"/CommitItem;)"
                                     "Ljava/lang/String;");
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;

      env->DeleteLocalRef(clazz2);
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;
    }

  // create a Java CommitItem for each of the passed in commit items
  for (int i = 0; i < count; ++i)
    {
      svn_client_commit_item3_t *item =
        APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);

      // convert the commit item members to the match Java members
      jstring jpath = JNIUtil::makeJString(item->path);

      jint jnodeKind = item->kind;

      jint jstateFlags = 0;
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)
        jstateFlags |=
          org_tigris_subversion_javahl_CommitItemStateFlags_Add;
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
        jstateFlags |=
          org_tigris_subversion_javahl_CommitItemStateFlags_Delete;
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS)
        jstateFlags |=
          org_tigris_subversion_javahl_CommitItemStateFlags_TextMods;
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS)
        jstateFlags |=
          org_tigris_subversion_javahl_CommitItemStateFlags_PropMods;
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY)
        jstateFlags |=
          org_tigris_subversion_javahl_CommitItemStateFlags_IsCopy;

      jstring jurl = JNIUtil::makeJString(item->url);

      jstring jcopyUrl = JNIUtil::makeJString(item->copyfrom_url);

      jlong jcopyRevision = item->revision;

      // create the Java object
      jobject jitem = env->NewObject(clazz, midConstructor, jpath,
                                     jnodeKind, jstateFlags, jurl,
                                     jcopyUrl, jcopyRevision);
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;

      // release the tempory Java objects
      env->DeleteLocalRef(jpath);
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;


      env->DeleteLocalRef(jurl);
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;

      env->DeleteLocalRef(jcopyUrl);
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;

      // store the Java object into the array
      env->SetObjectArrayElement(jitems, i, jitem);
      if (JNIUtil::isJavaExceptionThrown())
        return NULL;
    }
  env->DeleteLocalRef(clazz);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  // call the Java callback method
  jstring jmessage = (jstring)env->CallObjectMethod(m_jcommitMessage,
                                                    midCallback,
                                                    jitems);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  // release the Java object array
  env->DeleteLocalRef(jitems);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  return jmessage;
}
