/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details:
 *
 * Copyright (C) 2013 Google, Inc.
 */

#include <glib.h>
#include <glib-object.h>
#include <string.h>
#include <stdio.h>
#include <locale.h>

#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>

#include "mm-sms-part-cdma.h"
#include "mm-log.h"

/* If defined will print debugging traces */
#ifdef TEST_SMS_PART_ENABLE_TRACE
#define trace_pdu(pdu, pdu_len) do {      \
        guint i;                          \
                                          \
        g_print ("\n        ");           \
        for (i = 0; i < len; i++) {       \
            g_print ("  0x%02X", pdu[i]); \
            if (((i + 1) % 12) == 0)      \
                g_print ("\n        ");   \
        }                                 \
        g_print ("\n");                   \
    } while (0)
#else
#define trace_pdu(...)
#endif

/********************* PDU PARSER TESTS *********************/

static void
common_test_part_from_hexpdu (const gchar *hexpdu,
                              MMSmsCdmaTeleserviceId expected_teleservice_id,
                              MMSmsCdmaServiceCategory expected_service_category,
                              const gchar *expected_address,
                              guint8 expected_bearer_reply_option,
                              const gchar *expected_text)
{
    MMSmsPart *part;
    GError *error = NULL;

    mm_dbg (" ");
    part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, &error);
    g_assert_no_error (error);
    g_assert (part != NULL);

    if (expected_teleservice_id != MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN)
        g_assert_cmpuint (expected_teleservice_id, ==, mm_sms_part_get_cdma_teleservice_id (part));
    if (expected_service_category != MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN)
        g_assert_cmpuint (expected_service_category, ==, mm_sms_part_get_cdma_service_category (part));
    if (expected_address) {
        if (expected_address[0])
            g_assert_cmpstr (expected_address, ==, mm_sms_part_get_number (part));
        else
            g_assert (mm_sms_part_get_number (part) == NULL);
    }
    if (expected_bearer_reply_option)
        g_assert_cmpuint (expected_bearer_reply_option, ==, mm_sms_part_get_message_reference (part));
    if (expected_text)
        g_assert_cmpstr (expected_text, ==, mm_sms_part_get_text (part));

    mm_sms_part_free (part);
}

static void
common_test_part_from_pdu (const guint8 *pdu,
                           gsize pdu_size,
                           MMSmsCdmaTeleserviceId expected_teleservice_id,
                           MMSmsCdmaServiceCategory expected_service_category,
                           const gchar *expected_address,
                           guint8 expected_bearer_reply_option,
                           const gchar *expected_text)
{
    gchar *hexpdu;

    hexpdu = mm_utils_bin2hexstr (pdu, pdu_size);
    common_test_part_from_hexpdu (hexpdu,
                                  expected_teleservice_id,
                                  expected_service_category,
                                  expected_address,
                                  expected_bearer_reply_option,
                                  expected_text);
    g_free (hexpdu);
}

static void
common_test_invalid_part_from_hexpdu (const gchar *hexpdu)
{
    MMSmsPart *part;
    GError *error = NULL;

    mm_dbg (" ");
    part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, &error);
    g_assert (part == NULL);
    /* We don't care for the specific error type */
    g_assert (error != NULL);
    g_error_free (error);
}

static void
common_test_invalid_part_from_pdu (const guint8 *pdu,
                                   gsize pdu_size)
{
    gchar *hexpdu;

    hexpdu = mm_utils_bin2hexstr (pdu, pdu_size);
    common_test_invalid_part_from_hexpdu (hexpdu);
    g_free (hexpdu);
}

static void
test_pdu1 (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* originating address */
        0x02, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer reply option */
        0x06, 0x01,
        0xFC,
        /* bearer data */
        0x08, 0x15,
        0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06,
        0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03,
        0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47
    };

    common_test_part_from_pdu (
        pdu, sizeof (pdu),
        MM_SMS_CDMA_TELESERVICE_ID_WMT,
        MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
        "3305773196",
        63,
        "AAAA");
}

static void
test_invalid_parameter_length (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* originating address */
        0x02, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer reply option */
        0x06, 0x01,
        0xFC,
        /* bearer data */
        0x08, 0x20, /* wrong parameter length! */
        0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06,
        0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03,
        0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47
    };

    common_test_invalid_part_from_pdu (pdu, sizeof (pdu));
}

static void
test_invalid_address_length (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* originating address (wrong num_fields) */
        0x02, 0x07,
        0x03, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer reply option */
        0x06, 0x01,
        0xFC,
        /* bearer data */
        0x08, 0x15,
        0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06,
        0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03,
        0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47
    };

    common_test_part_from_pdu (
        pdu, sizeof (pdu),
        MM_SMS_CDMA_TELESERVICE_ID_WMT,
        MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
        "",
        63,
        NULL);
}

static void
test_created_by_us (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* destination address */
        0x04, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer data */
        0x08, 0x0D,
        0x00, 0x03, 0x20, 0x00, 0x00, /* message id */
        0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80 /* user_data */
    };

    common_test_part_from_pdu (
        pdu, sizeof (pdu),
        MM_SMS_CDMA_TELESERVICE_ID_WMT,
        MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
        "3305773196",
        0,
        "AAAA");
}

static void
test_latin_encoding (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* originating address */
        0x02, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer reply option */
        0x06, 0x01,
        0xFC,
        /* bearer data */
        0x08, 0x39,
            /* message id */
            0x00, 0x03,
            0x13, 0x8D, 0x20,
            /* user data */
            0x01, 0x27,
            0x41, 0x29, 0x19, 0x22, 0xE1, 0x19, 0x1A, 0xE1,
            0x1A, 0x01, 0x19, 0xA1, 0x19, 0xA1, 0xA9, 0xB1,
            0xB9, 0xE9, 0x53, 0x4B, 0x23, 0xAB, 0x53, 0x23,
            0xAB, 0x23, 0x2B, 0xAB, 0xAB, 0x2B, 0x23, 0xAB,
            0x53, 0x23, 0x2B, 0xAB, 0x53, 0xAB, 0x20,
            /* message center timestamp */
            0x03, 0x06,
            0x13, 0x10, 0x23, 0x20, 0x06, 0x37,
            /* priority indicator */
            0x08, 0x01,
            0x00
    };

    common_test_part_from_pdu (
        pdu, sizeof (pdu),
        MM_SMS_CDMA_TELESERVICE_ID_WMT,
        MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
        "3305773196",
        63,
        /* this is ASCII-7 but message uses latin encoding */
        "#$\\##\\#@#4#4567=*idujdudeuuedujdeujud");
}

static void
test_latin_encoding_2 (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* originating address */
        0x02, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer reply option */
        0x06, 0x01,
        0xFC,
        /* bearer data */
        0x08, 0x1C,
            /* message id */
            0x00, 0x03,
            0x13, 0x8D, 0x20,
            /* user data */
            0x01, 0x0A,
            0x40, 0x42, 0x1B, 0x0B, 0x6B, 0x83, 0x2F, 0x9B,
            0x71, 0x08,
            /* message center timestamp */
            0x03, 0x06,
            0x13, 0x10, 0x23, 0x20, 0x06, 0x37,
            /* priority indicator */
            0x08, 0x01,
            0x00
    };

    common_test_part_from_pdu (
        pdu, sizeof (pdu),
        MM_SMS_CDMA_TELESERVICE_ID_WMT,
        MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
        "3305773196",
        63,
        /* this is latin and message uses latin encoding */
        "Campeón!");
}

static void
test_unicode_encoding (void)
{
    static const guint8 pdu[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* originating address */
        0x02, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer reply option */
        0x06, 0x01,
        0xFC,
        /* bearer data */
        0x08, 0x28,
            /* message id */
            0x00, 0x03,
            0x1B, 0x73, 0xF0,
            /* user data */
            0x01, 0x16,
            0x20, 0x52, 0x71, 0x6A, 0xB8, 0x5A, 0xA7, 0x92,
            0xDB, 0xC3, 0x37, 0xC4, 0xB7, 0xDA, 0xDA, 0x82,
            0x98, 0xB4, 0x50, 0x42, 0x94, 0x18,
            /* message center timestamp */
            0x03, 0x06,
            0x13, 0x10, 0x24, 0x10, 0x45, 0x28,
            /* priority indicator */
            0x08, 0x01,
            0x00
    };

    common_test_part_from_pdu (
        pdu, sizeof (pdu),
        MM_SMS_CDMA_TELESERVICE_ID_WMT,
        MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
        "3305773196",
        63,
        "中國哲學書電子化計劃");
}

/********************* PDU CREATOR TESTS *********************/

static void
common_test_create_pdu (MMSmsCdmaTeleserviceId teleservice_id,
                        const gchar *number,
                        const gchar *text,
                        const guint8 *data,
                        gsize data_size,
                        const guint8 *expected,
                        gsize expected_size)
{
    MMSmsPart *part;
    guint8 *pdu;
    guint len = 0;
    GError *error = NULL;

    g_assert (number != NULL);

    part = mm_sms_part_new (0, MM_SMS_PDU_TYPE_CDMA_SUBMIT);
    mm_sms_part_set_cdma_teleservice_id (part, teleservice_id);
    mm_sms_part_set_number (part, number);
    if (text)
        mm_sms_part_set_text (part, text);
    else {
        GByteArray *data_bytearray;

        data_bytearray = g_byte_array_sized_new (data_size);
        g_byte_array_append (data_bytearray, data, data_size);
        mm_sms_part_take_data (part, data_bytearray);
    }

    pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, &error);
    mm_sms_part_free (part);

    trace_pdu (pdu, len);

    g_assert_no_error (error);
    g_assert (pdu != NULL);
    g_assert_cmpuint (len, ==, expected_size);
    g_assert_cmpint (memcmp (pdu, expected, len), ==, 0);

    g_free (pdu);
}

static void
test_create_pdu_text_ascii_encoding (void)
{
    static const char *number = "3305773196";
    static const char *text = "AAAA";
    static const guint8 expected[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* destination address */
        0x04, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer data */
        0x08, 0x0D,
        0x00, 0x03, 0x20, 0x00, 0x00, /* message id */
        0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80 /* user_data */
    };

    common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT,
                            number,
                            text,
                            NULL, 0,
                            expected, sizeof (expected));
}

static void
test_create_pdu_text_latin_encoding (void)
{
    static const char *number = "3305773196";
    static const char *text = "Campeón!";
    static const guint8 expected[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* destination address */
        0x04, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer data */
        0x08, 0x11,
            /* message id */
            0x00, 0x03,
            0x20, 0x00, 0x00,
            /* user data */
            0x01, 0x0A,
            0x40, 0x42, 0x1B, 0x0B, 0x6B, 0x83, 0x2F, 0x9B,
            0x71, 0x08
    };

    common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT,
                            number,
                            text,
                            NULL, 0,
                            expected, sizeof (expected));
}

static void
test_create_pdu_text_unicode_encoding (void)
{
    static const char *number = "3305773196";
    static const char *text = "中國哲學書電子化計劃";
    static const guint8 expected[] = {
        /* message type */
        0x00,
        /* teleservice id */
        0x00, 0x02,
        0x10, 0x02,
        /* destination address */
        0x04, 0x07,
        0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
        /* bearer data */
        0x08, 0x1D,
            /* message id */
            0x00, 0x03,
            0x20, 0x00, 0x00,
            /* user data */
            0x01, 0x16,
            0x20, 0x52, 0x71, 0x6A, 0xB8, 0x5A, 0xA7, 0x92,
            0xDB, 0xC3, 0x37, 0xC4, 0xB7, 0xDA, 0xDA, 0x82,
            0x98, 0xB4, 0x50, 0x42, 0x94, 0x18
    };

    common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT,
                            number,
                            text,
                            NULL, 0,
                            expected, sizeof (expected));
}

/************************************************************/

void
_mm_log (const char *loc,
         const char *func,
         guint32 level,
         const char *fmt,
         ...)
{
#if defined ENABLE_TEST_MESSAGE_TRACES
    /* Dummy log function */
    va_list args;
    gchar *msg;

    va_start (args, fmt);
    msg = g_strdup_vprintf (fmt, args);
    va_end (args);
    g_print ("%s\n", msg);
    g_free (msg);
#endif
}

int main (int argc, char **argv)
{
    setlocale (LC_ALL, "");

    g_type_init ();
    g_test_init (&argc, &argv, NULL);

    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/pdu1", test_pdu1);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-parameter-length", test_invalid_parameter_length);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-address-length", test_invalid_address_length);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/created-by-us", test_created_by_us);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/latin-encoding", test_latin_encoding);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/latin-encoding-2", test_latin_encoding_2);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/unicode-encoding", test_unicode_encoding);

    g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/ascii-encoding", test_create_pdu_text_ascii_encoding);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/latin-encoding", test_create_pdu_text_latin_encoding);
    g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/unicode-encoding", test_create_pdu_text_unicode_encoding);

    return g_test_run ();
}
