/*
 * ReactOS Calc (Utility functions for GMP/MPFR engine)
 *
 * Copyright 2007-2017, Carlo Bramini
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "calc.h"

void prepare_rpn_result_2(calc_number_t *rpn, TCHAR *buffer, int size, int base)
{
    char   temp[1024];
    char  *ptr, *dst;
    int    width, max_ld_width;
    unsigned long int n, q;
    mpz_t   zz;
    mpf_t   ff;

    mpz_init(zz);
    mpf_init(ff);
    mpfr_get_z(zz, rpn->mf, MPFR_DEFAULT_RND);
    mpfr_get_f(ff, rpn->mf, MPFR_DEFAULT_RND);

    switch (base) {
    case IDC_RADIO_HEX:
        gmp_sprintf(temp, "%ZX", zz);
        break;
    case IDC_RADIO_DEC:
        /*
         * The output display is much shorter in standard mode,
         * so I'm forced to reduce the precision here :(
         */
        if (calc.layout == CALC_LAYOUT_STANDARD)
            max_ld_width = 16;
        else
            max_ld_width = 64;

        /* calculate the width of integer number */
        if (mpf_sgn(ff) == 0)
            width = 1;
        else {
            mpfr_t t;
            mpfr_init(t);
            mpfr_abs(t, rpn->mf, MPFR_DEFAULT_RND);
            mpfr_log10(t, t, MPFR_DEFAULT_RND);
            width = 1 + mpfr_get_si(t, MPFR_DEFAULT_RND);
            mpfr_clear(t);
        }
        if (calc.sci_out == TRUE || width > max_ld_width || width < -max_ld_width)
            ptr = temp + gmp_sprintf(temp, "%*.*#Fe", 1, max_ld_width, ff);
        else {
            ptr = temp + gmp_sprintf(temp, "%#*.*Ff", width, ((max_ld_width-width-1)>=0) ? max_ld_width-width-1 : 0, ff);
            dst = strchr(temp, '.');
            while (--ptr > dst)
                if (*ptr != '0')
                    break;

            /* put the string terminator for removing the final '0' (if any) */
            ptr[1] = '\0';
            /* check if the number finishes with '.' */
            if (ptr == dst)
                /* remove the dot (it will be re-added later) */
                ptr[0] = '\0';
        }
        break;
    case IDC_RADIO_OCT:
        gmp_sprintf(temp, "%Zo", zz);
        break;
    case IDC_RADIO_BIN:
        /* if the number is zero, just write 0 ;) */
        if (rpn_is_zero(rpn)) {
            temp[0] = _T('0');
            temp[1] = _T('\0');
            break;
        }
        /* repeat until a bit set to '1' is found */
        n = 0;
        do {
            q = mpz_scan1(zz, n);
            if (q == ULONG_MAX)
                break;
            while (n < q)
                temp[n++] = '0';
            temp[n++] = '1';
        } while (1);
        /* now revert the string into TCHAR buffer */
        for (q=0; q<n; q++)
            buffer[n-q-1] = (temp[q] == '1') ? _T('1') : _T('0');
        buffer[n] = _T('\0');

        mpz_clear(zz);
        mpf_clear(ff);
        return;
    }
    mpz_clear(zz);
    mpf_clear(ff);
    StringCchPrintf(buffer, size, _T("%hs"), temp);
}

void convert_text2number_2(calc_number_t *a)
{
    int base;
#ifdef UNICODE
    int sz;
    char *temp;
#endif

    switch (calc.base) {
    case IDC_RADIO_HEX: base = 16; break;
    case IDC_RADIO_DEC: base = 10; break;
    case IDC_RADIO_OCT: base = 8; break;
    case IDC_RADIO_BIN: base = 2; break;
    default: return;
    }
#ifdef UNICODE
/*
 * libmpfr and libgmp accept only ascii chars.
 */
    sz = WideCharToMultiByte(CP_ACP, 0, calc.buffer, -1, NULL, 0, NULL, NULL);
    if (!sz)
        return;
    temp = (char *)_alloca(sz);
    sz = WideCharToMultiByte(CP_ACP, 0, calc.buffer, -1, temp, sz, NULL, NULL);
    mpfr_strtofr(a->mf, temp, NULL, base, MPFR_DEFAULT_RND);
#else
    mpfr_strtofr(a->mf, calc.buffer, NULL, base, MPFR_DEFAULT_RND);
#endif
}

void convert_real_integer(unsigned int base)
{
    switch (base) {
    case IDC_RADIO_DEC:
        break;
    case IDC_RADIO_OCT:
    case IDC_RADIO_BIN:
    case IDC_RADIO_HEX:
        if (calc.base == IDC_RADIO_DEC) {
            mpfr_trunc(calc.code.mf, calc.code.mf);
            apply_int_mask(&calc.code);
        }
        break;
    }
}
