/*
#  $Id$
# -----------------------------------------------------------------------------
#  The part of 'MediaPlayer' project
# -----------------------------------------------------------------------------
#  Author: Petrov Maxim, 25/10/2012
#  Edited by:
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
*/
#include <assert.h>
#include <string.h>

#include "VideoFrameRateController.h"
#include "JMediaPlayer.h"
#include "JsonObjectsTracker.h"
#include "GraphicsProxy.h"

JavaVM *jvm = 0;

/*inline*/ void tokenize(const std::string& str, const std::string& delimiters, std::vector<std::string> *tokens)
{
    // skip delimiters at beginning.
    std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    
    // find first "non-delimiter".
    std::string::size_type pos = str.find_first_of(delimiters, lastPos);
    
    while (std::string::npos != pos || std::string::npos != lastPos)
    {
        // found a token, add it to the vector.
        tokens->push_back(str.substr(lastPos, pos - lastPos));
        
        // skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        
        // find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

JMediaPlayer::JMediaPlayer(const std::string &url, int streamOverTCP, JNIEnv *jenv, jobject jobj, const std::map<std::string, jmethodID> &jms)
    : ctx_(0), jobj_(jobj), jms_(jms), url_(url), jRGB24bytes_(0), jPCMbytes_(0), jsOT_(new JsonObjectsTracker), dar(0.0f)
{
     int cacheSize = 0;
     int jitterBuffer = 0;
     int useHWDecoder = 0;

     jfieldID jfid = jenv->GetFieldID(jenv->GetObjectClass(jobj_), "cacheSize", "I");    
     if (jfid != NULL)
         cacheSize = jenv->GetIntField(jobj_, jfid);

     jfid = jenv->GetFieldID(jenv->GetObjectClass(jobj_), "jitterBuffer", "I");
     if (jfid != NULL)
     {
         jitterBuffer = jenv->GetIntField(jobj_, jfid);
     }

     jfid = jenv->GetFieldID(jenv->GetObjectClass(jobj_), "useHWDecoder", "I");
     if (jfid != NULL)
     {
         useHWDecoder = jenv->GetIntField(jobj_, jfid);
     }


     memset(&conf_, 0, sizeof(conf_));
     conf_.cache_size = cacheSize / 1000000;
     conf_.decoder_type = useHWDecoder ? DECODER_HW : DECODER_SW_MT;
     conf_.pixel_format = PIX_RGB24;
     conf_.rtp_transport = streamOverTCP ? RTP_TRANSPORT_TCP : RTP_TRANSPORT_UDP;
     conf_.client_data = this;
     conf_.buffer_len = jitterBuffer;

     fprintf(stderr, "Cache size: %dM, jitter buffer length: %dms\n", conf_.cache_size, conf_.buffer_len);


     struct vn_player_callbacks_t callbacks = {};
     callbacks.buffer_changed      = buffer_changed;
     callbacks.new_frame           = new_frame;
     callbacks.new_stream          = new_stream;
     callbacks.state_changed       = state_changed;
     callbacks.stream_removed      = stream_removed;
     callbacks.create_image_buffer = create_image_buffer;
     callbacks.lock_image_buffer   = lock_image_buffer;
     callbacks.unlock_image_buffer = unlock_image_buffer;
     callbacks.msg_log             = msg_log;

     ctx_ = vn_player_create(&conf_);
     vn_player_set_callbacks(ctx_, &callbacks);

     if (!jvm)
         jenv->GetJavaVM(&jvm);
}
    
/*virtual*/ JMediaPlayer::~JMediaPlayer()
{
    if (ctx_)
        vn_player_destroy(ctx_);

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);
    if (jRGB24bytes_)
    {
        jenv->ReleaseByteArrayElements(jRGB24picture_, jRGB24bytes_, JNI_ABORT);   
        jenv->DeleteLocalRef(jRGB24picture_);
    }

    if (jPCMbytes_)
    {
        jenv->ReleaseByteArrayElements(jPCMframe_, jPCMbytes_, JNI_ABORT);
        jenv->DeleteLocalRef(jPCMframe_); 
    }

    jenv->DeleteGlobalRef(jobj_);

    delete jsOT_;
}

void JMediaPlayer::open()
{
    vn_player_open(ctx_, url_.c_str());
}

void JMediaPlayer::play()
{
    vn_player_play(ctx_);
}

void JMediaPlayer::pause()
{
    vn_player_pause(ctx_);
}

void JMediaPlayer::resume(int direction)
{
    vn_player_resume(ctx_, direction);
}

void JMediaPlayer::jump(time_t value)
{
    vn_player_move_to_ts(ctx_, value);
}
    
void JMediaPlayer::setSpeed(float value)
{
    vn_player_set_playback_speed(ctx_, value);
}

void JMediaPlayer::setJitterBufferLen(unsigned value)
{
    vn_player_set_jitter_buffer_len(ctx_, value);
}

void JMediaPlayer::setStepMode(int value)
{
    vn_player_set_step_mode(ctx_, value);
}

void JMediaPlayer::setAnalyticsKeysVisualisation(const std::string &value)
{
    MutexGuard mg(analyticsKeysVisualisationMutex_);

    std::vector<std::string> keyList;

    tokenize(value, ",", &keyList);

    analyticsKeysVisualisation_.clear();

    std::vector<std::string> v;
    v.push_back("visible");

    for (size_t i = 0; i < keyList.size(); i++)
    {
        analyticsKeysVisualisation_.insert(std::make_pair(keyList[i], v));
    }
}

/*static*/ void* JMediaPlayer::create_image_buffer(int width, int height, float dar, VN_PLAYER_FRAME_BUFFERS_NUM& buf_num, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    if (jmp->jRGB24bytes_)
    {
        jenv->ReleaseByteArrayElements(jmp->jRGB24picture_, jmp->jRGB24bytes_, JNI_ABORT);   
        jenv->DeleteLocalRef(jmp->jRGB24picture_);
    }

//    float aspect_ratio = dar ? dar : 1.0f;
    float aspect_ratio = 1.0f;
    jmp->jRGB24picture_ = (jbyteArray)jenv->CallObjectMethod(jmp->jobj_, jmp->jms_["createRGB24Image"], width, height, aspect_ratio);
    if (!jmp->jRGB24picture_) //TODO: reporting of JVM memory lack
    {
        return 0;
    }

    jboolean iscopy = JNI_FALSE; 
    jmp->jRGB24bytes_ = jenv->GetByteArrayElements(jmp->jRGB24picture_, &iscopy);


    fprintf(stderr, "jni image buffer created: %dx%d: %p\n", width, height,  jmp->jRGB24bytes_);

    buf_num = SINGLE;

    return jmp->jRGB24bytes_;
}

/*static*/ int JMediaPlayer::lock_image_buffer(int, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["lockImageBuffer"]);

    return 0;
}

/*static*/ void JMediaPlayer::unlock_image_buffer(int, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["unlockImageBuffer"]);
}


/*static*/ void JMediaPlayer::new_frame(const vn_player_frame_t *frame, const vn_player_cache_boundaries_t *bounds, int, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    if (frame->stream_info->type == VIDEO && frame->data[0])
    {    
        if (jmp->jRGB24bytes_ /*&& jenv->CallIntMethod(jmp->jobj_, jmp->jms_["isDisplayReady"])*/
            && jenv->CallIntMethod(jmp->jobj_, jmp->jms_["canDisplayFrame"], frame->captured_time->tv_sec, frame->captured_time->tv_usec))
        {
            // Copy frame data and commit to java
//            memcpy(jmp->jRGB24bytes_, frame->data[0], frame->width * frame->height * 3);
            jenv->ReleaseByteArrayElements(jmp->jRGB24picture_, jmp->jRGB24bytes_, JNI_COMMIT);

            int bufferUsage = 1;
            jbyteArray barr = NULL;

            jmp->jsOT_->setFrameTime(frame->captured_time);
            if (frame->objects_data_size > 2)
            {
                MutexGuard mg(jmp->analyticsKeysVisualisationMutex_);
                
                // json data
                jmp->jsOT_->setMetadataInfo(jmp->analyticsKeysVisualisation_);
                jmp->jsOT_->setObjectsData((const char*)frame->objects_data);
            }

            GraphicsProxy grp;
            grp.setStrokeWidth( 1 );
            grp.setColor( Color( 0, 255, 0 ) );
            grp.setFont( "Verdana", 14, Graphics::Regular );
            jmp->jsOT_->parseAndDraw( &grp, Rectangle2( 0, 0, frame->width, frame->height ), Rectangle2( 0, 0, frame->width, frame->height ), 1.0 );
            grp.finalizeSequence();
        
            barr = jenv->NewByteArray(grp.binaryData.size());
            
            jenv->SetByteArrayRegion(barr, 0, grp.binaryData.size(), (jbyte*)grp.binaryData.data());

            jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["displayFrame"],  
                                 
                             // current frame's time
                             frame->captured_time->tv_sec,
                             frame->captured_time->tv_usec,
                         
                             // time of first frame in the buffer
                             bounds->begin.tv_sec,
                             bounds->begin.tv_usec,

                             // time of last frame in the buffer
                             bounds->end.tv_sec,
                             bounds->end.tv_usec,

                             // buffer usage 
                             bufferUsage,

                             // objects data
                             barr
            );

            jenv->DeleteLocalRef(barr);
        }
    }
    else if (frame->stream_info->type == AUDIO)
    {
        if (!jmp->jPCMbytes_)
        {
            jboolean isCopy = JNI_FALSE; 
            jmp->jPCMframe_ = jenv->NewByteArray(256000);        
            jmp->jPCMbytes_ = jenv->GetByteArrayElements(jmp->jPCMframe_, &isCopy);

            // Configuring audio device
            jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["configureAudioDevice"], frame->stream_info->sampling_freq, frame->stream_info->bits_per_sample, frame->stream_info->num_channels);
        }

        // Copy frame data and commit to java
        memcpy(jmp->jPCMbytes_, frame->data[0], frame->data_size[0]);
        jenv->ReleaseByteArrayElements(jmp->jPCMframe_, jmp->jPCMbytes_, JNI_COMMIT);
    
        int bufferUsage = 1;
        jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["playPCMFrame"], jmp->jPCMframe_, frame->data_size[0], 
                            
                             // current frame's time
                             frame->captured_time->tv_sec,
                             frame->captured_time->tv_usec,
                         
                             // time of first frame in the buffer
                             bounds->begin.tv_sec,
                             bounds->begin.tv_usec,

                             // time of last frame in the buffer
                             bounds->end.tv_sec,
                             bounds->end.tv_usec,
                            
                             // buffer usage 
                             bufferUsage);

    }

//    jvm->DetachCurrentThread();     
}
    
/*static*/ void JMediaPlayer::buffer_changed(const vn_player_cache_boundaries_t *bounds, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["bufferChanged"], 
                         bounds->begin.tv_sec, bounds->begin.tv_usec,
                         bounds->end.tv_sec, bounds->end.tv_usec);
     
//    jvm->DetachCurrentThread();        
}
	
/*static*/ void JMediaPlayer::state_changed(VN_PLAYER_STREAM_STATE state, const vn_player_result_status_t *status, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["setResultCode"], status->error_code);

    if (status->error_str)
    {
        jstring jstr = jenv->NewStringUTF(status->error_str);
        if (jstr != NULL)
        {
            jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["setResultMsg"], jstr);  
            jenv->DeleteLocalRef(jstr);
        }
    }

    jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["changeStatus"], state);


//    jvm->DetachCurrentThread();
}
	
/*static*/ void JMediaPlayer::new_stream(const vn_player_stream_info_t *stream, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);

    if (stream->type == VIDEO && stream->RTP_Codec_Str)
    {
        jstring jstr = jenv->NewStringUTF(stream->RTP_Codec_Str);
        if (jstr != NULL)
        {
           jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["setStreamType"], jstr);  
           jenv->DeleteLocalRef(jstr);
        }
    }

//    jvm->DetachCurrentThread();
}
	
/*static*/ void JMediaPlayer::stream_removed(const vn_player_stream_info_t*, void *client_data)
{
}
	
/*static*/ void JMediaPlayer::msg_log(const char *msg, void *client_data)
{
    JMediaPlayer *jmp = (JMediaPlayer*)client_data;

    JNIEnv *jenv;
    jvm->AttachCurrentThread((void**)&jenv, NULL);
    jstring jstr = jenv->NewStringUTF(msg);
    if (jstr != NULL)
    {
        jenv->CallVoidMethod(jmp->jobj_, jmp->jms_["log"], jstr);  
        jenv->DeleteLocalRef(jstr);
    }
//    jvm->DetachCurrentThread();
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_stop (JNIEnv *jenv, jobject, jlong mpID)
{
   assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;

   delete jmp;
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_pause (JNIEnv *jenv, jobject, jlong mpID)
{
   assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;

   jmp->pause();
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_resume (JNIEnv *jenv, jobject, jlong mpID, jint direction)
{
   assert(mpID);

   fprintf(stderr, "---Java_com_videonext_mplayer_MediaPlayer_resume\n");

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;

   jmp->resume(direction);
}

JNIEXPORT void /*JNICALL*/ Java_com_videonext_mplayer_internal_MediaPlayer_play (JNIEnv *jenv, jobject jobj, jlong mpID)
{
    assert(mpID);
    
    JMediaPlayer *jmp = (JMediaPlayer*)mpID;
    
    jmp->play();
}

JNIEXPORT void /*JNICALL*/ Java_com_videonext_mplayer_internal_MediaPlayer_open (JNIEnv *jenv, jobject jobj, jstring url,
                                                                                 jint streamOverTCP, jint streamOverHTTP, jint mediaType, jint maxHWFrameRate)
{    
    jclass jcls;
    jfieldID jfid = NULL;
    jmethodID jmid;
    std::map<std::string, jmethodID> jmethods;
    
    
    jcls = jenv->GetObjectClass(jobj);
    
    
    jmid = jenv->GetMethodID(jcls, "changeStatus", "(I)V");
    
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("changeStatus", jmid));
    
    
    jmid = jenv->GetMethodID(jcls, "createRGB24Image", "(IIF)[B");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("createRGB24Image", jmid));

    jmid = jenv->GetMethodID(jcls, "lockImageBuffer", "()V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("lockImageBuffer", jmid));

    jmid = jenv->GetMethodID(jcls, "unlockImageBuffer", "()V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("unlockImageBuffer", jmid));

    
    jmid = jenv->GetMethodID(jcls, "displayFrame", "(IIIIIII[B)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown;
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("displayFrame", jmid));
    

    jmid = jenv->GetMethodID(jcls, "playPCMFrame", "([BIIIIIIII)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("playPCMFrame", jmid));
    
    
    jmid = jenv->GetMethodID(jcls, "configureAudioDevice", "(III)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("configureAudioDevice", jmid));
    
    
    jmid = jenv->GetMethodID(jcls, "log", "(Ljava/lang/String;)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("log", jmid));
    
    
    jmid = jenv->GetMethodID(jcls, "isDisplayReady", "()I");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("isDisplayReady", jmid));


    jmid = jenv->GetMethodID(jcls, "canDisplayFrame", "(JJ)I");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("canDisplayFrame", jmid));

    
    
    jmid = jenv->GetMethodID(jcls, "setResultMsg", "(Ljava/lang/String;)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("setResultMsg", jmid));
    
    
    jmid = jenv->GetMethodID(jcls, "setResultCode", "(I)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("setResultCode", jmid));
    
    
    jmid = jenv->GetMethodID(jcls, "bufferChanged", "(IIII)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("bufferChanged", jmid));


    jmid = jenv->GetMethodID(jcls, "setStreamType", "(Ljava/lang/String;)V");
    if (jmid == NULL)
    {
        // NoSuchMethodError already thrown
        return;
    }
    jmethods.insert(std::pair<std::string, jmethodID>("setStreamType", jmid));


    
    if (mediaType == 0 /*video*/)
    {

        jfid = jenv->GetFieldID(jcls, "mpVideoID", "J");
        if (jfid == NULL)
        {
            return;
        }
    }
    else if (mediaType == 1 /*audio*/)
    {
        jfid = jenv->GetFieldID(jcls, "mpAudioID", "J");
        if (jfid == NULL)
        {
            return;
        }
    }
    
   
    const char *url_buf;
    url_buf = jenv->GetStringUTFChars(url, NULL);
    if (url_buf == NULL) 
    {
        return; /* OutOfMemoryError already thrown */
    }
  
    JMediaPlayer *jmp = new JMediaPlayer(url_buf, streamOverTCP, jenv, jenv->NewGlobalRef(jobj), jmethods);
     
    if (jmp == NULL)
    {
        return;
    }
    
    VideoFrameRateController::instance()->setMaxFrameRate((float)maxHWFrameRate);
    
    jenv->SetLongField(jobj, jfid, (jlong)jmp);
    
    jenv->ReleaseStringUTFChars(url, url_buf);
    
    jmp->open();
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_speed (JNIEnv *jenv, jobject jobj, jlong mpID, jfloat value)
{
   assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;
   
   jmp->setSpeed(value);
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_stepMode (JNIEnv *jenv, jobject jobj, jlong mpID, jint value)
{
   assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;

   jmp->setStepMode(value);
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_jumpToTimestamp (JNIEnv *jenv, jobject jobj, jlong mpID, jint value)
{
   assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;
   
   jmp->jump(value);
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_setBandwidth (JNIEnv *jenv, jobject jobj, jlong mpID, jint value)
{
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_saveCurrentVideoFrame (JNIEnv *jenv, jobject jobj, jstring fileName, jbyteArray byteArray)
{
    const char *fileName_buf;
    fileName_buf = jenv->GetStringUTFChars(fileName, NULL);
    if (fileName_buf == NULL) 
    {
        return; /* OutOfMemoryError already thrown */
    }
    
    jbyte *data = jenv->GetByteArrayElements(byteArray, NULL);
    int dataSize = jenv->GetArrayLength(byteArray);
    
    if (data == NULL) 
    {
        jenv->ReleaseStringUTFChars(fileName, fileName_buf);
        return;
        
    }
    
    FILE *fh = fopen(fileName_buf, "wb");
    if (fh == NULL)
    {
        return;
    }
    
    fwrite(data, dataSize, 1, fh);
    fclose(fh);
    
    
    jenv->ReleaseByteArrayElements(byteArray, data, 0);
    jenv->ReleaseStringUTFChars(fileName, fileName_buf);
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_setAnalyticsKeysVisualisation (JNIEnv *jenv, jobject jobj, jlong mpID, jstring keyList)
{
   assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;

    const char *buf = jenv->GetStringUTFChars(keyList, NULL);
    if (buf == NULL) 
    {
        return; /* OutOfMemoryError already thrown */
    }
  
    jmp->setAnalyticsKeysVisualisation(buf);

    jenv->ReleaseStringUTFChars(keyList, buf);    
}

JNIEXPORT void JNICALL Java_com_videonext_mplayer_internal_MediaPlayer_setJitterBufferLen (JNIEnv *jenv, jobject, jlong mpID, jint value)
{
    assert(mpID);

   JMediaPlayer *jmp = (JMediaPlayer*)mpID;

   jmp->setJitterBufferLen(value);

}

JNIEXPORT jint JNI_OnLoad(JavaVM *g_jvm, void *reserved) 
{ 
    // store java virtual machine in global variable 
    jvm = g_jvm; 
    return JNI_VERSION_1_6; 
} 
