/** @file
    Source code style checks.

    Copyright (C) 2019 by Christian Zuckschwerdt <zany@triq.net>

    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.
*/

// Run with: make CTEST_OUTPUT_ON_FAILURE=1 test

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define MAX_LEN 1024

/// Source code file style checks.
/// Check that there are no long lines.
/// Check that there are no CRLF endings.
/// Check that there are no mixed tabs/spaces.
static int style_check(char *path)
{
    char *strict = strstr(path, "/devices/");

    FILE *fp = fopen(path, "r");
    assert(fp);
    if (!fp)
        exit(EXIT_FAILURE);

    int read_errors = 0;
    int long_errors = 0;
    int crlf_errors = 0;
    int tabs_errors = 0;
    int trailing_errors = 0;
    int memc_errors = 0;
    int funbrace_errors = 0;

    int leading_tabs = 0;
    int leading_spcs = 0;

    int use_stdout = 0;
    int use_printf = 0;

    int need_cond = 0;

    char str[MAX_LEN];
    while (fgets(str, MAX_LEN, fp)) {
        int len = strlen(str);
        if (len <= 0) {
            read_errors++;
            continue;
        }
        if (len >= MAX_LEN - 1) {
            long_errors++;
        }
        if (str[len - 1] == '\r' || (len > 1 && str[len - 2] == '\r')) {
            crlf_errors++;
        }

        if (str[0] == '\t') {
            leading_tabs++;
        }
        if (len >= 4 && str[0] == ' ' && str[1] == ' ' && str[2] == ' ' && str[3] == ' ') {
            leading_spcs++;
        }
        if (len > 1 && (str[len - 2] == ' ' || str[len - 2] == '\t')) {
            trailing_errors++;
        }
        if (strstr(str, "(r_device *decoder") && str[len - 2] == '{') {
            funbrace_errors++;
        }

        if (strstr(str, "stdout")) {
            use_stdout++;
        }
        char *p;
        if ((p = strstr(str, "printf"))) {
            if (p == str || p[-1] < '_'|| p[-1] > 'z') {
                use_printf++;
            }
        }
        if (need_cond && !strstr(str, "if (!")) {
            // we had an alloc but no check on the following line
            memc_errors++;
        }
        need_cond = 0;
        if (strstr(str, "alloc(") && !strstr(str, "alloc()")) {
            need_cond++;
        }
        if (strstr(str, "strdup(") && !strstr(str, "strdup()")) {
            need_cond++;
        }
    }
    fclose(fp);
    if (leading_tabs && leading_spcs) {
        tabs_errors = leading_tabs > leading_spcs ? leading_spcs : leading_tabs;
    }

    if (read_errors)
        printf("File \"%s\" has %d READ errors.\n", path, read_errors);
    if (long_errors)
        printf("File \"%s\" has %d LONG line errors.\n", path, long_errors);
    if (crlf_errors)
        printf("File \"%s\" has %d CRLF errors.\n", path, crlf_errors);
    if (tabs_errors)
        printf("File \"%s\" has %d MIXED tab/spaces errors.\n", path, tabs_errors);
    if (trailing_errors)
        printf("File \"%s\" has %d TRAILING whitespace errors.\n", path, trailing_errors);
    if (memc_errors)
        printf("File \"%s\" has %d ALLOC check errors.\n", path, memc_errors);
    if (funbrace_errors)
        printf("File \"%s\" has %d BRACE function on newline error.\n", path, funbrace_errors);
    if (leading_tabs)
        printf("File \"%s\" has %d TAB indented lines.\n", path, leading_tabs);
    if (strict && use_stdout)
        printf("File \"%s\" has %d STDOUT lines.\n", path, use_stdout);
    if (strict && use_printf)
        printf("File \"%s\" has %d PRINTF lines.\n", path, use_printf);

    return read_errors + long_errors + crlf_errors + tabs_errors + leading_tabs + trailing_errors
            + funbrace_errors + (strict ? use_stdout + use_printf : 0) + memc_errors;
}

int main(int argc, char *argv[])
{
    int failed = 0;
    for (int i = 1; i < argc; ++i) {
        failed += style_check(argv[i]);
    }
    exit(!!failed);
}
