/******************************************************************
 * DPUSER - The Next Generation
 *
 * File:     libfits/fits_mem.cpp
 * Purpose:  Fits class methods for memory allocation
 * Author:   Thomas Ott
 *
 * Revision history
 * ================
 * 13.08.1999: file created
 ******************************************************************/

#include "fits.h"

/*
 * Initialize all memory pointers
 */

void Fits::initializeMemory() {
	int i;

	FileName = (char *)calloc(257, sizeof(char));
    columnName = (char *)calloc(73, sizeof(char));
	dataptr = NULL;
	setCasts();
    n_elements = 0;
	for (i = 0; i <= MAXNAXIS; i++) {
        naxis[i] = 0;
	}
	bscale = 1.0;
	bzero = 0.0;
//	type = IMAGE;
//	bitpix = 0;
	filebits = membits = R4;
    bytesPerPixel = 0;
	bytesAllocated = 0;
    extensionType = UNKNOWN;
}

/*!
Update all casts to memory
*/

void Fits::setCasts() {
//	data = (float *)dataptr;
	cdata = (dpCOMPLEX *)dataptr;
	r8data = (double *)dataptr;
	r4data = (float *)dataptr;
	i1data = (unsigned char *)dataptr;
	i2data = (short *)dataptr;
	i4data = (int *)dataptr;
    i8data = (long long *)dataptr;
}

/*!
(Re)allocate of memory and update the pointers to data.
*/

bool Fits::allocateMemory(dpint64 s) {
	dpint64 memNecessary;
	int i;

	bytesPerPixel = abs(membits) / 8;
	n_elements = 1;
	
//	bitpix = membits;
	for (i = 1; i <= naxis[0]; i++) n_elements *= naxis[i];
	memNecessary = bytesPerPixel * n_elements;
	if (s) {
		memNecessary = s;
		n_elements = s / bytesPerPixel;
	}
// Paranoia, just to make sure we have no dangling NULL pointer
    if (memNecessary == 0) memNecessary = 2 * sizeof(double);

    if (bytesAllocated == memNecessary) return TRUE;

	if ((dataptr = realloc(dataptr, memNecessary)) == NULL) {
		initializeMemory();
		if (memNecessary > 0)
			return fits_error("Could not allocate enough memory");
		else
			return TRUE;
	}
	setCasts();
	bytesAllocated = memNecessary;
	return TRUE;
}

/*
 * Change the type of data we are handling
 */

bool Fits::setType(FitsBitpix newbits, double nbscale, double nbzero) {
    dpint64 i;
    dpint64 n;
	FitsBitpix oldbits = membits;

// return if nothing to be done
	if (membits == newbits) return TRUE;

	membits = newbits;
	if (abs(oldbits) < abs(membits)) {
		if (!allocateMemory()) return FALSE;
		setCasts();
	}

	switch (newbits) {
		case I1: switch (oldbits) {
			case C16: for (n = 0; n < n_elements; n++) i1data[n] = (unsigned char)((cdata[n].r - nbzero) / nbscale); break;
			case R8:  for (n = 0; n < n_elements; n++) i1data[n] = (unsigned char)((r8data[n] - nbzero) / nbscale); break;
			case R4:  for (n = 0; n < n_elements; n++) i1data[n] = (unsigned char)(((double)r4data[n] - nbzero) / nbscale); break;
            case I8:  for (n = 0; n < n_elements; n++) i1data[n] = (unsigned char)(((double)i8data[n] * bscale + bzero - nbzero) / nbscale); break;
            case I4:  for (n = 0; n < n_elements; n++) i1data[n] = (unsigned char)(((double)i4data[n] * bscale + bzero - nbzero) / nbscale); break;
			case I2:  for (n = 0; n < n_elements; n++) i1data[n] = (unsigned char)(((double)i2data[n] * bscale + bzero - nbzero) / nbscale); break;
			default: break;
		}
			bzero = nbzero;
			bscale = nbscale;
			break;
		case I2: switch (oldbits) {
			case C16: for (n = 0; n < n_elements; n++) i2data[n] = (short)((cdata[n].r - nbzero) / nbscale); break;
			case R8:  for (n = 0; n < n_elements; n++) i2data[n] = (short)((r8data[n] - nbzero) / nbscale); break;
			case R4:  for (n = 0; n < n_elements; n++) i2data[n] = (short)(((double)r4data[n] - nbzero) / nbscale); break;
            case I8:  for (n = 0; n < n_elements; n++) i2data[n] = (short)(((double)i8data[n] * bscale + bzero - nbzero) / nbscale); break;
            case I4:  for (n = 0; n < n_elements; n++) i2data[n] = (short)(((double)i4data[n] * bscale + bzero - nbzero) / nbscale); break;
            case I1:  for (i = n_elements - 1; i >= 0; i--) i2data[i] = (short)(((double)i1data[i] * bscale + bzero - nbzero) / nbscale); break;
			default: break;
		}
			bzero = nbzero;
			bscale = nbscale;
			break;
		case I4: switch (oldbits) {
			case C16: for (n = 0; n < n_elements; n++) i4data[n] = (int)((cdata[n].r - nbzero) / nbscale); break;
			case R8:  for (n = 0; n < n_elements; n++) i4data[n] = (int)((r8data[n] - nbzero) / nbscale); break;
			case R4:  for (n = 0; n < n_elements; n++) i4data[n] = (int)(((double)r4data[n] - nbzero) / nbscale); break;
            case I8:  for (n = 0; n < n_elements; n++) i4data[n] = (int)(((double)i8data[n] * bscale + bzero - nbzero) / nbscale); break;
            case I2:  for (i = n_elements - 1; i >= 0; i--) i4data[i] = (int)(((double)i2data[i] * bscale + bzero - nbzero) / nbscale); break;
            case I1:  for (i = n_elements - 1; i >= 0; i--) i4data[i] = (int)(((double)i1data[i] * bscale + bzero - nbzero) / nbscale); break;
			default: break;
		}
			bzero = nbzero;
			bscale = nbscale;
			break;
        case I8: switch (oldbits) {
            case C16: for (n = 0; n < n_elements; n++) i8data[n] = (long long)((cdata[n].r - nbzero) / nbscale); break;
            case R8:  for (n = 0; n < n_elements; n++) i8data[n] = (long long)((r8data[n] - nbzero) / nbscale); break;
            case R4:  for (i = n_elements - 1; i >= 0; i--) i8data[i] = (long long)(((double)r4data[i] - nbzero) / nbscale); break;
            case I4:  for (i = n_elements - 1; i >= 0; i--) i8data[i] = (long long)(((double)i4data[i] * bscale + bzero - nbzero) / nbscale); break;
            case I2:  for (i = n_elements - 1; i >= 0; i--) i8data[i] = (long long)(((double)i2data[i] * bscale + bzero - nbzero) / nbscale); break;
            case I1:  for (i = n_elements - 1; i >= 0; i--) i8data[i] = (long long)(((double)i1data[i] * bscale + bzero - nbzero) / nbscale); break;
            default: break;
        }
            bzero = nbzero;
            bscale = nbscale;
            break;
        case R4: switch (oldbits) {
			case C16: for (n = 0; n < n_elements; n++) r4data[n] = (float)(cdata[n].r); break;
			case R8:  for (n = 0; n < n_elements; n++) r4data[n] = (float)r8data[n]; break;
            case I8:  for (n = 0; n < n_elements; n++) r4data[n] = (float)(i8data[n] * bscale + bzero); break;
            case I4:  for (n = 0; n < n_elements; n++) r4data[n] = (float)(i4data[n] * bscale + bzero); break;
            case I2:  for (i = n_elements - 1; i >= 0; i--) r4data[i] = (float)(i2data[i] * bscale + bzero); break;
            case I1:  for (i = n_elements - 1; i >= 0; i--) r4data[i] = (float)(i1data[i] * bscale + bzero); break;
			default: break;
		}
			bzero = 0.0;
			bscale = 1.0;
			break;
		case R8: switch (oldbits) {
			case C16: for (n = 0; n < n_elements; n++) r8data[n] = cdata[n].r; break;
            case R4:  for (i = n_elements - 1; i >= 0; i--) r8data[i] = (double)r4data[i]; break;
            case I8:  for (n = 0; n < n_elements; n++) r8data[n] = (double)i8data[n] * bscale + bzero; break;
            case I4:  for (i = n_elements - 1; i >= 0; i--) r8data[i] = (double)i4data[i] * bscale + bzero; break;
            case I2:  for (i = n_elements - 1; i >= 0; i--) r8data[i] = (float)i2data[i] * bscale + bzero; break;
            case I1:  for (i = n_elements - 1; i >= 0; i--) r8data[i] = (float)i1data[i] * bscale + bzero; break;
			default: break;
		}
			bzero = 0.0;
			bscale = 1.0;
			break;
		case C16: switch (oldbits) {
            case R8: for (i = n_elements - 1; i >= 0; i--) {
				cdata[i].r = r8data[i];
				cdata[i].i = 0.0;
			}
				break;
            case R4: for (i = n_elements - 1; i >= 0; i--) {
				cdata[i].r = (double)r4data[i];
				cdata[i].i = 0.0;
			}
				break;
            case I8: for (i = n_elements - 1; i >= 0; i--) {
                cdata[i].r = (double)i8data[i] * bscale + bzero;
                cdata[i].i = 0.0;
            }
                break;
            case I4: for (i = n_elements - 1; i >= 0; i--) {
				cdata[i].r = (double)i4data[i] * bscale + bzero;
				cdata[i].i = 0.0;
			}
				break;
            case I2: for (i = n_elements - 1; i >= 0; i--) {
				cdata[i].r = (float)i2data[i] * bscale + bzero;
				cdata[i].i = 0.0;
			}
				break;
            case I1: for (i = n_elements - 1; i >= 0; i--) {
				cdata[i].r = (float)i1data[i] * bscale + bzero;
				cdata[i].i = 0.0;
			}
				break;
			default: break;
		}
			bzero = 0.0;
			bscale = 1.0;
			break;
		default: break;
	}
	if (abs(oldbits) > abs(membits)) {
		if (!allocateMemory()) return FALSE;
		setCasts();
	}

	return TRUE;
}

FitsBitpix Fits::getType() {
    return membits;
}
