//////////////////////////////////////////////////////////////////////////////
//
//  This file is part of the Atari Machine Specific Library,
//  and is Copyright 1992 by Warwick W. Allison.
//
//  You are free to copy and modify these sources, provided you acknoledge
//  the origin by retaining this notice, and adhere to the conditions
//  described in the file COPYING.
//
//////////////////////////////////////////////////////////////////////////////

#include "FastCounter.h"
#include <DoubleBuffer.h>

CounterFont::CounterFont(short Height, short Plane=0)
{
	height=Height;

	for (shifts=0; 1<<shifts < Height; shifts++);

	Data=new short[111<<shifts];
	plane=Plane;
}

CounterFont::GetImages(Screen& S,short x,short y)
{
	short *From=(short*)S.Location()+((x>>4)<<2)+y*80; // LOW REZ ONLY *****
	int n;

	short *Blank=Data+(110<<shifts);
	for (int h=0; h<height; h++) {
		Blank[h]=From[h*80-4];
	}

	for (n=0; n<=9; n++) {
		short *D=Data+((n+100)<<shifts);
		for (int h=0; h<height; h++) {
			D[h]=*(From+80*h);
			if (!(n&1)) D[h]>>=8;
			D[h]&=0xFF;
			D[h]|=Blank[h]<<8;
		}
		if (n&1) From+=4;
	}

	for (n=0; n<=99; n++) {
		short *D=Data+(n<<shifts);
		short *S1=Data+((n/10+100)<<shifts);
		short *S2=Data+((n%10+100)<<shifts);
		for (int h=0; h<height; h++) {
			D[h]=((S1[h]&0xff)<<8)|(S2[h]&0xff);
		}
	}
}

CounterFont::Draw(short n,long Offset)
{
	short *At=(short*)Pages->Location()+Offset+plane;
	short *D=Data+(n<<shifts);
	for (int h=0; h<height; h++) {
		*At=*D;
		D++;
		At+=80;
	}
}

FastCounter::FastCounter(CounterFont* F,int x,int y,unsigned v=0,short digits=6)
	:	Font(F),
		Size((digits>>1)+(digits&1)),
		Digit(new unsigned short[Size]),
		Offset(((x>>4)<<2)+y*80+(Size-1)*4)
{
	Changed[0]=Size;
	Changed[1]=Size;
	Set(v);
}

FastCounter::~FastCounter()
{
	delete Digit;
}

void FastCounter::Draw()
{
	long O=Offset;
	short top=Changed[Pages->Pulse];

	for (int d=0; d<top; d++) {
		Font->Draw(Digit[d],O);
		O-=4;
	}
	Changed[Pages->Pulse]=0;
}

void FastCounter::Add(short Carry)
{
	unsigned short *D=Digit;

	int Ch=0;
	while (Carry && Ch<Size) {
		short C;
		if (LeadingZeroes || *D<100) {
			C=Carry+*D;
		} else {
			if (*D==110)
				C=Carry;
			else
				C=Carry+*D-100;
		}
		Carry=0;
		while (C>=1000) {
			C-=1000;
			Carry+=10;
		}
		while (C>=100) {
			C-=100;
			Carry++;
		}
		*D++=C;
		Ch++;
	}

	Changed[0]=Changed[0] >? Ch >? Changed[1];
	Changed[1]=Changed[0];

	if (!LeadingZeroes) {
		while (Ch>0 && *--D<10) {
			if (*D || Ch==1) {
				*D+=100;
				Ch=0;
			} else {
				*D=110;
			}
			Ch--;
		}
	}
}

void FastCounter::ZeroSuppression(bool on=TRUE)
{
	LeadingZeroes=!on;
	Changed[0]=Size;
	Changed[1]=Size;

	unsigned short *D=&Digit[Size-1];

	if (!LeadingZeroes) {
		int Ch=Size;
		while (Ch>0 && *--D<10) {
			if (*D || Ch==1) {
				*D+=100;
				Ch=0;
			} else {
				*D=110;
			}
			Ch--;
		}
	} else {
		int Ch=Size;
		while (Ch>0 && *--D>99) {
			if (*D<110) {
				*D-=100;
				Ch=0;
			} else {
				*D=0;
			}
			Ch--;
		}
	}
}

void FastCounter::Set(unsigned Carry)
{
	unsigned short *D=Digit;

	for (int d=0; d<Size; d++) {
		if (!LeadingZeroes && Carry<10) {
			if (Carry || !d) {
				*D++=Carry%10+100;
				Carry=0;
			} else {
				*D++=110;
			}
		} else {
			*D++=Carry%100;
			Carry/=100;
		}
	}
	Changed[0]=Size;
	Changed[1]=Size;
}

void FastCounter::MoveTo(short x,short y)
{
	Offset=((x>>4)<<3)+y/80;
	Changed[0]=Size;
	Changed[1]=Size;
}

FastCounter::operator int()
{
	int result=0;
	for (int d=Size; d>=0; d--) {
		if (LeadingZeroes || Digit[d]<100) {
			result=result*100+Digit[d];
		} else {
			if (Digit[d]==110) {	
				result=result*100;
			} else {
				result=result*100+(Digit[d]-100);
			}
		}
	}
	return result;
}

FastCounter::operator double()
{
	double result=0.0;
	for (int d=Size; d>=0; d--) {
		if (LeadingZeroes || Digit[d]<100) {
			result=result*100.0+(double)Digit[d];
		} else {
			if (Digit[d]==110) {	
				result=result*100.0;
			} else {
				result=result*100.0+(double)(Digit[d]-100);
			}
		}
	}
	return result;
}
