/**
 *
 * @file plugins/MatrixVisualizer/Parsers/Readers/Pastix.cpp
 *
 * @copyright 2008-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
 *                      Univ. Bordeaux. All rights reserved.
 *
 * @author Camille Ordronneau
 * @author Arthur Chevalier
 * @author Johnny Jazeix
 * @author Mathieu Faverge
 *
 * @date 2024-07-17
 */
#include "Pastix.hpp"

#include "../../Helper.hpp"
#include <iostream>
#include <cstdlib>
#include <ctype.h>

int pastix_read_symbol(FILE *stream, symbol_matrix_t *matrix) {
    int32_t versval;
    int32_t cblknum;
    int32_t bloknum;
    int32_t nodenbr;

    int32_t result = 0;

    result += pastix_read_int(stream, &versval);
    result += pastix_read_int(stream, &matrix->m_cblknbr);
    result += pastix_read_int(stream, &matrix->m_bloknbr);
    result += pastix_read_int(stream, &nodenbr);
    result += pastix_read_int(stream, &matrix->m_baseval);
    matrix->m_sndenbr = matrix->m_cblknbr;

    if ((result != 5) || (versval < -1) || (versval > 1) || (matrix->m_bloknbr < matrix->m_cblknbr) || (nodenbr < matrix->m_cblknbr)) {
        Helper::log(LogStatus::FATAL, "Loading symbol file, bad header !");

        return 1;
    }
    Helper::log(LogStatus::MESSAGE, "Version %d", versval);

    matrix->m_sndetab = (int32_t *)malloc((matrix->m_sndenbr + 1) * sizeof(int32_t));
    matrix->m_cblktab = (symbol_cblk_t *)malloc((matrix->m_cblknbr + 1) * sizeof(symbol_cblk_t));
    matrix->m_bloktab = (symbol_blok_t *)malloc(matrix->m_bloknbr * sizeof(symbol_blok_t));

    if ((matrix->m_sndetab == nullptr) || (matrix->m_cblktab == nullptr) || (matrix->m_bloktab == nullptr)) {
        Helper::log(LogStatus::FATAL, "Out of memory while allocating tabs !");

        return 1;
    }

    symbol_cblk_t *cblk = matrix->m_cblktab;
    int32_t *snde = matrix->m_sndetab;
    int32_t cblknbr = matrix->m_cblknbr;
    for (cblknum = 0; cblknum < cblknbr; ++cblknum, ++cblk, ++snde) {
        result = pastix_read_int(stream, &(cblk->m_fcolnum));
        result += pastix_read_int(stream, &(cblk->m_lcolnum));
        result += pastix_read_int(stream, &(cblk->m_bloknum));

        if ((result != 3) || (cblk->m_fcolnum > cblk->m_lcolnum)) {
            Helper::log(LogStatus::FATAL, "Bad input while reading cblk !");

            return 1;
        }

        cblk->m_color = 0.f;
        cblk->m_flags = 0;
        *snde = 0;
    }
    /* Extra cblk and supernode */
    cblk->m_fcolnum = cblk->m_lcolnum = nodenbr + matrix->m_baseval;
    cblk->m_bloknum = matrix->m_bloknbr + matrix->m_baseval;
    *snde = cblknum;

    symbol_blok_t *blok = matrix->m_bloktab;
    int32_t bloknbr = matrix->m_bloknbr;
    for (bloknum = 0; bloknum < bloknbr; ++bloknum, ++blok) {
        result = pastix_read_int(stream, &(blok->m_frownum));
        result += pastix_read_int(stream, &(blok->m_lrownum));
        result += pastix_read_int(stream, &(blok->m_fcblknm));
        if ((result != 3) || (blok->m_frownum > blok->m_lrownum)) {
            Helper::log(LogStatus::FATAL, "Bad input while reading blok %d !", bloknum);

            return 1;
        }

        blok->m_localization = 0;
        blok->m_flags = 0;
        if (versval == -1) {
            int32_t rk, rkmax;
            result = pastix_read_int(stream, &rk);
            result += pastix_read_int(stream, &rkmax);

            if (result != 2) {
                Helper::log(LogStatus::FATAL, "Bad input while reading blok %d ranks !", bloknum);
                return 1;
            }

            blok->m_color = (double)(rkmax - rk) / (double)rkmax;
        }
        else {
            blok->m_color = -1.f;
        }

        // Compatibility block
        {
            int32_t tmp;
            if ((versval == 0) && (pastix_read_int(stream, &tmp) != 1)) {
                Helper::log(LogStatus::FATAL, "Bad input while reading lefval !");
                return 1;
            }
        }
    }

    matrix->m_dof = 1;
    matrix->m_colsnbr = nodenbr;
    matrix->m_rowsnbr = nodenbr;

    Helper::log(LogStatus::MESSAGE,
                "Loaded header:\n"
                "  File Version:        %10d\n"
                "  Number of cblk:      %10d\n"
                "  Number of rows:      %10d\n"
                "  Number of blok:      %10d\n"
                "  Number of nodes:     %10d\n"
                "  Base value:          %10d\n",
                versval,
                matrix->m_cblknbr, matrix->m_rowsnbr,
                matrix->m_bloknbr, nodenbr,
                matrix->m_baseval);

    return 0;
}

int pastix_read_int(FILE *stream, int32_t *const value) {
    int32_t val;
    int sign;
    int car;

    sign = 0;
    for (;;) {
        car = getc(stream);
        if (isspace(car))
            continue;
        if ((car >= '0') && (car <= '9'))
            break;
        if (car == '-') {
            sign = 1;
            car = getc(stream);
            break;
        }
        if (car == '+') {
            car = getc(stream);
            break;
        }
        return (0);
    }

    if ((car < '0') || (car > '9')) {
        return (0);
    }
    val = car - '0';
    for (;;) {
        car = getc(stream);
        if ((car < '0') || (car > '9')) {
            ungetc(car, stream);
            break;
        }
        val = val * 10 + (car - '0');
    }

    // Maybe do: *value = (sign << 31 | val);
    *value = (sign != 0) ? (-val) : val;

    return (1);
}
