/******************************************************************************
*
* Copyright (C) 2019  Brian Berlin
* 
* 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 3 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.
* 
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************
*
* FILENAME: test_toml.c
*
* DESCRIPTION:  This contains tests for the toml module.  It can take two
*               arguments, input and output file, if output is left out, it 
*               will use stdout
*
* AUTHORS:  bberlin
*
* CHANGE LOG
*
*  VER     DATE     CHANGED BY   DESCRIPTION OF CHANGE
* ------  --------  ----------   ------------------------------------
* 0.9.0   02/28/19  bberlin      Creation
******************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "../src/interface/toml.h"

int failure = 0;

void output_result ( int result, const char *test, const char *node, 
        const char *value, const char *error ) {

    if ( result ) {
        printf ( "\e[32m[PASS]\e[m %s for node \"%s\" Result=%s\n", test, node, value );
    }
    else {
        failure++;
        printf ( "\e[31m[FAIL]\e[m %s for node \"%s\" %s Result=%s\n", test, node, error, value );
    }

}

int get_error_string ( char *err ) {

    char err_str[40];
    int line_no;
    int error;

    if ( error = toml_get_error(&line_no) ) {
        switch(error) {
            case TOML_FILE: strcpy(err_str, "File Error"); break;
            case TOML_INTERNAL_ERROR: strcpy(err_str, "Internal Error"); break;
            case TOML_INVALID_TABLE_DECLARATION: 
                   strcpy(err_str, "Invalid Table Declaration"); break;
            case TOML_INVALID_KEY_DECLARATION: 
                   strcpy(err_str, "Invalid_Key Declaration"); break;
            case TOML_INVALID_KEY: strcpy(err_str, "Invalid Key"); break;
            case TOML_INVALID_ARRAY: strcpy(err_str, "Invalid Array"); break;
            case TOML_INVALID_ARRAY_MIXED_TYPES: 
                   strcpy(err_str, "Array has mixed types"); break;
            default: strcpy ( err_str, "Unknown error" );
        }

        sprintf ( err, "Error: %s on line number %d\n", err_str, line_no );
    }

    return error;
}

int compare_files(FILE *fp1, FILE *fp2) {

    char ch1 = getc(fp1);
    char ch2 = getc(fp2);

    int error = 0, pos = 0, line = 1;

    while (ch1 != EOF && ch2 != EOF)
    {
        pos++;

        if (ch1 == '\n' && ch2 == '\n')
        {
            line++;
            pos = 0;
        }

        if (ch1 != ch2)
        {
            error++;
            printf("Line Number : %d \tError"
               " Position : %d \n", line, pos);
        }

        ch1 = getc(fp1);
        ch2 = getc(fp2);
    }

    return error;
}

int write_text_file(const char *filename, const char *contents) {

    FILE *fp = fopen(filename, "w");

    if (!fp)
        return -1;

    fputs(contents, fp);
    fclose(fp);

    return 0;
}

void make_temp_filename(char *filename, size_t filename_size) {

    static int counter = 0;

    snprintf(filename, filename_size, "test_toml_tmp_%d.tmp", counter++);
}

t_toml_node * parse_inline_toml(const char *contents, char *filename) {

    make_temp_filename(filename, L_tmpnam);

    if (write_text_file(filename, contents))
        return NULL;

    return toml_parse_file(filename);
}

int write_matches_text(t_toml_node *node, const char *contents) {

    char actual_file[L_tmpnam];
    char expected_file[L_tmpnam];
    FILE *fp_expected;
    FILE *fp_actual;
    int result;

    make_temp_filename(actual_file, sizeof(actual_file));
    make_temp_filename(expected_file, sizeof(expected_file));

    if (toml_write_file(node, actual_file) ||
            write_text_file(expected_file, contents))
        return -1;

    fp_expected = fopen(expected_file, "r");
    fp_actual = fopen(actual_file, "r");
    result = (!fp_expected || !fp_actual) ? -1 :
        compare_files(fp_expected, fp_actual);
    if (fp_expected) fclose(fp_expected);
    if (fp_actual) fclose(fp_actual);

    remove(actual_file);
    remove(expected_file);

    return result;
}

void set_node_text(char **node_string, const char *contents) {

    if (*node_string)
        free(*node_string);

    *node_string = malloc(strlen(contents) + 1);
    if (*node_string)
        strcpy(*node_string, contents);
}

t_toml_node * find_array_end_node(t_toml_node *array_node) {

    t_toml_node *node;

    if (!array_node || array_node->type != TOML_ARRAY)
        return NULL;

    node = array_node->child;
    while (node && node->type != TOML_ARRAY_END)
        node = node->next;

    return node;
}

/******************************************************************************
**  Function   :  main
**                                                
**  Objective  :  This is the main used to test the toml module
**                                               
**  Parameters :  NONE                          
**                                             
**  return     :  0 if success                
**                -1 if failure               
******************************************************************************/ 
int main ( int argc, char *argv[]  ) {

    t_toml_node *node,*result_node, *tmp_node;
    int line_no, error, result;
    char err_str[75];
    char *result_value;
    char *temp_file;
    char temp_file_buf[L_tmpnam];
    const char *node_name;
    FILE *fp1, *fp2;

    if ( argc < 2 ) {
        printf ( "toml_test - input file required\n" );
        return -1;
    }

    /*
     * Test parsing input file
     */
    node = toml_parse_file ( argv[1] );

    result = get_error_string ( err_str );
    output_result ( result ? 0 : 1, "Parse File", argv[1], 
                    NULL, err_str );

    if ( node == NULL )
        return -1;

    /* Regression: empty quoted strings must be present, non-NULL values. */
    {
        const char *empty_string_text =
            "empty = \"\"\n"
            "filled = \"value\"\n";
        char empty_string_file[L_tmpnam];
        t_toml_node *empty_string_node;

        empty_string_node = parse_inline_toml(empty_string_text,
                empty_string_file);
        result_value = empty_string_node ?
            toml_get_key_value(empty_string_node, "empty") : NULL;
        result = empty_string_node &&
            result_value &&
            result_value[0] == '\0' &&
            !write_matches_text(empty_string_node, empty_string_text);
        output_result ( result, "Empty String Round Trip", "empty",
                result_value ? result_value : "NULL",
                " empty string was missing or changed" );

        toml_free_node_tree(empty_string_node);
        remove(empty_string_file);
    }

    /* Regression: parsed documents must own independent roots. */
    {
        char first_file[L_tmpnam];
        char second_file[L_tmpnam];
        char third_file[L_tmpnam];
        t_toml_node *first_node;
        t_toml_node *second_node;
        t_toml_node *third_node;

        first_node = parse_inline_toml(
                "[first]\n"
                "value = \"one\"\n",
                first_file);
        second_node = parse_inline_toml(
                "[second]\n"
                "value = \"two\"\n",
                second_file);

        toml_free_node_tree(first_node);
        result_value = second_node ?
            toml_get_key_value(second_node, "second.value") : NULL;
        result = second_node && result_value && !strcmp(result_value, "two") &&
            !write_matches_text(second_node,
                    "[second]\n"
                    "value = \"two\"\n");
        output_result ( result, "Independent Roots After Free",
                "second.value", result_value, " second tree was invalidated" );

        toml_free_node_tree(second_node);

        third_node = parse_inline_toml(
                "value = 3\n",
                third_file);
        result_value = third_node ?
            toml_get_key_value(third_node, "value") : NULL;
        output_result ( third_node && result_value && !strcmp(result_value, "3"),
                "Parse After Root Free", "value", result_value,
                " parse reused freed root memory" );

        toml_free_node_tree(third_node);
        remove(first_file);
        remove(second_file);
        remove(third_file);
    }

    /* Regression: table headers with or without interior whitespace parse. */
    {
        char compact_table_file[L_tmpnam];
        char spaced_table_file[L_tmpnam];
        t_toml_node *compact_table_node;
        t_toml_node *spaced_table_node;

        compact_table_node = parse_inline_toml(
                "[foo]\n"
                "value = 1\n",
                compact_table_file);
        result = compact_table_node &&
            !write_matches_text(compact_table_node,
                    "[foo]\n"
                    "value = 1\n");
        output_result ( result, "Compact Table Header", "foo",
                NULL, " failed to round-trip" );

        spaced_table_node = parse_inline_toml(
                "[ foo ]\n"
                "value = 1\n",
                spaced_table_file);
        result = spaced_table_node &&
            !write_matches_text(spaced_table_node,
                    "[foo]\n"
                    "value = 1\n");
        output_result ( result, "Spaced Table Header", "foo",
                NULL, " failed to parse or normalize" );

        toml_free_node_tree(compact_table_node);
        toml_free_node_tree(spaced_table_node);
        remove(compact_table_file);
        remove(spaced_table_file);
    }

    /* Regression: scalar tokens must parse fully and serialize safely. */
    {
        const char *scalar_text =
            "integer = 42\n"
            "float = 3.14\n"
            "date = 2026-05-17\n"
            "time = 12:34:56\n"
            "datetime = 2026-05-17T12:34:56\n"
            "plain_inf = inf\n"
            "pos_inf = +inf\n"
            "neg_inf = -inf\n"
            "plain_nan = nan\n"
            "pos_nan = +nan\n"
            "neg_nan = -nan\n";
        char scalar_file[L_tmpnam];
        char invalid_scalar_file[L_tmpnam];
        t_toml_node *scalar_node;
        t_toml_node *invalid_scalar_node;

        scalar_node = parse_inline_toml(scalar_text, scalar_file);
        result = scalar_node && !write_matches_text(scalar_node, scalar_text);
        output_result ( result, "Scalar Round Trip", "date/time/special",
                NULL, " valid scalars failed" );

        invalid_scalar_node = parse_inline_toml(
                "broken = 1abc\n",
                invalid_scalar_file);
        result = invalid_scalar_node == NULL && get_error_string(err_str);
        output_result ( result, "Reject Partial Scalar", "broken",
                NULL, " malformed number parsed successfully" );

        toml_free_node_tree(scalar_node);
        toml_free_node_tree(invalid_scalar_node);
        remove(scalar_file);
        remove(invalid_scalar_file);
    }

    /* Regression: multiline strings keep bodies, delimiters, and blank lines. */
    {
        const char *multiline_text =
            "inline_double = \"\"\"alpha\n"
            "\n"
            "beta\"\"\"\n"
            "next_single = '''\n"
            "gamma\n"
            "\n"
            "delta\n"
            "'''\n";
        char multiline_file[L_tmpnam];
        t_toml_node *multiline_node;

        multiline_node = parse_inline_toml(multiline_text, multiline_file);
        result = multiline_node &&
            !write_matches_text(multiline_node, multiline_text);
        output_result ( result, "Multiline String Round Trip",
                "inline_double/next_single", NULL,
                " multiline text changed" );

        toml_free_node_tree(multiline_node);
        remove(multiline_file);
    }

    /* Regression: escaped backslashes in double-quoted strings do not escape
       the next ordinary character. */
    {
        const char *windows_path_text =
            "path = \"C:\\\\Users\\\\Brian\\\\AppData\\\\Roaming\\\\kat5200\\\\Atari5200Rom\\\\_romset_dl.zip/No-Intro-5200_mod/[BIOS] Atari 5200 (USA).a52\"\n";
        const char *expected_value =
            "C:\\Users\\Brian\\AppData\\Roaming\\kat5200\\Atari5200Rom\\_romset_dl.zip/No-Intro-5200_mod/[BIOS] Atari 5200 (USA).a52";
        char windows_path_file[L_tmpnam];
        t_toml_node *windows_path_node;

        windows_path_node = parse_inline_toml(windows_path_text, windows_path_file);
        result_value = windows_path_node ?
            toml_get_key_value(windows_path_node, "path") : NULL;
        result = windows_path_node &&
            result_value &&
            !strcmp(result_value, expected_value) &&
            !write_matches_text(windows_path_node, windows_path_text);
        output_result ( result, "Escaped Backslash String",
                "path", result_value,
                " escaped backslash path failed" );

        toml_free_node_tree(windows_path_node);
        remove(windows_path_file);
    }

    /* Regression: scalar mutation succeeds; array mutation must be rejected. */
    {
        char mutation_file[L_tmpnam];
        t_toml_node *mutation_node;

        mutation_node = parse_inline_toml(
                "value = \"old\"\n"
                "items = [ \"A\" ]\n",
                mutation_file);
        result_value = mutation_node ?
            toml_get_key_value(mutation_node, "value") : NULL;
        result = mutation_node &&
            !toml_set_value(mutation_node, "value", "new") &&
            (result_value = toml_get_key_value(mutation_node, "value")) &&
            !strcmp(result_value, "new");
        output_result ( result, "Set Scalar Value", "value",
                result_value,
                " scalar update failed" );

        result_value = mutation_node ?
            toml_get_array_value_by_index(mutation_node, "items", 0) : NULL;
        result = mutation_node &&
            toml_set_value(mutation_node, "items", "B") != 0 &&
            (result_value = toml_get_array_value_by_index(mutation_node, "items", 0)) &&
            !strcmp(result_value, "A") &&
            !write_matches_text(mutation_node,
                    "value = \"new\"\n"
                    "items = [ \"A\" ]\n");
        output_result ( result, "Reject Array Set Value", "items",
                result_value,
                " array update reported success" );

        toml_free_node_tree(mutation_node);
        remove(mutation_file);
    }

    /* Test write to stdout or file */
    
    if ( argc > 2 )
        temp_file = argv[2];
    else {
        make_temp_filename(temp_file_buf, sizeof(temp_file_buf));
        temp_file = temp_file_buf;
    }
    toml_write_file ( node, temp_file );
    fp1 = fopen(argv[1], "r");
    fp2 = fopen(temp_file, "r");
    result = compare_files ( fp1, fp2 );
    fclose(fp1);
    fclose(fp2);
    output_result ( result ? 0 : 1, "Original and Parse File Equal", argv[1], 
                    NULL, err_str );

    /* Test finding a key */
    node_name = "general.machine";
    result_node = toml_find_node ( node, node_name );
    output_result ( result_node ? 1 : 0, "Find Key", node_name, 
                    result_node ? result_node->value : NULL, "not found" );

    /* Test finding a key value */
    node_name = "input.game.p0.mouse.speed";
    result_value = toml_get_key_value ( node, node_name);
    output_result ( result_value ? 1 : 0, "Find Key Value", node_name, 
                    result_value, "not found" );

    /* Test finding a key value */
    node_name = "input.game.p0.5200.joystick.left";
    result_value = toml_get_array_value_by_index ( node, node_name, 1 );
    output_result ( result_value ? 1 : 0, "Find Array Value by Index(1)", node_name, 
                    result_value, "not found" );

    /* Test finding input game (use the child of main node) */
    node_name = "input.game";
    result_node = toml_get_table_array_by_key ( node->child, node_name, "tag", "All" );
    output_result ( result_node ? 1 : 0, "Table Array for tag All", node_name, 
                    NULL, "not found" );

    if ( result_node ) {
        node_name = "input.game.p1.joy.deadzone";
        result_value = toml_get_key_value ( result_node, node_name );
        output_result ( result_value ? 1 : 0, "Table Array key", node_name, 
                        result_value, "not found" );
    }

    /* Test inserting into an array 0 */
    node_name = "input.misc.load.state.8";
    result = toml_insert_array_value_by_index ( node, node_name, 
                                    "enable|j0|b2|press|combo|b4|press", " ", "\n", 
                                    "                 ", 0 );

    output_result ( result ? 0 : 1, "Array insert index(0)", node_name, 
                    NULL, " array not found" );

    /* Test inserting into an array 2 */
    node_name = "input.game";
    result_node = toml_get_table_array_by_key ( node->child, node_name, "tag", "All" );
    if ( result_node ) {
        node_name = "input.game.p1.5200.trackball.right";
        result = toml_insert_array_value_by_index ( node, node_name, 
                                "enable|j1|b2|press|combo|b4|press", 
                                "            ", "\n", 
                                "            ", 2 );
        output_result ( result ? 0 : 1, "Array insert index(2)", node_name, 
                        NULL, " array not found" );
    }

    /* Test inserting into an array -1 (end) */
    node_name = "input.misc.exit";
    result = toml_insert_array_value_by_index ( node, node_name, 
                         "enable|j0|b2|press|combo|b4|press", "         ", "\n", 
                         " ", -1 );

    output_result ( result ? 0 : 1, "Array insert index(-1)", node_name, 
                    NULL, " array not found" );

    /* Regression: comma newline plus value indentation controls multiline
     * append formatting. */
    {
        char format_file[L_tmpnam];
        t_toml_node *format_node;

        format_node = parse_inline_toml(
                "items = [ \"A\" ]\n",
                format_file);

        result = format_node ?
            toml_insert_array_value_by_index(format_node, "items",
                    "B", "        ", "\n", " ", -1) : -1;
        if (!result) {
            result = toml_insert_array_value_by_index(format_node, "items",
                    "C", "        ", "\n", " ", -1);
        }

        result = !result && !write_matches_text(format_node,
                "items = [ \"A\", \n"
                "        \"B\", \n"
                "        \"C\" ]\n");
        output_result ( result, "Array Multiline Append",
                "items", NULL, " multiline append formatting changed" );

        toml_free_node_tree(format_node);
        remove(format_file);
    }

    /* Regression: palette-style arrays can start below '[' and use one
     * separator space for same-row values while wrapping every eight values. */
    {
        char palette_file[L_tmpnam];
        t_toml_node *palette_node;
        t_toml_node *colors_node;
        t_toml_node *array_node;
        t_toml_node *end_node;
        char value[20];

        palette_node = parse_inline_toml(
                "name = \"test\"\n",
                palette_file);

        result = palette_node ?
            toml_start_array_with_value(palette_node, "colors", TOML_INT,
                    "0x000000", "", " ") : -1;

        colors_node = palette_node ? toml_find_node(palette_node, "colors") : NULL;
        array_node = colors_node ? colors_node->child : NULL;
        if (!result && array_node && array_node->type == TOML_ARRAY) {
            set_node_text(&array_node->to_newline, "\n");
            if (array_node->child && array_node->child->type != TOML_ARRAY_END)
                set_node_text(&array_node->child->indent_initial, "  ");
        }

        for (int i = 1; !result && i < 10; ++i) {
            sprintf(value, "0x%06x", i * 0x111111);
            result = toml_insert_array_value_by_index(palette_node, "colors",
                    value, (i % 8 == 0) ? "  " : "",
                    (i % 8 == 0) ? "\n" : NULL, "\n  ", -1);
        }

        end_node = find_array_end_node(array_node);
        if (end_node)
            set_node_text(&end_node->indent_initial, "\n  ");

        result = !result && !write_matches_text(palette_node,
                "name = \"test\"\n"
                "colors = [\n"
                "  0x000000, 0x111111, 0x222222, 0x333333, 0x444444, 0x555555, 0x666666, 0x777777, \n"
                "  0x888888, 0x999999\n"
                "  ]\n");
        output_result ( result, "Palette Array Layout",
                "colors", NULL, " palette row formatting changed" );

        toml_free_node_tree(palette_node);
        remove(palette_file);
    }

    /* Add table and keys */
    node_name = "testarray";
    result_node = toml_add_table ( node, node_name, TOML_TABLE_ARY, "" );
    output_result ( result_node ? 1 : 0, "Add Table Array", node_name, 
                    NULL, " add table failed" );
    if ( result_node ) {
        node_name = "file";
        result = toml_add_key ( result_node, node_name, TOML_DQ_STRING, 
                                "bigshaderfile", "", " " );
        output_result ( result ? 0 : 1, "Add Key", node_name, 
                        NULL, " add key failed" );

        tmp_node = toml_add_generic_node ( result_node,  TOML_NEWLINE, NULL, NULL );
        output_result ( tmp_node ? 1 : 0, "Add Newline", "", 
                        NULL, " add newline failed" );

        node_name = "testarray.child";
        result_node = toml_add_table ( result_node, node_name, TOML_TABLE_ARY, "  " );
        output_result ( result_node ? 1 : 0, "Add Child Table Array", node_name, 
                        NULL, " add child table failed" );

        node_name = "file";
        result = toml_add_key ( result_node, node_name, TOML_DQ_STRING, 
                                "paramshaderfile", "  ", " " );
        output_result ( result ? 0 : 1, "Add Key", node_name, 
                        NULL, " add key failed" );

    }

    /* Regression: a missing non-array table should be addable. */
    {
        char add_table_file[L_tmpnam];
        t_toml_node *add_table_node;
        t_toml_node *added_table;

        add_table_node = parse_inline_toml(
                "[filebrowser]\n"
                "w = 420\n",
                add_table_file);
        added_table = add_table_node ?
            toml_add_table(add_table_node, "debugger", TOML_TABLE, "") : NULL;
        output_result ( added_table ? 1 : 0, "Add Missing Table",
                "debugger", NULL, " add table failed" );

        toml_free_node_tree(add_table_node);
        remove(add_table_file);
    }

    /* Delete Table */
    node_name = "input.joysticks";
    result = toml_delete_node_and_children ( node, node_name );
    output_result ( result ? 0 : 1, "Delete Table Array", node_name, 
                    NULL, " delete table failed" );

    /* Delete Key */
    node_name = "input.types.paddles";
    result = toml_delete_node_and_children ( node, node_name );
    output_result ( result ? 0 : 1, "Delete Key", node_name, 
                    NULL, " delete key" );

    /* Regression: deleting the first sibling must preserve the remaining
     * sibling list rather than replacing it with the deleted node's children. */
    {
        char delete_table_file[L_tmpnam];
        char delete_table_output_file[L_tmpnam];
        char delete_table_expected_file[L_tmpnam];
        t_toml_node *delete_table_node;
        FILE *fp_expected, *fp_actual;

        delete_table_node = parse_inline_toml(
                "[alpha]\n"
                "a = \"one\"\n"
                "\n"
                "[beta]\n"
                "b = \"two\"\n",
                delete_table_file);
        result = delete_table_node ?
            toml_delete_node_and_children(delete_table_node, "alpha") : -1;

        make_temp_filename(delete_table_output_file,
                           sizeof(delete_table_output_file));
        make_temp_filename(delete_table_expected_file,
                           sizeof(delete_table_expected_file));
        if (!result) {
            toml_write_file(delete_table_node, delete_table_output_file);
            write_text_file(delete_table_expected_file,
                    "[beta]\n"
                    "b = \"two\"\n");
            fp_expected = fopen(delete_table_expected_file, "r");
            fp_actual = fopen(delete_table_output_file, "r");
            result = (!fp_expected || !fp_actual) ? -1 :
                compare_files(fp_expected, fp_actual);
            if (fp_expected) fclose(fp_expected);
            if (fp_actual) fclose(fp_actual);
        }
        output_result ( result ? 0 : 1, "Delete First Sibling",
                "alpha", NULL, " sibling list was lost" );

        toml_free_node_tree(delete_table_node);
        remove(delete_table_file);
        remove(delete_table_output_file);
        remove(delete_table_expected_file);
    }

    /* Delete Array value */
    node_name = "input.game.p0.5200.joystick.bottom_button";
    result =  toml_delete_array_value_by_index ( node, node_name, 1 );
    output_result ( result ? 0 : 1, "Delete Array Value", node_name, 
                    NULL, " delete array value" );

    /* Delete last Array value */
    node_name = "input.misc.exit";
    result =  toml_delete_array_value_by_index ( node, node_name, -1 );
    output_result ( result ? 0 : 1, "Delete Array Value", node_name, 
                    NULL, " delete array value" );

    /* Regression: deleting index 0 must delete a value, not a leading comma,
     * and appending to an emptied array must not emit a leading comma. */
    {
        char array_input_file[L_tmpnam];
        char array_output_file[L_tmpnam];
        char array_expected_file[L_tmpnam];
        t_toml_node *array_node;
        t_toml_node *debugger_node;
        FILE *fp_expected, *fp_actual;

        array_node = parse_inline_toml(
                "[debugger]\n"
                "breakpoints = [ \"A\", \"B\" ]\n",
                array_input_file);
        debugger_node = toml_find_node(array_node, "debugger");

        result = debugger_node ?
            toml_delete_array_value_by_index(debugger_node, "breakpoints", 0) : -1;
        result_value = debugger_node ?
            toml_get_array_value_by_index(debugger_node, "breakpoints", 0) : NULL;
        output_result ( !result && result_value && !strcmp(result_value, "B"),
                "Delete First Array Value", "debugger.breakpoints",
                result_value, " expected B" );

        result = debugger_node ?
            toml_delete_array_value_by_index(debugger_node, "breakpoints", 0) : -1;
        result_value = debugger_node ?
            toml_get_array_value_by_index(debugger_node, "breakpoints", 0) : NULL;
        output_result ( !result && !result_value,
                "Delete Last Remaining Array Value", "debugger.breakpoints",
                result_value, " expected empty array" );

        result = debugger_node ?
            toml_insert_array_value_by_index(debugger_node, "breakpoints",
                    "C", " ", " ", " ", -1) : -1;
        make_temp_filename(array_output_file, sizeof(array_output_file));
        make_temp_filename(array_expected_file, sizeof(array_expected_file));
        if (!result) {
            toml_write_file(array_node, array_output_file);
            write_text_file(array_expected_file,
                    "[debugger]\n"
                    "breakpoints = [ \"C\" ]\n");
            fp_expected = fopen(array_expected_file, "r");
            fp_actual = fopen(array_output_file, "r");
            result = (!fp_expected || !fp_actual) ? -1 :
                compare_files(fp_expected, fp_actual);
            if (fp_expected) fclose(fp_expected);
            if (fp_actual) fclose(fp_actual);
        }
        output_result ( result ? 0 : 1, "Append To Emptied Array",
                "debugger.breakpoints", NULL, " emitted leading comma" );

        toml_free_node_tree(array_node);
        remove(array_input_file);
        remove(array_output_file);
        remove(array_expected_file);
    }

    /* Parse new file after changes */
    make_temp_filename(temp_file_buf, sizeof(temp_file_buf));
    temp_file = temp_file_buf;
    toml_write_file ( node, temp_file );
    result_node = toml_parse_file ( temp_file );
    result = get_error_string ( err_str );
    output_result ( result ? 0 : 1, "Parse after changes", temp_file, 
                    NULL, err_str );
    toml_free_node_tree(result_node);

    printf ("Delete %s after examining it\n", temp_file);

    //remove ( temp_file );

    /*
     * Free Memory
     */
    toml_free_node_tree ( node );

    return failure;

} /* end main */
