/*
 * Copyright 2018, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_

#include "jni.h"

#include <media/stagefright/foundation/ADebug.h>  // CHECK
#include <media/stagefright/foundation/AudioPresentationInfo.h>
#include <nativehelper/ScopedLocalRef.h>

namespace android {

struct JAudioPresentationInfo {
    struct fields_t {
        jclass      clazz = NULL;
        jmethodID   constructID;

        // list parameters
        jclass listClazz = NULL;
        jmethodID listConstructId;
        jmethodID listAddId;

        // hashmap parameters
        jclass hashMapClazz = NULL;
        jmethodID hashMapConstructID;
        jmethodID hashMapPutID;

        // ulocale parameters
        jclass ulocaleClazz = NULL;
        jmethodID ulocaleConstructID;

        void init(JNIEnv *env) {
            jclass lclazz = env->FindClass("android/media/AudioPresentation");
            CHECK(lclazz != NULL);
            clazz = (jclass)env->NewGlobalRef(lclazz);
            CHECK(clazz != NULL);
            constructID = env->GetMethodID(clazz, "<init>",
                    "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
            CHECK(constructID != NULL);

            // list objects
            jclass llistClazz = env->FindClass("java/util/ArrayList");
            CHECK(llistClazz != NULL);
            listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
            CHECK(listClazz != NULL);
            listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
            CHECK(listConstructId != NULL);
            listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
            CHECK(listAddId != NULL);

            // hashmap objects
            jclass lhashMapClazz = env->FindClass("java/util/HashMap");
            CHECK(lhashMapClazz != NULL);
            hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
            CHECK(hashMapClazz != NULL);
            hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
            CHECK(hashMapConstructID != NULL);
            hashMapPutID = env->GetMethodID(
                    hashMapClazz,
                    "put",
                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
            CHECK(hashMapPutID != NULL);

            jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
            CHECK(lulocaleClazz != NULL);
            ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
            CHECK(ulocaleClazz != NULL);
            ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
            CHECK(ulocaleConstructID != NULL);
        }

        void exit(JNIEnv *env) {
            env->DeleteGlobalRef(clazz); clazz = NULL;
            env->DeleteGlobalRef(listClazz); listClazz = NULL;
            env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
            env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
        }
    };

    static jobject asJobject(JNIEnv *env, const fields_t& fields) {
        return env->NewObject(fields.listClazz, fields.listConstructId);
    }

    static void addPresentations(JNIEnv *env, const fields_t& fields,
                    const AudioPresentationCollection& presentations, jobject presentationsJObj) {
        for (const auto& ap : presentations) {
            ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
            if (jLabelObject == nullptr) return;
            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
            if (jLanguage == nullptr) return;
            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
                            fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
            ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
                            static_cast<jint>(ap.mPresentationId),
                            static_cast<jint>(ap.mProgramId),
                            jLocale.get(),
                            static_cast<jint>(ap.mMasteringIndication),
                            static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
                            static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
                            static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
                            jLabelObject.get()));
            if (jValueObj != nullptr) {
                env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
            }
        }
    }

  private:
    static ScopedLocalRef<jobject> convertLabelsToMap(
            JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
        ScopedLocalRef<jobject> nullMap(env, nullptr);
        ScopedLocalRef<jobject> hashMap(env, env->NewObject(
                        fields.hashMapClazz, fields.hashMapConstructID));
        if (hashMap == nullptr) {
            return nullMap;
        }

        for (const auto& label : labels) {
            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
            if (jLanguage == nullptr) return nullMap;
            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
                            fields.ulocaleClazz,
                            fields.ulocaleConstructID,
                            jLanguage.get()));
            if (jLocale == nullptr) return nullMap;
            ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
            if (jValue == nullptr) return nullMap;
            env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
        }
        return hashMap;
    }
};
}  // namespace android

#endif  // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_