/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Declarations common within but private to the DiskImg library.
 *
 * External code should not include this.
 */
#ifndef __DISK_IMG_PRIV__
#define __DISK_IMG_PRIV__

#include "DiskImgDetail.h"
#include <errno.h>
#include <assert.h>
// "GenericFD.h" included at end

using namespace DiskImgLib;		// make life easy for all internal code

namespace DiskImgLib {

#ifndef _DEBUG_LOG
//# define _DEBUG_LOG		/* define this to force log msgs in non-debug build */
#endif

#if defined(_DEBUG) || defined(_DEBUG_LOG)
# define _DEBUG_MSGS
#endif

/*
 * Win32-style debug message macros.
 */
#ifdef _DEBUG_MSGS
#define WMSG0(fmt) \
	Global::PrintDebugMsg(__FILE__, __LINE__, fmt)
#define WMSG1(fmt, arg0) \
	Global::PrintDebugMsg(__FILE__, __LINE__, fmt, arg0)
#define WMSG2(fmt, arg0, arg1) \
	Global::PrintDebugMsg(__FILE__, __LINE__, fmt, arg0, arg1)
#define WMSG3(fmt, arg0, arg1, arg2) \
	Global::PrintDebugMsg(__FILE__, __LINE__, fmt, arg0, arg1, arg2)
#define WMSG4(fmt, arg0, arg1, arg2, arg3) \
	Global::PrintDebugMsg(__FILE__, __LINE__, fmt, arg0, arg1, arg2, arg3)
#define WMSG5(fmt, arg0, arg1, arg2, arg3, arg4) \
	Global::PrintDebugMsg(__FILE__, __LINE__, fmt, arg0, arg1, arg2, arg3, arg4)
#else
#define WMSG0(fmt) ((void) 0)
#define WMSG1(fmt, arg0) ((void) 0)
#define WMSG2(fmt, arg0, arg1) ((void) 0)
#define WMSG3(fmt, arg0, arg1, arg2) ((void) 0)
#define WMSG4(fmt, arg0, arg1, arg2, arg3) ((void) 0)
#define WMSG5(fmt, arg0, arg1, arg2, arg3, arg4) ((void) 0)
#endif

/* put this in to break on interesting events when built debug */
#if defined(_DEBUG)
# define DebugBreak() { assert(false); }
#else
# define DebugBreak() ((void) 0)
#endif

/*
 * Standard goodies.
 */
#define NELEM(x) ((int) (sizeof(x) / sizeof(x[0])))

#define nil NULL

#define ErrnoOrGeneric() (errno != 0 ? (DIError) errno : kDIErrGeneric)


/* filename manipulation functions */
const char* FilenameOnly(const char* pathname, char fssep);
const char* FindExtension(const char* pathname, char fssep);
char* StrcpyNew(const char* str);

/* get/set integer values out of a memory buffer */
unsigned short GetShortLE(const unsigned char* buf);
unsigned long GetLongLE(const unsigned char* buf);
unsigned short GetShortBE(const unsigned char* buf);
unsigned long GetLongBE(const unsigned char* buf);
unsigned long Get24BE(const unsigned char* ptr);
void PutShortLE(unsigned char* ptr, unsigned short val);
void PutLongLE(unsigned char* ptr, unsigned long val);
void PutShortBE(unsigned char* ptr, unsigned short val);
void PutLongBE(unsigned char* ptr, unsigned long val);

/* little-endian read/write, for file headers (mainly 2MG and DC42) */
DIError ReadShortLE(GenericFD* pGFD, short* pBuf);
DIError ReadLongLE(GenericFD* pGFD, long* pBuf);
DIError WriteShortLE(FILE* fp, unsigned short val);
DIError WriteLongLE(FILE* fp, unsigned long val);
DIError WriteShortLE(GenericFD* pGFD, unsigned short val);
DIError WriteLongLE(GenericFD* pGFD, unsigned long val);
DIError WriteShortBE(GenericFD* pGFD, unsigned short val);
DIError WriteLongBE(GenericFD* pGFD, unsigned long val);

#ifdef _WIN32
/* Windows helpers */
DIError LastErrorToDIError(void);
bool IsWin9x(void);
#endif


/*
 * Provide access to a buffer of data as if it were a circular buffer.
 * Access is through the C array operator ([]).
 *
 * This DOES NOT own the array it is handed, and will not try to
 * free it.
 */
class CircularBufferAccess {
public:
    CircularBufferAccess(unsigned char* buf, long len) :
        fBuf(buf), fLen(len)
		{ assert(fLen > 0); assert(fBuf != nil); }
    CircularBufferAccess(const unsigned char* buf, long len) :
        fBuf(const_cast<unsigned char*>(buf)), fLen(len)
		{ assert(fLen > 0); assert(fBuf != nil); }
    ~CircularBufferAccess(void) {}

    /*
     * Be circular.  Assume that we won't stray far past the end, so
     * it's cheaper to subtract than mod.
     */
    unsigned char& operator[](int idx) const {
        if (idx < 0) {
            assert(false);
        }
        while (idx >= fLen)
            idx -= fLen;
        return fBuf[idx];
    }

    //unsigned char* GetPointer(int idx) const {
    //    while (idx >= fLen)
    //        idx -= fLen;
    //    return &fBuf[idx];
    //}

    int Normalize(int idx) const {
        while (idx >= fLen)
            idx -= fLen;
        return idx;
    }

    long GetSize(void) const {
        return fLen;
    }

private:
    unsigned char*  fBuf;
    long            fLen;
};

/*
 * Manage an output buffer into which we write one bit at a time.
 *
 * Bits fill in from the MSB to the LSB.  If we write 10 bits, the
 * output buffer will look like this:
 *
 *	xxxxxxxx xx000000
 *
 * Call WriteBit() repeatedly.  When done, call Finish() to write any pending
 * data and return the number of bits in the buffer.
 */
class BitOutputBuffer {
public:
	/* pass in the output buffer and the output buffer's size */
	BitOutputBuffer(unsigned char* buf, int size) {
		fBufStart = fBuf = buf;
		fBufSize = size;
		fBitMask = 0x80;
		fByte = 0;
		fOverflow = false;
	}
	virtual ~BitOutputBuffer(void) {}

	/* write a single bit */
	void WriteBit(int val) {
		if (fBuf - fBufStart >= fBufSize) {
			if (!fOverflow) {
				WMSG0("Overran bit output buffer\n");
				DebugBreak();
				fOverflow = true;
			}
			return;
		}

		if (val)
			fByte |= fBitMask;
		fBitMask >>= 1;
		if (fBitMask == 0) {
			*fBuf++ = fByte;
			fBitMask = 0x80;
			fByte = 0;
		}
	}

	/* flush pending bits; returns length in bits (or -1 on overrun) */
	int Finish(void) {
		int outputBits;

		if (fOverflow)
			return -1;

		outputBits = (fBuf - fBufStart) * 8;

		if (fBitMask != 0x80) {
			*fBuf++ = fByte;

			assert(fBitMask != 0);
			while (fBitMask != 0x80) {
				outputBits++;
				fBitMask <<= 1;
			}
		}
		return outputBits;
	}

private:
	unsigned char*	fBufStart;
	unsigned char*	fBuf;
	int				fBufSize;
	unsigned char	fBitMask;
	unsigned char	fByte;
	bool			fOverflow;
};

/*
 * Extract data from the buffer one bit or one byte at a time.
 */
class BitInputBuffer {
public:
	BitInputBuffer(const unsigned char* buf, int bitCount) {
		fBufStart = fBuf = buf;
		fBitCount = bitCount;
		fCurrentBit = 0;
		fBitPosn = 7;
		fBitsConsumed = 0;
	}
	virtual ~BitInputBuffer(void) {}

	/*
	 * Get the next bit.  Returns 0 or 1.
	 *
	 * If we wrapped around to the start of the buffer, and "pWrap" is
	 * non-null, set "*pWrap". (This does *not* set it to "false" if we
	 * don't wrap.)
	 */
	unsigned char GetBit(bool* pWrap) {
		unsigned char val;

		//assert(fBitPosn == 7 - (fCurrentBit & 0x07));

		if (fCurrentBit == fBitCount) {
			/* end reached, wrap to start */
			fCurrentBit = 0;
			fBitPosn = 7;
			fBuf = fBufStart;
			//fByte = *fBuf++;
			if (pWrap != nil)
				*pWrap = true;
		}

		val = (*fBuf >> fBitPosn) & 0x01;

		fCurrentBit++;
		fBitPosn--;
		if (fBitPosn < 0) {
			fBitPosn = 7;
			fBuf++;
		}

		fBitsConsumed++;
		return val;
	}

	/*
	 * Get the next 8 bits.
	 */
	unsigned char GetByte(bool* pWrap) {
		unsigned char val;
		int i;

		if (true || fCurrentBit > fBitCount-8) {
			/* near end, use single-bit function iteratively */
			val = 0;
			for (i = 0; i < 8; i++)
				val = (val << 1) | GetBit(pWrap);
		} else {
			/* room to spare, grab it in one or two chunks */
			assert(false);
		}
		return val;
	}

	/*
	 * Set the start position.
	 */
	void SetStartPosition(int bitOffset) {
		assert(bitOffset >= 0 && bitOffset < fBitCount);
		fCurrentBit = bitOffset;
		fBitPosn = 7 - (bitOffset & 0x07);		// mod 8, 0 to MSB
		fBuf = fBufStart + (bitOffset >> 3);	// div 8
	}

	/* used to ensure we consume exactly 100% of bits */
	void ResetBitsConsumed(void) { fBitsConsumed = 0; }
	int GetBitsConsumed(void) const { return fBitsConsumed; }

private:
	const unsigned char*	fBufStart;
	const unsigned char*	fBuf;
	int				fBitCount;			// #of bits in buffer
	int				fCurrentBit;		// where we are in buffer
	int				fBitPosn;			// which bit to access within byte
	//unsigned char	fByte;

	int				fBitsConsumed;		// sanity check - all bits used?
};

/*
 * Linear bitmap.  Suitable for use as a bad block map.
 */
class LinearBitmap {
public:
	LinearBitmap(int numBits) {
		assert(numBits > 0);
		fBits = new unsigned char[(numBits + 7) / 8];
		memset(fBits, 0, (numBits + 7) / 8);
		fNumBits = numBits;
	}
	~LinearBitmap(void) {
		delete[] fBits;
	}

	/*
	 * Set or get the status of bit N.
	 */
	bool IsSet(int bit) const {
		assert(bit >= 0 && bit < fNumBits);
		return ((fBits[bit >> 3] >> (bit & 0x07)) & 0x01) != 0;
	}
	void Set(int bit) {
		assert(bit >= 0 && bit < fNumBits);
		fBits[bit >> 3] |= 1 << (bit & 0x07);
	}

private:
	unsigned char*	fBits;
	int				fNumBits;
};


};	// namespace DiskImgLib

/*
 * Most of the code needs these.
 */
#include "GenericFD.h"

#endif /*__DISK_IMG_PRIV__*/
