#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);
}
