/*
 * Copyright (C) 2010 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.
 */

#define LOG_TAG "NdefRecord"

#include <stdlib.h>

#include "jni.h"
#include "JNIHelp.h"

#include "android_nfc.h"

#include <utils/Log.h>

namespace android {

static jbyteArray android_nfc_NdefRecord_generate(
        JNIEnv *e, jobject o, jshort flags, jshort tnf, jbyteArray type,
        jbyteArray id, jbyteArray payload)
{
    uint32_t status;
    phFriNfc_NdefRecord_t record;
    uint32_t buf_size;
    uint32_t record_size;
    uint8_t *buf = NULL;
    jbyteArray result = NULL;

    /* Prepare NDEF record structure */
    record.Flags = (uint8_t)flags;
    record.Tnf = (uint8_t)tnf;
    record.TypeLength = (uint32_t)e->GetArrayLength(type);
    record.Type = (uint8_t *)e->GetByteArrayElements(type, NULL);
    record.IdLength = (uint32_t)e->GetArrayLength(id);
    record.Id = (uint8_t *)e->GetByteArrayElements(id, NULL);
    record.PayloadLength = (uint32_t)e->GetArrayLength(payload);
    record.PayloadData = (uint8_t *)e->GetByteArrayElements(payload, NULL);

    buf_size = record.PayloadLength + record.IdLength + record.TypeLength + 8;

    buf = (uint8_t*)malloc(buf_size);
    if (buf == NULL)
        goto end;

    TRACE("phFriNfc_NdefRecord_Generate()");

    status = phFriNfc_NdefRecord_Generate(&record, buf, buf_size,
            &record_size);

    if (status) {
        LOGE("phFriNfc_NdefRecord_Generate() returned 0x%04x", status);
        goto end;
    }
    TRACE("phFriNfc_NdefRecord_Generate() returned 0x%04x", status);

    result = e->NewByteArray(record_size);
    if (result == NULL)
        goto end;

    e->SetByteArrayRegion(result, 0, record_size, (jbyte *)buf);

end:
    e->ReleaseByteArrayElements(type, (jbyte *)record.Type, JNI_ABORT);
    e->ReleaseByteArrayElements(id, (jbyte *)record.Id, JNI_ABORT);
    e->ReleaseByteArrayElements(payload, (jbyte *)record.PayloadData, JNI_ABORT);

    if(buf)
        free(buf);

    return result;
}

static jint android_nfc_NdefRecord_parseNdefRecord(JNIEnv *e, jobject o,
        jbyteArray array)
{
    uint16_t status;
    jbyte *raw_record;
    jsize raw_record_size;
    jint ret = -1;
    phFriNfc_NdefRecord_t record;

    jfieldID mType, mId, mPayload, mTnf;
    jbyteArray type = NULL;
    jbyteArray id = NULL;
    jbyteArray payload = NULL;

    jclass record_cls = e->GetObjectClass(o);

    raw_record_size = e->GetArrayLength(array);
    raw_record = e->GetByteArrayElements(array, NULL);
    if (raw_record == NULL) {
        goto clean_and_return;
    }

    TRACE("phFriNfc_NdefRecord_Parse()");
    status = phFriNfc_NdefRecord_Parse(&record, (uint8_t *)raw_record);
    if (status) {
        LOGE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status);
        goto clean_and_return;
    }
    TRACE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status);

    /* Set TNF field */
    mTnf = e->GetFieldID(record_cls, "mTnf", "S");
    e->SetShortField(o, mTnf, record.Tnf);

    /* Set type field */
    mType = e->GetFieldID(record_cls, "mType", "[B");
    type = e->NewByteArray(record.TypeLength);
    if (type == NULL) {
        goto clean_and_return;
    }
    e->SetByteArrayRegion(type, 0, record.TypeLength,
            (jbyte *)record.Type);
    e->SetObjectField(o, mType, type);

    /* Set id field */
    mId = e->GetFieldID(record_cls, "mId", "[B");
    id = e->NewByteArray(record.IdLength);
    if (id == NULL) {
        goto clean_and_return;
    }
    e->SetByteArrayRegion(id, 0, record.IdLength,
            (jbyte *)record.Id);
    e->SetObjectField(o, mId, id);

    /* Set payload field */
    mPayload = e->GetFieldID(record_cls, "mPayload", "[B");
    payload = e->NewByteArray(record.PayloadLength);
    if (payload == NULL) {
        goto clean_and_return;
    }
    e->SetByteArrayRegion(payload, 0, record.PayloadLength,
            (jbyte *)record.PayloadData);
    e->SetObjectField(o, mPayload, payload);

    ret = 0;

clean_and_return:
    if (type != NULL) {
        e->DeleteLocalRef(type);
    }
    if (id != NULL) {
        e->DeleteLocalRef(id);
    }
    if (payload != NULL) {
        e->DeleteLocalRef(payload);
    }
    if (raw_record != NULL) {
        e->ReleaseByteArrayElements(array, raw_record, JNI_ABORT);
    }

    return ret;
}

static JNINativeMethod gMethods[] = {
    {"generate", "(SS[B[B[B)[B", (void *)android_nfc_NdefRecord_generate},
    {"parseNdefRecord", "([B)I", (void *)android_nfc_NdefRecord_parseNdefRecord},
};

int register_android_nfc_NdefRecord(JNIEnv *e)
{
    return jniRegisterNativeMethods(e, "android/nfc/NdefRecord", gMethods, NELEM(gMethods));
}

} // namespace android
