/*
 * This file is part of libbluray
 * Copyright (C) 2010  William Hahne
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
 */

/*
 * Note:
 * This module should be called only from Java side.
 * If it is called from native C thread, lot of references are leaked !
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include "bdjo.h"

#include "util.h"

#include "bdj/bdjo_data.h"

#include "util/logging.h"

#include <jni.h>

/* Documentation: HD Cookbook
 * https://hdcookbook.dev.java.net/
 */

#define JNICHK(a) \
  do {                                                              \
      if ((*env)->ExceptionOccurred(env)) {                         \
          BD_DEBUG(DBG_BDJ | DBG_CRIT, "Exception occurred\n");     \
          (*env)->ExceptionDescribe(env);                           \
      }                                                             \
      if (!(a)) {                                                   \
          return NULL;                                              \
      } \
  } while (0)

/*
 *
 */

static jobject _make_terminal_info(JNIEnv* env, const BDJO_TERMINAL_INFO *p)
{
    jstring jdefault_font = (*env)->NewStringUTF(env, p->default_font);
    return bdj_make_object(env, "org/videolan/bdjo/TerminalInfo", "(Ljava/lang/String;IZZ)V",
                           jdefault_font, (jint)p->initial_havi_config_id,
                           (jboolean)p->menu_call_mask, (jboolean)p->title_search_mask);
}

static jobject _make_app_cache_info(JNIEnv* env, const BDJO_APP_CACHE_INFO *p)
{
    unsigned ii;

    jobjectArray app_cache_array = bdj_make_array(env, "org/videolan/bdjo/AppCache", p->num_item);
    JNICHK(app_cache_array);

    for (ii = 0; ii < p->num_item; ii++) {
        jstring jref_to_name = (*env)->NewStringUTF(env, p->item[ii].ref_to_name);
        jstring jlang_code = (*env)->NewStringUTF(env, p->item[ii].lang_code);
        JNICHK(jref_to_name);
        JNICHK(jlang_code);

        jobject entry = bdj_make_object(env, "org/videolan/bdjo/AppCache",
                                        "(ILjava/lang/String;Ljava/lang/String;)V",
                                        (jint)p->item[ii].type,
                                        jref_to_name, jlang_code);
        JNICHK(entry);

        (*env)->SetObjectArrayElement(env, app_cache_array, ii, entry);
    }

    return app_cache_array;
}

static jobject _make_accessible_playlists(JNIEnv* env, const BDJO_ACCESSIBLE_PLAYLISTS *p)
{
    unsigned ii;

    jobjectArray playlists = bdj_make_array(env, "java/lang/String", p->num_pl);
    JNICHK(playlists);

    for (ii = 0; ii < p->num_pl; ii++) {
        jstring jplaylist_name = (*env)->NewStringUTF(env, p->pl[ii].name);
        JNICHK(jplaylist_name);

        (*env)->SetObjectArrayElement(env, playlists, ii, jplaylist_name);
    }

    return bdj_make_object(env, "org/videolan/bdjo/PlayListTable", "(ZZ[Ljava/lang/String;)V",
                           (jboolean)p->access_to_all_flag, (jboolean)p->autostart_first_playlist_flag,
                           playlists);
}

static jobject _make_app(JNIEnv* env, const BDJO_APP *p)
{
    unsigned ii;

    jobjectArray profiles = bdj_make_array(env, "org/videolan/bdjo/AppProfile", p->num_profile);
    JNICHK(profiles);

    for (ii = 0; ii < p->num_profile; ii++) {
        jobject profile = bdj_make_object(env, "org/videolan/bdjo/AppProfile", "(SBBB)V",
                                          (jshort)p->profile[ii].profile_number,
                                          (jbyte)p->profile[ii].major_version,
                                          (jbyte)p->profile[ii].minor_version,
                                          (jbyte)p->profile[ii].micro_version);
        JNICHK(profile);

        (*env)->SetObjectArrayElement(env, profiles, ii, profile);
        JNICHK(1);
    }

    jobjectArray app_names = bdj_make_array(env, "[Ljava/lang/String;", p->num_name);
    JNICHK(app_names);

    for (ii = 0; ii < p->num_name; ii++) {
        jstring jlang = (*env)->NewStringUTF(env, p->name[ii].lang);
        JNICHK(jlang);

        jstring jname = (*env)->NewStringUTF(env, p->name[ii].name);
        JNICHK(jname);

        jobjectArray app_name = bdj_make_array(env, "java/lang/String", 2);
        JNICHK(app_name);

        (*env)->SetObjectArrayElement(env, app_name, 0, jlang);
        JNICHK(1);
        (*env)->SetObjectArrayElement(env, app_name, 1, jname);
        JNICHK(1);

        (*env)->SetObjectArrayElement(env, app_names, ii, app_name);
        JNICHK(1);
    }


    jstring icon_locator = (*env)->NewStringUTF(env, p->icon_locator);
    JNICHK(icon_locator);

    jstring base_dir = (*env)->NewStringUTF(env, p->base_dir);
    JNICHK(base_dir);

    jstring classpath_extension = (*env)->NewStringUTF(env, p->classpath_extension);
    JNICHK(classpath_extension);

    jstring initial_class = (*env)->NewStringUTF(env, p->initial_class);
    JNICHK(initial_class);

    jobjectArray params = bdj_make_array(env, "java/lang/String", p->num_param);
    JNICHK(params);

    for (ii = 0; ii < p->num_param; ii++) {
        jstring param = (*env)->NewStringUTF(env, p->param[ii].param);
        JNICHK(param);

        (*env)->SetObjectArrayElement(env, params, ii, param);
        JNICHK(1);
    }

    jobject entry = bdj_make_object(env, "org/videolan/bdjo/AppEntry",
                                    "(IIIS[Lorg/videolan/bdjo/AppProfile;SII[[Ljava/lang/String;Ljava/lang/String;"
                                    "SLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
                                    (jint)p->control_code, (jint)p->type,
                                    (jint)p->org_id, (jshort)p->app_id,
                                    profiles,
                                    (jshort)p->priority, (jint)p->binding, (jint)p->visibility,
                                    app_names, icon_locator,
                                    (jshort)p->icon_flags,
                                    base_dir, classpath_extension, initial_class, params);

    return entry;
}

static jobjectArray _make_app_management_table(JNIEnv* env, const BDJO_APP_MANAGEMENT_TABLE *p)
{
    unsigned ii;

    jclass entries = bdj_make_array(env, "org/videolan/bdjo/AppEntry", p->num_app);
    JNICHK(entries);

    for (ii = 0; ii < p->num_app; ii++) {
        jobject entry = _make_app(env, &p->app[ii]);
        JNICHK(entry);

        (*env)->SetObjectArrayElement(env, entries, ii, entry);
    }

    return entries;
}

jobject bdjo_make_jobj(JNIEnv* env, const BDJO *p)
{
    jobject terminal_info = _make_terminal_info(env, &p->terminal_info);
    JNICHK(terminal_info);

    jobjectArray app_cache_info = _make_app_cache_info(env, &p->app_cache_info);
    JNICHK(app_cache_info);

    jobject accessible_playlists = _make_accessible_playlists(env, &p->accessible_playlists);
    JNICHK(accessible_playlists);

    jobjectArray app_table = _make_app_management_table(env, &p->app_table);
    JNICHK(app_table);

    jstring file_access_info = (*env)->NewStringUTF(env, p->file_access_info.path);
    JNICHK(file_access_info);

    jint key_interest_table =
      (p->key_interest_table.vk_play                   ) |
      (p->key_interest_table.vk_stop              << 1 ) |
      (p->key_interest_table.vk_ffw               << 2 ) |
      (p->key_interest_table.vk_rew               << 3 ) |
      (p->key_interest_table.vk_track_next        << 4 ) |
      (p->key_interest_table.vk_track_prev        << 5 ) |
      (p->key_interest_table.vk_pause             << 6 ) |
      (p->key_interest_table.vk_still_off         << 7 ) |
      (p->key_interest_table.vk_sec_audio_ena_dis << 8 ) |
      (p->key_interest_table.vk_sec_video_ena_dis << 9 ) |
      (p->key_interest_table.pg_textst_ena_dis    << 10);

    jobject result = bdj_make_object(env, "org/videolan/bdjo/Bdjo",
                                     "(Lorg/videolan/bdjo/TerminalInfo;[Lorg/videolan/bdjo/AppCache;"
                                     "Lorg/videolan/bdjo/PlayListTable;[Lorg/videolan/bdjo/AppEntry;ILjava/lang/String;)V",
                                     terminal_info, app_cache_info, accessible_playlists, app_table,
                                     key_interest_table, file_access_info);

    return result;
}
