/**
	\file
	\author Matthew Allen
	\date 1/4/97
	\brief Graphics file filters
*/

#ifndef FILTER2_H
#define FILTER2_H

#include <string.h>
#include "lgi/common/Containers.h"
#include "lgi/common/Progress.h"

// These are properties defined for use in the LFilter property list
#define LGI_FILTER_ERROR		"ErrorMsg"
#define LGI_FILTER_COLOUR_PROF	"ColourProfile"
#define LGI_FILTER_PARENT_WND	"Parent"
#define LGI_FILTER_FOREGROUND	"Fore"
#define LGI_FILTER_BACKGROUND	"Back"
#define LGI_FILTER_TRANSPARENT	"Transparent"
#define LGI_FILTER_QUALITY		"Quality"
#define LGI_FILTER_SUBSAMPLE	"SubSample"
#define LGI_FILTER_INFO			"Info"
#define LGI_FILTER_DPI_X		"DpiX"
#define LGI_FILTER_DPI_Y		"DpiY"

// These must be returned by a LFilter's GetVariant method
/// A descriptive name for a LFilter
#define LGI_FILTER_TYPE			"Type"
/// A comma separated list of extensions the LFilter can handle
#define LGI_FILTER_EXTENSIONS	"Extension"

LgiFunc int FindHeader(int Offset, const char *Str, LStream *f);

/// Filter can read an image
#define FILTER_CAP_READ			0x0001
/// Filter can write an image
#define FILTER_CAP_WRITE		0x0002
/// Filter can get information about an image
#define FILTER_CAP_INFO			0x0004

/// General base class for image filters. If you are creating a new filter you will
/// need to also create a factory for it by inheriting another singleton class from
/// GFilterFactory.
class LgiClass LFilter : public LDom
{
	LArray<uint8_t> Buf;

protected:
	LBmpMem *GetSurface(LSurface *pDC) { return pDC->pMem; }
	LRect *GetClip(LSurface *pDC) { return &pDC->Clip; }
	ssize_t GetLineLength(LSurface *pDC) { return (pDC->pMem) ? pDC->pMem->Line : 0; }

	Progress *Meter;

	inline void Swap(uint8_t *out, const void *in, ssize_t len)
	{
		#ifdef __BIG_ENDIAN__
		// Swap the bytes...
		uint8_t *o = out + len - 1;
		const uint8_t *i = in;
		while (i < in + len)
		{
			*o++ = *i++;
		}
		#else
		// Copy the byte
		memcpy(out, in, len);
		#endif
	}

	bool Read(LStream *s, void *p, ssize_t len)
	{
		if (Buf.Length() < (uint32_t)len)
			Buf.Length(len);

		ssize_t r = s->Read(&Buf[0], len);
		if (r != len)
			return false;

		Swap((uint8_t*)p, &Buf[0], len);
		return true;
	}

	bool Write(LStream *s, const void *p, ssize_t len)
	{
		if (Buf.Length() < (uint32_t)len)
			Buf.Length(len);
		Swap(&Buf[0], p, len);
		ssize_t w = s->Write(&Buf[0], len);
		return w == len;
	}

public:
	LDom *Props;

	enum Format
	{
		FmtJpeg,
		FmtPng,
		FmtBmp,
		FmtIco,
		FmtTiff,
		FmtGif,
		FmtPcx,
		FmtSpi,
		FmtTga,
	};
	
	enum IoStatus
	{
	    IoError,
	    IoSuccess,
	    IoComponentMissing,
	    IoUnsupportedFormat,
	    IoCancel
	};

	LFilter()
	{
		Meter = 0;
		Props = 0;
	}
	
	virtual ~LFilter() { }
	virtual Format GetFormat() = 0;

	/// Get the progress meter
	Progress *GetProgress() { return Meter; }
	/// Set the progress meter
	void SetProgress(Progress *Prg) { Meter = Prg; }

	/// Override this to return the capabilities of your filter.
	/// \returns some combination of #FILTER_CAP_READ, #FILTER_CAP_WRITE and #FILTER_CAP_INFO.
	virtual int GetCapabilites() { return 0; }
	/// \returns the number of images in the file
	virtual int GetImages() { return 1; }
	/// Reads an image into the specified surface. Override to implement reading an image.
	/// Also you need to return FILTER_CAP_READ from GetCapabilites if implemented.
	virtual IoStatus ReadImage(LSurface *Out, LStream *In) = 0;
	/// Writes an image from the specified surface. Override to implement writing an image.
	/// Also you need to return FILTER_CAP_WRITE from GetCapabilites if implemented.
	virtual IoStatus WriteImage(LStream *Out, LSurface *In) = 0;
    /// Returns the name of the external component needed to read/write images.
    virtual const char *GetComponentName() { return NULL; }
};

////////////////////////////////////////////////////////////////

/// Factory class for creating filter objects. You should create a static
/// instance of a class inheriting from GFilterFactory in your filter code
/// that can create instances of your filter.
class LgiClass GFilterFactory
{
	static class GFilterFactory *First;
	class GFilterFactory *Next;

	/// Override this to detect whether you can handle openning a file.
	/// \returns true if this filter can handle a given file.
	virtual bool CheckFile
	(
		/// The filename, useful for extension checking.
		/// \sa LGetExtension.
		const char *File,
		/// The access mode that is desired, either #FILTER_CAP_READ or #FILTER_CAP_WRITE.
		int Access,
		/// The first 16 bytes of the file, or NULL if not available. Useful for checking
		/// magic numbers etc.
		const uchar *Hint
	) = 0;
	/// Override to return an new (heap alloced) instance of your filter.
	virtual LFilter *NewObject() = 0;
	
public:
	GFilterFactory();
	virtual ~GFilterFactory();

	static LFilter *New(const char *File, int Access, const uchar *Hint);
	static LFilter *NewAt(int i);
	static int GetItems();
};

#endif
