/*****************************************************************************
 * JVLC.java: JNI interface for vlc Java Bindings
 *****************************************************************************
 * Copyright (C) 1998-2005 the VideoLAN team
 *
 * Authors: Filippo Carone <filippo@carone.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/* These are a must*/
#include <jni.h>
#include <vlc/vlc.h>

#include <stdio.h>  // for printf
#include <stdlib.h> // for calloc
#include <string.h> // for strcmp


/* JVLC internal imports, generated by gcjh */
#include "org_videolan_jvlc_JVLC.h"
#include "org_videolan_jvlc_JVLCVariable.h"
#include "org_videolan_jvlc_JVLCIntVariable.h"
#include "org_videolan_jvlc_JVLCBoolVariable.h"
#include "org_videolan_jvlc_JVLCFloatVariable.h"
#include "org_videolan_jvlc_JVLCStringVariable.h"
#include "org_videolan_jvlc_JVLCTimeVariable.h"
#include "org_videolan_jvlc_JVLCVarVariable.h"
#include "org_videolan_jvlc_JVLCVarValue.h"

int getClassID (JNIEnv *env, jobject obj);

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_create (JNIEnv *env, jobject obj) {
  return VLC_Create();
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_init (JNIEnv *env, jobject obj, jobjectArray args) {

  /* build the argv vector */
  const char **argv;

  /* count the arguments */
  //  int argc = (int) (*env)->GetArrayLength(env, args)
  int argc = (int) env->GetArrayLength((jarray) args);

  /* convert java strings to c strings
     how many pointers do we need? */
  argv = (const char **) malloc(argc * sizeof(char*));

  /* now fill the argv* */
  for (int i = 0; i < argc; i++) {
    argv[i] = env->GetStringUTFChars((jstring) env->GetObjectArrayElement(args, i),
				     0
				    );
  }
 
  /* execute the library call */ 
  int res = VLC_Init(getClassID(env,obj), argc, (char **) argv);
  
  free(argv);

  return res;
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_addInterface (JNIEnv *env, jobject obj, jstring moduleName, jboolean blocking, jboolean startPlay) {
  const char *psz_module = NULL;
  int i_blocking, i_startPlay;

  if (moduleName != NULL) {
    psz_module = env->GetStringUTFChars(moduleName, 0);
  }

  if (blocking == true)  i_blocking  = 1;
  else                   i_blocking  = 0;

  if (startPlay == true) i_startPlay = 1;
  else                   i_startPlay = 0;

  int addIntf_res = VLC_AddIntf(getClassID(env,obj), psz_module, i_blocking, i_startPlay);
  
  if (psz_module != NULL) {
    env->ReleaseStringUTFChars(moduleName, psz_module);
  }
  return addIntf_res;
}

JNIEXPORT jstring JNICALL Java_org_videolan_jvlc_JVLC_getVersion (JNIEnv *env, jobject obj) {
  return env->NewStringUTF(VLC_Version());
}

JNIEXPORT jstring JNICALL Java_org_videolan_jvlc_JVLC_getError (JNIEnv *env, jobject obj, jint errorCode) {
  return env->NewStringUTF(VLC_Error(errorCode));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_die (JNIEnv *env, jobject obj) {
  return VLC_Die(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_cleanUp (JNIEnv *env, jobject obj) {
  return VLC_CleanUp(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_setVariable (JNIEnv *env, jobject obj, jobject jvlcVariable) {
  vlc_value_t value;
  char*       psz_var = NULL;
  jstring     varName;
  jstring     stringValue = NULL;

  if (jvlcVariable != NULL) {
    /* get the name of jvlcVariable class */
    jclass    variableClass = env->GetObjectClass(jvlcVariable);
    jclass    varSuperClass = env->GetSuperclass (variableClass);
    jmethodID getNameMethod = env->GetMethodID   (varSuperClass, "getName", "()Ljava/lang/String;");
    jstring   varName       = (jstring) env->CallObjectMethod (jvlcVariable, getNameMethod);

    /* i_int */
    jclass tmpClass = env->FindClass("org/videolan/jvlc/JVLCIntVariable");
    if (env->IsInstanceOf(jvlcVariable, tmpClass)) {
      jmethodID mid = env->GetMethodID(variableClass, "getIntValue", "()I");
      value.i_int   = env->CallIntMethod(jvlcVariable, mid);
      goto done;
    }

    /* b_bool */
    tmpClass = env->FindClass("org/videolan/jvlc/JVLCBoolVariable");
    if (env->IsInstanceOf(jvlcVariable, tmpClass )) {
      jmethodID mid = env->GetMethodID(variableClass, "getBoolValue", "()Z");
      value.b_bool  = env->CallBooleanMethod(jvlcVariable, mid);
      goto done;
    }

    /* f_float */
    tmpClass = env->FindClass("org/videolan/jvlc/JVLCFloatVariable");
    if (env->IsInstanceOf(jvlcVariable, tmpClass )) {
      jmethodID mid = env->GetMethodID(variableClass, "getFloatValue", "()F");
      value.f_float = env->CallBooleanMethod(jvlcVariable, mid);
      goto done;
    }

    /* psz_string */
    tmpClass = env->FindClass("org/videolan/jvlc/JVLCStringVariable");
    if (env->IsInstanceOf(jvlcVariable, tmpClass )) {
      jmethodID mid       = env->GetMethodID(variableClass, "getStringValue", "()Ljava/lang/String");
      jstring stringValue = (jstring) env->CallObjectMethod(jvlcVariable, mid);
      value.psz_string    = (char *)env->GetStringUTFChars(stringValue, 0);
      goto done;
    }

    /* i_time */
    tmpClass = env->FindClass("org/videolan/jvlc/JVLCTimeVariable");
    if (env->IsInstanceOf(jvlcVariable, tmpClass )) {
      jmethodID mid  = env->GetMethodID(variableClass, "getTimeValue", "()L");
      value.i_time   = env->CallLongMethod(jvlcVariable, mid);
      goto done;
    }

    /* var */
    tmpClass = env->FindClass("org/videolan/jvlc/JVLCVarVariable");
    if (env->IsInstanceOf(jvlcVariable, tmpClass )) {
      /*
       * This is the longer one. We have to get the VarValue object 
       * from the JVLCVarVariable and retrieve the OID from it. 
       */
      jmethodID getVarValueMID  = env->GetMethodID(variableClass, "getVarValue", "()Lorg/videolan/jvlc/JVLCVarValue;");
      jobject varVariableObj = env->CallObjectMethod(jvlcVariable, getVarValueMID);
      
      /*
       * Now from the varVariableObj we need to retrieve its name and OID
       */

      /*
       * Get the VarValue name
       */

      jclass varVariableCls = env->GetObjectClass(varVariableObj);
      jmethodID getVarName  = env->GetMethodID(varVariableCls, "getName","()Ljava/lang/String;");
      jstring varName       = (jstring) env->CallObjectMethod(varVariableObj, getVarName);
      value.var.psz_name    = (char *)env->GetStringUTFChars(varName, 0);

      /*
       * Get the VarValue OID
       */

      jmethodID getOID = env->GetMethodID(varVariableCls, "getOID", "()I");
      value.var.i_object_id = env->CallIntMethod(varVariableObj, getOID);

      goto done;
     }

  done:
    psz_var = (char *) env->GetStringUTFChars(varName, 0);
  }
    
  if (psz_var != NULL) {
    int res = VLC_VariableSet(getClassID(env, obj), (char* const) psz_var, value);
    env->ReleaseStringUTFChars(varName, psz_var);
    if (stringValue != NULL) env->ReleaseStringUTFChars(stringValue, value.psz_string);
    return res;
  }
  return -1;
}


JNIEXPORT jobject JNICALL Java_org_videolan_jvlc_JVLC_getVariable (JNIEnv *env, jobject obj, jstring varName) {

  const char* psz_var = env->GetStringUTFChars(varName, 0);
  vlc_value_t value;
  if (VLC_VariableGet(getClassID(env, obj), psz_var, &value) != VLC_SUCCESS) {
    // throw exception
    return NULL;
  }



  return NULL;

}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_addTarget (JNIEnv *env, jobject obj, jstring URI, jobjectArray options, jint insertMode, jint position) {
  
  char*  psz_target   = NULL;
  char** ppsz_options = NULL;
  int options_number = 0;
  
  if (URI != NULL) {
    psz_target = (char *) env->GetStringUTFChars(URI, 0);
  }

  if (options != NULL) {
    options_number = env->GetArrayLength(options);
    ppsz_options = (char **) malloc (options_number * sizeof(char*));

    for (int i = 0; i < options_number; i++) {
      ppsz_options[i] = (char *) env->GetStringUTFChars((jstring) env->GetObjectArrayElement(options, i), 0);
    }
  }
  
  int res = VLC_AddTarget(getClassID(env, obj), (char const *)psz_target, (const char **) ppsz_options, options_number, insertMode, position);

  if (ppsz_options != NULL) {
    for (int i = 0; i < options_number; i++) {
      free(ppsz_options[i]);
    }
    free(ppsz_options);
  }
  if (psz_target != NULL) {
    free(psz_target);
  }

}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_play (JNIEnv *env, jobject obj) {
  return VLC_Play(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_pause (JNIEnv *env, jobject obj) {
  return VLC_Pause(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_stop (JNIEnv *env, jobject obj) {
  return VLC_Stop(getClassID(env, obj));
}

JNIEXPORT jboolean JNICALL Java_org_videolan_jvlc_JVLC_isPlaying (JNIEnv *env, jobject obj) {
  return VLC_IsPlaying(getClassID(env, obj));
}

JNIEXPORT jfloat JNICALL Java_org_videolan_jvlc_JVLC_getPosition (JNIEnv *env, jobject obj) {
  return VLC_PositionGet(getClassID(env, obj));
}

JNIEXPORT jfloat JNICALL Java_org_videolan_jvlc_JVLC_setPosition (JNIEnv *env, jobject obj, jfloat position) {
  return VLC_PositionSet(getClassID(env, obj), position);
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_getTime (JNIEnv *env, jobject obj) {
  return VLC_TimeGet(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_setTime (JNIEnv *env, jobject obj, jint seconds, jboolean relative) {
  return VLC_TimeSet(getClassID(env, obj), seconds, relative);
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_getLength (JNIEnv *env, jobject obj) {
  return VLC_LengthGet(getClassID(env, obj));
}

JNIEXPORT jfloat JNICALL Java_org_videolan_jvlc_JVLC_speedFaster (JNIEnv *env, jobject obj) {
  return VLC_SpeedFaster(getClassID(env, obj));
}

JNIEXPORT jfloat JNICALL Java_org_videolan_jvlc_JVLC_speedSlower (JNIEnv *env, jobject obj) {
  return VLC_SpeedSlower(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_getPlaylistIndex (JNIEnv *env, jobject obj) {
  return VLC_PlaylistIndex(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_getPlaylistItems (JNIEnv *env, jobject obj) {
  return VLC_PlaylistNumberOfItems(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_playlistNext (JNIEnv *env, jobject obj) {
  return VLC_PlaylistNext(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_playlistPrev (JNIEnv *env, jobject obj) {
  return VLC_PlaylistPrev(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_playlistClear (JNIEnv *env, jobject obj) {
  return VLC_PlaylistClear(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_getVolume (JNIEnv *env, jobject obj) {
  return VLC_VolumeGet(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_setVolume (JNIEnv *env, jobject obj, jint volume) {
  return VLC_VolumeSet(getClassID(env, obj), volume);
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_muteVolume (JNIEnv *env, jobject obj) {
  return VLC_VolumeMute(getClassID(env, obj));
}

JNIEXPORT jint JNICALL Java_org_videolan_jvlc_JVLC_fullScreen (JNIEnv *env, jobject obj) {
  return VLC_FullScreen(getClassID(env, obj));
}

/*
 * This method is an helper method 
 * only valid if obj == JVLC
 */

int getClassID (JNIEnv *env, jobject obj) {
  /* get the id field of object */
  jclass    cls   = env->GetObjectClass(obj);
  jmethodID mid   = env->GetMethodID(cls, "getID", "()I");
  int       field = env->CallIntMethod(obj, mid);
  return field;
}

