/*
 *  Xfcalc version 1.1 - A multi-base calculator for X
 *  Copyright (C) 2026 Christopher John Purnell
 *                     cjp@lost.org.uk
 *
 *  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.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not see <https://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <stdint.h>
#include <math.h>

#include "calc.h"

char bra_str[3];
char num_str[DIGITS + 7];

double value = 0.0;
double mem_val = 0.0;
double drg = M_PI/180.0;
double base = 10.0;

int edit = 0;
int braket = 0;
int tok_ptr, num_ptr = 0;
char last = 0;
char tok_stack[43];
double num_stack[32] = { 0.0 };

const char digit[16] = "0123456789ABCDEF";

void set_value(double v)
{
    value = v;
    last = 1;
    redraw();
}

void redraw(void)
{
    char *str = bra_str;
    double num = value;

    if(braket)
    {
	*(str++) = '(';
	*(str++) = digit[braket];
    }
    *str = 0;
    str = num_str;

    if(isnan(num))
    {
	strcpy(str, "NaN");
    }
    else
    {
	if(num < 0.0)
	{
	    num = -num;
	    *(str++) = '-';
	}

	if(isinf(num))
	{
	    strcpy(str, "Inf");
	}
	else if(num <= 0.0)
	{
	    strcpy(str, "0");
	}
	else
	{
	    char tmp[DIGITS];
	    double min = pow(base, (double)(DIGITS - 1));
	    double max = min * base;
	    double rn = round(num);
	    int e = DIGITS - 1;
	    int d = 0;
	    int i;
	    int64_t n;
	    int ibase;
	    char *ptr;

	    for(i = 256; i > 0 && rn <= min; i >>= 1)
	    {
		int j = i;
		double p = pow(base, (double)j);
		double tn, tr;

		if(isinf(p))
		{
		    j >>= 1;
		    p = pow(base, (double)j);
		}

		tn = num * p;
		tr = round(tn);

		if(tr < max)
		{
		    num = tn;
		    rn = tr;
		    e -= j;
		}
	    }

	    for(i = 256; i > 0 && rn >= max; i >>= 1)
	    {
		double p = pow(base, (double)-i);
		double tn = num * p;
		double tr = round(tn);

		if(tr >= min)
		{
		    num = tn;
		    rn = tr;
		    e += i;
		}
	    }

	    ptr = str + 1;
	    if(e < DIGITS && e > -5)
	    {
		if(e < 0)
		{
		    num *= pow(base, (double)e);
		    rn = round(num);
		}
		else
		{
		    d = e;
		    ptr += d;
		}

		e = 0;
	    }

	    n = (int64_t)rn;
	    ibase = (int)base;

	    for(i = DIGITS - 1; i >= 0; i--)
	    {
		tmp[i] = n % ibase;
		n /= ibase;
	    }

	    for(i = 0; i < DIGITS; i++)
	    {
		int c = tmp[i];
		*(str++) = digit[c];

		if(c && str > ptr) ptr = str;
		if(i == d) *(str++) = '.';
	    }

	    if(e)
	    {
		*(ptr++) = e < 0 ? e = -e, '-' : '+';

		for(i = 2; i >= 0; i--)
		{
		    tmp[i] = e % ibase;
		    e /= ibase;
		}

		i = 0;
		while(i < 2 && !tmp[i]) i++;
		for(; i < 3; i++)
		{
		    int c = tmp[i];
		    *(ptr++) = digit[c];
		}
	    }

	    *ptr = 0;
	}
    }

    refresh();
}

void start_edit(char c)
{
    edit = ED_NUM;
    edit_len = (c == '0') ? 1 : 2;

    num_str[0] = ' ';
    num_str[1] = c;
    num_str[2] = 0;
}

void stop_edit(void)
{
    if(edit)
    {
	char c, a, *str = num_str;
	double v = 0.0, e;

	last = 1;
	edit = 0;

	while((c = *(++str)))
	{
	    if(c == '.' || c == '+' || c == '-')
		break;

	    c -= '0';
	    if(c > 9) c -= 'A'-('0'+10);
	    v *= base;
	    v += (double)c;
	}

	if(c)
	{
	    if(c == '.')
	    {
		e = 1.0;

		while((c = *(++str)))
		{
		    if(c == '.' || c == '+' || c == '-')
			break;

		    c -= '0';
		    if(c > 9) c -= 'A'-('0'+10);
		    v *= base; e *= base;
		    v += (double)c;
		}

		v /= e;
	    }

	    if(c)
	    {
		e = 0.0;

		while((a = *(++str)))
		{
		    a -= '0';
		    if(a > 9) a -= 'A'-('0'+10);
		    e *= base;
		    e += (double)a;
		}

		if(c == '-')
		    e = -e;

		v *= pow(base, e);
	    }
	}

	if(num_str[0] == '-')
	    v = -v;

	value = v;
    }
}
