/* 'checkit_tiff' is a conformance checker for baseline TIFFs
*
* author: Andreas Romeyke, 2015-2022
* licensed under conditions of libtiff
* (see http://libtiff.maptools.org/misc.html)
*
*/


#include "check_renderer.h"
#include "ctstring.h"
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>

static const char delim = ',';
static const char  delim_string[2] = {
#ifdef __TINYC__
        ',', '\0'
#else
        delim,
        '\0'
#endif
};
static const char stringquote = '\\';
static const char stringcsv_open = '"';
static const char stringcsv_close = '"';


static bool is_num_string(string_t s) {
    for (unsigned int i = 0; i < s.len; i++) {
        if (
                (!isdigit(s.string[i]))
                && ( s.string[i] != '.' )
                ) {
            return false;
        }
    }
    return true;
}
static string_t quote_csv(string_t s) {
    if (s.len == 0) { return s;}
    if (is_num_string(s)) {return s;}
    /* we found text */
    unsigned int current_buf_size = s.len;
    char *buffer = calloc(s.len+3, sizeof(char)); /* textquotes plus '\0' */
    if (NULL == buffer) {
        perror("could not allocate memory");
        exit(EXIT_FAILURE);
    }
    unsigned int j = 0;
    buffer[j++] = stringcsv_open;
    for (unsigned int i = 0; i < s.len; i++) {
        /* realloc if needed */
        if (j > current_buf_size) {
#ifdef COMPAT_NEED_REALLOCARRAY
            char *newbuf = realloc(buffer, 2 * current_buf_size * sizeof(char));
#else
            char *newbuf = reallocarray(buffer, 2 * current_buf_size, sizeof(char));
#endif
            if (NULL == newbuf) {
                perror("could not allocate memory");
                exit(EXIT_FAILURE);
            }
            buffer = newbuf;
        }
        /* now quote if needed */
        if (s.string[i] == stringquote) {
            buffer[j++] = stringquote;
            buffer[j++] = s.string[i];
        } else if (s.string[i] == stringcsv_open) {
            buffer[j++] = stringquote;
            buffer[j++] = s.string[i];
        } else if (s.string[i] == stringcsv_close) {
            buffer[j++] = stringquote;
            buffer[j++] = s.string[i];
        } else if (s.string[i] == '\r') {
            buffer[j++] = ' ';
        } else if (s.string[i] == '\n') {
            buffer[j++] = ' ';
        } else {
            buffer[j++] = s.string[i];
        }
    }
    buffer[j++] = stringcsv_close;
    buffer[j] = 0;
//    fprintf(stderr,"string = >%s<\n", s.string);
//    fprintf(stderr, "quoted = >%s<\n", buffer);
    if (j == s.len) {
        free(buffer);
        return s;
    } else {
        if (!s.is_const) {
            //free(s.string);
        }
        s.string = buffer;
        s.len = j;
        return s;
    }
}
typedef enum {
    field_result,
    field_tag_mode,
    field_rule,
    field_expected,
    field_errordesc,
    field_value,
    field_lineno,
    field_end
} field_cycle_t;

static stringbuf_t render_table_header (stringbuf_t res) {
    // print header
    res = secstrcat_string(res, quote_csv(str("is_valid/file/summary")));
    res = secstrcat_cstr(res, delim_string);
    res = secstrcat_string(res, quote_csv(str("ifd/tag/filename")));
    res = secstrcat_cstr(res, delim_string);
    res = secstrcat_string(res, quote_csv(str("rule_description")));
    res = secstrcat_cstr(res, delim_string);
    res = secstrcat_string(res, quote_csv(str("expected_specific_values")));
    res = secstrcat_cstr(res, delim_string);
    res = secstrcat_string(res, quote_csv(str("errordescription")));
    res = secstrcat_cstr(res, delim_string);
    res = secstrcat_string(res, quote_csv(str("value_found")));
    res = secstrcat_cstr(res, delim_string);
    res = secstrcat_string(res, quote_csv(str("lineno_in_config")));
    res = secstrcat_cstr(res, "\n");
    return res;
}

static stringbuf_t render_table_line (stringbuf_t res, string_t fields[field_end] ) {
    /* check if first field set */
   if (fields[0].len > 0) {
        res = secstrcat_string(res, fields[0]);
        fields[0] = empty_str();
        for (int i = 1; i < field_end; i++) {
            res = secstrcat_cstr(res, delim_string);
            if (fields[i].len > 1) {
                res = secstrcat_string(res, fields[i]);
            }
            fields[i] = empty_str();
        }
        res = secstrcat_cstr(res, "\n");
    }
 return res;
}

stringbuf_t renderer_csv ( const retmsg_t * ret) {
    assert (ret != NULL);
    stringbuf_t res = empty_stringbuf();
    const retmsg_t * startp = ret;
    rc_tristate_t rc_is_valid = rc_undef;
    string_t fields[field_end];
    static bool header_already_rendered = false;
    if (header_already_rendered == false) {
        res = render_table_header(res);
        header_already_rendered = true;
    }
    for (int i=0; i<field_end; i++) {
        fields[i] = empty_str();
    }
    while (NULL != startp) {
      if (startp->rm_type == rm_is_valid) {rc_is_valid = rc_valid;}
      else if (startp->rm_type == rm_error || startp->rm_type == rm_hard_error) {rc_is_valid = rc_invalid;;}
        if ( check_if_quiet(startp, rc_is_valid) ) { /*do nothing */ }
        else {
            switch (startp->rm_type) {
                case rm_rule:
                    fields[field_rule] = quote_csv(startp->rm_msg);
                    break;
                case rm_tag:
                case rm_mode:
                    fields[field_tag_mode] = quote_csv(startp->rm_msg);
                    break;
                case rm_value:
                    fields[field_value] = quote_csv(startp->rm_msg);
                    break;
                case rm_expected:
                    fields[field_expected] = quote_csv(startp->rm_msg);
                    break;
                case rm_hard_error:
                    fields[field_result] = quote_csv(str("hard error"));
                    break;
                case rm_error:
                    fields[field_result] = quote_csv(str("error"));
                    break;
                case rm_error_description:
                    fields[field_errordesc] = quote_csv(startp->rm_msg);
                    break;
                case rm_warning:
                    fields[field_result] = quote_csv(str("warning"));
                    break;
                case rm_logicalor_error:
                    fields[field_result] = quote_csv(str("logical error"));
                    break;
                case rm_file:
                    fields[field_result] = quote_csv(str("file"));
                    fields[field_tag_mode] = quote_csv(startp->rm_msg);
                    res = render_table_line(res, fields);
                    rc_is_valid = rc_undef;;
                    break;
                case rm_lineno:
                    fields[field_lineno] = quote_csv(startp->rm_msg);
                    break;
                case rm_endrule:
                case rm_endtiff:
                    rc_is_valid = rc_undef;;
                    res = render_table_line(res, fields);
                    break;
                case rm_is_valid:
                    fields[field_result] = quote_csv(str("valid"));
                    rc_is_valid = rc_valid;
                    break;
                case rm_count_valid:
                case rm_count_invalid:
                case rm_summary_valid:
                case rm_summary_invalid:
                    break;

                default:
                    res = secstrcat_string(res, startp->rm_msg);
            }
        }
        startp = startp->next;
    }
    res=secstrcat_cstr( res, "\n");
    return res;
}
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab :*/
