/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the Collabora Office project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <jni.h>

#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

#include <osl/detail/android-bootstrap.h>

#include <COKit/COKit.hxx>

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "COKit", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "COKit", __VA_ARGS__))

// Everything declared here is defined in C, in cokit-bootstrap.c or in the sofficeapp library,
// so it keeps C linkage.
extern "C" {

/* These are valid / used in all apps. */
extern const char* data_dir;
extern const char* cache_dir;
extern void* apk_file;
extern int apk_file_size;

extern void Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv* env, jobject clazz, jstring string);
extern void Java_org_libreoffice_android_Bootstrap_redirect_1stdio(JNIEnv* env, jobject clazz, jboolean state);

extern COKit* cokit_hook(const char* install_path);

}

AAssetManager* native_asset_manager;

static char *full_program_dir = nullptr;

/// Call the same method from Bootstrap.
extern "C" __attribute__ ((visibility("default")))
void
Java_org_libreoffice_kit_COKit_putenv
    (JNIEnv* env, jobject clazz, jstring string)
{
    Java_org_libreoffice_android_Bootstrap_putenv(env, clazz, string);
}

/// Call the same method from Bootstrap.
extern "C" __attribute__ ((visibility("default")))
void Java_org_libreoffice_kit_COKit_redirectStdio
    (JNIEnv* env, jobject clazz, jboolean state)
{
    Java_org_libreoffice_android_Bootstrap_redirect_1stdio(env, clazz, state);
}

/// Initialize the COKit.
extern "C" __attribute__ ((visibility("default")))
jboolean cokit_initialize(JNIEnv* env,
     jstring dataDir, jstring cacheDir, jstring apkFile, jobject assetManager)
{
    struct stat st;
    int fd;
    const char *dataDirPath;
    const char *cacheDirPath;
    const char *apkFilePath;

    size_t data_dir_len;
    const char fontsConf[] = "/etc/fonts/fonts.conf";
    char *fontsConfPath;

    setenv("OOO_DISABLE_RECOVERY", "1", 1);

    native_asset_manager = AAssetManager_fromJava(env, assetManager);

    dataDirPath = env->GetStringUTFChars(dataDir, nullptr);
    data_dir = strdup(dataDirPath);
    env->ReleaseStringUTFChars(dataDir, dataDirPath);

    cacheDirPath = env->GetStringUTFChars(cacheDir, nullptr);
    cache_dir = strdup(cacheDirPath);
    env->ReleaseStringUTFChars(cacheDir, cacheDirPath);

    // TMPDIR is used by osl_getTempDirURL()
    setenv("TMPDIR", cache_dir, 1);

    data_dir_len = strlen(data_dir);
    fontsConfPath = static_cast<char*>(malloc(data_dir_len + sizeof(fontsConf)));
    strncpy(fontsConfPath, data_dir, data_dir_len);
    strncpy(fontsConfPath + data_dir_len, fontsConf, sizeof(fontsConf));

    fd = open(fontsConfPath, O_RDONLY);
    if (fd != -1) {
        close(fd);
        LOGI("Setting FONTCONFIG_FILE to %s", fontsConfPath);
        setenv("FONTCONFIG_FILE", fontsConfPath, 1);
        // DEBUG:
        //setenv("FC_DEBUG", "8191", 1); // log everything
        //Java_org_libreoffice_android_Bootstrap_redirect_1stdio(nullptr, nullptr, JNI_TRUE);
    }
    free(fontsConfPath);

    apkFilePath =  env->GetStringUTFChars(apkFile, nullptr);

    fd = open(apkFilePath, O_RDONLY);
    if (fd == -1) {
        LOGE("Could not open %s", apkFilePath);
        env->ReleaseStringUTFChars(apkFile, apkFilePath);
        return JNI_FALSE;
    }
    if (fstat(fd, &st) == -1) {
        LOGE("Could not fstat %s", apkFilePath);
        close(fd);
        env->ReleaseStringUTFChars(apkFile, apkFilePath);
        return JNI_FALSE;
    }
    apk_file = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
    close(fd);

    if (apk_file == MAP_FAILED) {
        LOGE("Could not mmap %s", apkFilePath);
        env->ReleaseStringUTFChars(apkFile, apkFilePath);
        return JNI_FALSE;
    }
    apk_file_size = st.st_size;

    env->ReleaseStringUTFChars(apkFile, apkFilePath);

    if (!setup_cdir())
    {
        LOGE("setup_cdir failed");
        return JNI_FALSE;
    }

    if (!setup_assets_tree())
    {
        LOGE("setup_assets_tree failed");
        return JNI_FALSE;
    }

    LOGI("COKit: cokit_initialize finished");

    return JNI_TRUE;
}

/// Initialize the COKit.
extern "C" __attribute__ ((visibility("default")))
jboolean Java_org_libreoffice_kit_COKit_initializeNative
    (JNIEnv* env, jobject clazz,
     jstring dataDir, jstring cacheDir, jstring apkFile, jobject assetManager)
{
    const char program_dir[] = "/program";
    size_t data_dir_len;

    (void) clazz;

    cokit_initialize(env, dataDir, cacheDir, apkFile, assetManager);

    // COKit expects a path to the program/ directory
    free(full_program_dir);
    data_dir_len = strlen(data_dir);
    full_program_dir = static_cast<char*>(malloc(data_dir_len + sizeof(program_dir)));

    strncpy(full_program_dir, data_dir, data_dir_len);
    strncpy(full_program_dir + data_dir_len, program_dir, sizeof(program_dir));

    // Initialize COKit
    if (!cokit_hook(full_program_dir))
    {
        LOGE("cokit_hook returned null");
        return JNI_FALSE;
    }

    LOGI("COKit successfully initialized");

    return JNI_TRUE;
}

extern "C" __attribute__ ((visibility("default")))
jobject Java_org_libreoffice_kit_COKit_getCOKitHandle
    (JNIEnv* env, jobject clazz)
{
    COKit* aOffice;

    (void) clazz;

    aOffice = cokit_hook(full_program_dir);

    return env->NewDirectByteBuffer(static_cast<void*>(aOffice), sizeof(COKit));
}

__attribute__ ((visibility("default")))
AAssetManager *
lo_get_native_assetmgr(void)
{
    return native_asset_manager;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
