#ifndef _MPFS2lib_
#define _MPFS2lib_
/*<<<NL>>>*/

#ifndef _MPFS2dynVar_
#	include "mpfs2dynVar.h"
#endif
#include <xbCompressItf.h>

interface IMPFSBuilderUI:public IDynamicVariableParserUI
{
	virtual void warningLog(const xbString &log,const FileLoc &fileLoc) = 0;
	virtual void buildStart(const xbString &msg) = 0;
};
enum MPFSOutputFormat
{
    MPFSOutputFormat_BIN,
    MPFSOutputFormat_C18,
    MPFSOutputFormat_ASM30,
    MPFSOutputFormat_C32,
    MPFSOutputFormat_MDD,
};

enum MPFShexFormat
{
    MPFShexFormat_disabled = 0,
    MPFShexFormat_8bits = 8,
    MPFShexFormat_16bits = 16,
    MPFShexFormat_24bits = 24,
    MPFShexFormat_32bits = 32
};

/*class FileRecord:public xbBase
{
public:
    uint16 nameHash;
	uint32 fileRecordOffset;
	uint32 dynVarCntr;
	
	FileRecord(uint16 _nameHash, uint32 _fileRecordOffset,uint32 _dynVarCntr)
	{
	   nameHash = _nameHash;
	   fileRecordOffset = _fileRecordOffset;
	   dynVarCntr = _dynVarCntr;
	}
};*/

class MPFSFileRecord:public xbBase
{
private:
	xbString fileName;
public:
	xbString origFileName;
	uint16 nameHash;
    xbDateTime fileDate;
    xbData data;
	uint32 locStr;
    uint32 locData;
    bool hasIndex;
    bool isIndex;
    bool isZipped;
	int extType;
//	uint32 dynVarCntr;/*Number of Dynamic Variables in the file*/
//	xbData dynVarOffsetAndIndexID;/*Location of dynamic var and its ID*/
//	uint32 fileRecordOffset;/* Byte location in the Record file where this file record/information is written from*/
//  uint32 fileRecordLength;/* Total length/number of bytes in this file record*/

    /// <summary>
    /// Sets up a new MPFSFileRecord
    /// </summary>
    MPFSFileRecord()
    {
		nameHash = 0xffff;
        locStr = 0;
        locData = 0;
        hasIndex = false;
        isIndex = false;
        isZipped = false;
		extType=-1;
		//dynVarCntr=0;
		//fileRecordOffset = 0;
		//fileRecordLength = 0;
    }

    xbString GetFileName() const
    {
        return fileName; 
	}
	void SetFileName(const xbString &value)
	{
		fileName = value;
        if(value.IsEmpty())
            nameHash = 0xffff;
        else
        {
            nameHash = 0;
            const char *ptr = value.Begin();
            const char *pend = value.End();
			while(ptr < pend)
			{
                nameHash += *(ptr++);
                nameHash <<= 1;
			}
        }
    }
	xbPropertyRW(xbString,FileName);
};


class __declspec(uuid("{16A0A931-0720-4fa8-9335-8640722E06BE}")) 
	MPFSBuilder:public xbObject
{
protected:
	IMPFSBuilderUI		*m_pUI;
//	xbString			Mddpath;
    xbString			projectDir;
    xbString			outputFile;
    xbPtrArray<MPFSFileRecord> files;
    xbString			generatedImageName;
    bool				indexUpdated;

public:
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(MPFSBuilder);
		return xbObject::GetInterface(riid,ppInterf);
	}

	/// <summary>
    /// Specifies the path for the image file, along with any associated index files
    /// </summary>
    xbString GetProjectDir() const
    {
        return projectDir;
    }
	xbPropertyR(xbString,ProjectDir);

    /// <summary>
    /// Specifies the name of the image file to save to the file system
    /// </summary>
    xbString GetOutputFile() const
    {
        return outputFile;
    }
	xbPropertyR(xbString,OutputFile);

    /// <summary>
    /// Retrieves the file name of the most recently generated image
    /// </summary>
    xbString GetGeneratedImageFileName() const
    {
        return generatedImageName;
    }
	xbPropertyR(xbString,GeneratedImageFileName);

    /// <summary>
    /// Indicates whether or not the index file was updated
    /// </summary>
    bool GetIndexUpdated() const
    {
        return indexUpdated;
    }
	xbPropertyR(bool,IndexUpdated);

    /// <summary>
    /// Creates a new MPFSBuilder
    /// </summary>
    MPFSBuilder()
    {
        m_pUI = NULL;
        indexUpdated = false;
    }

	/// <summary>
    /// Adds a file to the MPFS image
    /// </summary>
    /// <param name="localName">Local file name to read</param>
    /// <param name="imageName">Name to use in image file</param>
    virtual void AddFile(const xbString &localName, const xbString &imageName) = 0;

    /// <summary>
    /// Recursively adds a directory of files to the image
    /// </summary>
    /// <param name="dataPath">Local directory name to add</param>
    /// <param name="imagePath">Path in MPFS image to add files to</param>
    virtual void AddDirectory(const xbString &dataPath, const xbString &imagePath) = 0;

    /// <summary>
    /// Generates the MPFS image and any necessary support files
    /// </summary>
    virtual void Generate(MPFSOutputFormat format) = 0;

    /// <summary>
    /// Dump the list of used tokens and their locations in sources
    /// </summary>
    virtual void ListUsedTokens(bool bFull) = 0;
protected:
	virtual void infoLog(const xbString &log)
	{
		m_pUI->infoLog(log);
	}
	virtual void warningLog(const xbString &log,const FileLoc &fileLoc)
	{
		m_pUI->warningLog(log,fileLoc);
	}
};

class __declspec(uuid("{6C13E5BA-8852-4820-838A-EAD52E3C10D8}")) 
	MPFS2Builder : public MPFSBuilder
{
protected:
	xbArray<xbString> dynamicTypes;
    xbArray<xbString> nonGZipTypes;
	xbArray<xbString> hexTypes;
	xbArray<xbString> excludeTypes;
	xbArray<xbString> utf8types;
	uint32 hexRow,hexRowLimit;
	MPFShexFormat hexFormat;
	DynamicVariableParser dynVarParser;

    // Constants
    static const uint16 MPFS2_FLAG_ISZIPPED = 0x0001;
    static const uint16 MPFS2_FLAG_HASINDEX = 0x0002;
public:
	MPFS2Builder()
	{
		hexRow = hexRowLimit = 0;
		hexFormat = MPFShexFormat_disabled;
	}
	virtual xbString GetLabel() const {return "MPFS2Builder";}
	xbImplCreateObject(MPFS2Builder,MPFS2Builder);

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(MPFS2Builder);
		return MPFSBuilder::GetInterface(riid,ppInterf);
	}

	// Constructor
    /// <summary>
    /// Creates a new MPFS2 image builder
    /// </summary>
    /// <param name="localPath">The directory to save the image in, and to read/write index files</param>
    /// <param name="localFile">The output file name for the image</param>
    virtual void Init(IMPFSBuilderUI *pUI,xbString _projectDir, xbString _sourceDir, xbString _outputFile)
    {
		m_pUI = pUI;
        if (!_projectDir.EndWith("\\"))
            _projectDir += "\\";
		pUI->buildStart(GetLabel()+" started: project dir="+_projectDir+", output="+_outputFile);
		dynVarParser.Init(pUI,_projectDir,_sourceDir);
        projectDir = _projectDir;
        outputFile = _outputFile;
    }

	/// <summary>
    /// Sets a comma-separated list of types to be considered dynamic
    /// </summary>
    void SetDynamicTypes(const xbString &value)
    {
        xbArray<xbString> arr;
		value./*RetReplace("*","").*/Split(',',&arr);
		dynamicTypes = arr;
    }
	xbString GetDynamicTypes() const
	{
		xbString ret;
		for(int i = 0; i < dynamicTypes.Length;i++)
		{
			if (ret.IsValid())
				ret += ',';
			ret += dynamicTypes.GetAt(i);
		}
		return ret;
	}
	xbPropertyRW(xbString,DynamicTypes);

    /// <summary>
    /// Sets a comma-separated list of types not to be compressed
    /// </summary>
    void SetNonGZipTypes(const xbString &value)
    {
        xbArray<xbString> arr;
		value./*RetReplace("*","").*/Split(',',&arr);
		nonGZipTypes = arr;
    }
	xbString GetNonGZipTypes() const
	{
		xbString ret;
		for(int i = 0; i < nonGZipTypes.Length;i++)
		{
			if (ret.IsValid())
				ret += ',';
			ret += nonGZipTypes.GetAt(i);
		}
		return ret;
	}
	xbPropertyRW(xbString,NonGZipTypes);

    /// <summary>
    /// Sets a comma-separated list of types not to be compressed
    /// </summary>
    void SetHexTypes(const xbString &value)
    {
        xbArray<xbString> arr;
		value./*RetReplace("*","").*/Split(',',&arr);
		hexTypes = arr;
    }
	xbString GetHexTypes() const
	{
		xbString ret;
		for(int i = 0; i < hexTypes.Length;i++)
		{
			if (ret.IsValid())
				ret += ',';
			ret += hexTypes.GetAt(i);
		}
		return ret;
	}
	xbPropertyRW(xbString,HexTypes);

    /// <summary>
	/// Sets hex row count (row count of data managed as blocks)
    /// </summary>
	void SetHexRow(uint32 value)
    {
		hexRow = value;
    }
	uint32 GetHexRow() const
	{
		return hexRow;
	}
	xbPropertyRW(uint32,HexRow);


    /// <summary>
	/// Sets hex row limit (address max to manage blocks as rows)
    /// </summary>
	void SetHexRowLimit(uint32 value)
    {
		hexRowLimit = value;
    }
	uint32 GetHexRowLimit() const
	{
		return hexRowLimit;
	}
	xbPropertyRW(uint32,HexRowLimit);

    /// <summary>
    /// Sets hex format
    /// </summary>
    void SetHexFormat(MPFShexFormat value)
    {
		hexFormat = value;
    }
	MPFShexFormat GetHexFormat() const
	{
		return hexFormat;
	}
	xbPropertyRW(MPFShexFormat,HexFormat);


    /// <summary>
    /// Sets a comma-separated list of types not to be included in mpfs file
    /// </summary>
    void SetExcludeTypes(const xbString &value)
    {
        xbArray<xbString> arr;
		value./*RetReplace("*","").*/Split(',',&arr);
		excludeTypes = arr;
    }
	xbString GetExcludeTypes() const
	{
		xbString ret;
		for(int i = 0; i < excludeTypes.Length;i++)
		{
			if (ret.IsValid())
				ret += ',';
			ret += excludeTypes.GetAt(i);
		}
		return ret;
	}
	xbPropertyRW(xbString,ExcludeTypes);

    /// <summary>
    /// Sets a comma-separated list of types not to be included in mpfs file
    /// </summary>
    void SetUtf8types(const xbString &value)
    {
        xbArray<xbString> arr;
		value./*RetReplace("*","").*/Split(',',&arr);
		utf8types = arr;
    }
	xbString GetUtf8types() const
	{
		xbString ret;
		for(int i = 0; i < utf8types.Length;i++)
		{
			if (ret.IsValid())
				ret += ',';
			ret += utf8types.GetAt(i);
		}
		return ret;
	}
	xbPropertyRW(xbString,Utf8types);

	/// <summary>
    /// Adds a file to the MPFS image
    /// </summary>
    /// <param name="localName">Local file name to read</param>
    /// <param name="imageName">Name to use in image file</param>
    virtual void AddFile(const xbString &localName, const xbString &imageName);

	/// <summary>
    /// Recursively adds a directory to the MPFS image.  All non-hidden files will be included.
    /// </summary>
    /// <param name="dataPath">The local directory to search</param>
    /// <param name="imagePath">The base directory this folder is in the MPFS2 image</param>
    /// <returns></returns>
    virtual void AddDirectory(const xbString &dataPath, const xbString &imagePath);

    /// <summary>
    /// Generates an image in the specified format
    /// </summary>
    /// <param name="format">One of the MPFSOutputFormat constants indicating the format</param>
    /// <returns>true if generation was successful, false otherwise</returns>
    virtual void Generate(MPFSOutputFormat format);

    /// <summary>
    /// Dump the list of used tokens and their locations in sources
    /// </summary>
    virtual void ListUsedTokens(bool bFull);
protected:
	xbRefPtr<IxbCompressPlug> m_prCompressPlug;
	void GetCompressPlug(xbRefPtr<IxbCompressPlug> *ppPlug);
	bool gzipData(xbData *pdata,int *pgzipRatio);
	class  __declspec(uuid("{D49319B7-E456-4ca4-872B-1B0557EB1E43}")) MPFS2Writer;
	virtual void WriteImage(MPFS2Writer &w);
	virtual void contentTypeCheck(MPFSFileRecord *pFile,bool bUtf8);

	virtual MPFStokenVersion GetTokenVersion() {return MPFStokenVersion_2;}
	virtual xbRefPtr<MPFS2Writer> MPFS2WriterCreate(MPFSOutputFormat format);

	virtual void hexCompress(MPFSFileRecord *pFile);

#ifdef hhhhh
	class FilesRecordWriter : BinaryWriter 
	{
        protected BinaryWriter fout;
		public int ImageLength = 0;

        public FilesRecordWriter(String DataPath)
        {
			String filename = "FileRcrd.bin";
		
			string newPath = System.IO.Path.Combine(DataPath, filename);

			if (!filename.EndsWith(".bin", StringComparison.OrdinalIgnoreCase))
								filename += ".bin";
							
			fout = new BinaryWriter(new FileStream(newPath, FileMode.Create), Encoding.ASCII);
        }

		public void Write(byte data)
        {
			fout.Write(data);
			ImageLength++;
        }
		
        public void Close()
        {
            fout.Close();
        }
    }

	private class DynamicVarRecordWriter : BinaryWriter
	{
		#region Fields
		protected BinaryWriter fout;
		#endregion

		public DynamicVarRecordWriter(String DataPath)
		{

			String filename = "DynRcrd.bin";


			//Create a new subfolder under the current active folder
			string newPath = System.IO.Path.Combine(DataPath, filename);

			
			if (!filename.EndsWith(".bin", StringComparison.OrdinalIgnoreCase))
				filename += ".bin";
			//BinaryWriter fout = new BinaryWriter(new FileStream(filename, FileMode.Create), Encoding.ASCII);
			fout = new BinaryWriter(new FileStream(newPath, FileMode.Create), Encoding.ASCII);

		}
		//public override void Write(byte data)
        public void Write(byte data)
		{				
			fout.Write(data);
		}

		//public override void Close()
         public void Close()
		 {
				
			fout.Close();
		 } 
	}
	
	//public bool MDDWriter(String localPath)
    public void MDDWriter(String localPath)
	{
    	FilesRecordWriter FileRecrd /*Files with dynamic variables Record*/;
		//RecordWriter DynVarRecrd /*Dynamic Variables Record of each file*/;
        DynamicVarRecordWriter DynVarRecrd /*Dynamic Variables Record of each file*/;

        UInt32 counter = 0;
        UInt32 loopCntr = 0;
        UInt32 numFileRecrds = 0;
		
        FileRecrd = new FilesRecordWriter(localPath);
		DynVarRecrd = new DynamicVarRecordWriter(localPath);


		UInt16 flags;
		List<FileRecord> FileRcrdList = new List<FileRecord>();
        
		foreach (MPFSFileRecord file in files)
        {
        	counter=0;
			loopCntr=0;
        	if(file.dynVarCntr >0)
        	{
				FileRcrdList.Add(new FileRecord ((UInt16)file.nameHash,
												 (UInt32)file.fileRecordOffset,
												 (UInt32)file.dynVarCntr));
				numFileRecrds++;

	
				DynVarRecrd.Write((byte)(file.fileRecordLength));
				DynVarRecrd.Write((byte)(file.fileRecordLength>>8));
				DynVarRecrd.Write((byte)(file.fileRecordLength>>16));
				DynVarRecrd.Write((byte)(file.fileRecordLength>>24));

				
				flags = 0;
				if (file.hasIndex)
					flags |= MPFS2_FLAG_HASINDEX;
				if (file.isZipped)
					flags |= MPFS2_FLAG_ISZIPPED;


				DynVarRecrd.Write((byte)(flags));
				DynVarRecrd.Write((byte)(flags>>8));

               /* loopCntr = (UInt16)file.FileName.Length;
	                        flags = 0x00;

	                        while (loopCntr != (UInt32)flags)
	                        {
	                            DynVarRecrd.Write((byte)file.FileName[flags]);
	                            flags += 1;
	                        }
	                     */
				loopCntr=0;
				
                while(loopCntr!=file.dynVarCntr)
				{

					DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[0+counter]));
					DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[1+counter]));
					DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[2+counter]));
					DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[3+counter]));

					DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[4 + counter]));
                    DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[5 + counter]));
                    DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[6 + counter]));
                    DynVarRecrd.Write((byte)(file.dynVarOffsetAndIndexID[7 + counter]));


					counter+=8;
					loopCntr+=1;
				}

			}
	        }

       FileRcrdList.Sort(delegate(FileRecord FR1, FileRecord FR2) 
			{
				return FR1.nameHash.CompareTo(FR2.nameHash); 
			}
		);

		FileRecrd.Write((byte)(numFileRecrds));
        FileRecrd.Write((byte)(numFileRecrds>>8));
        FileRecrd.Write((byte)(numFileRecrds>>16));
        FileRecrd.Write((byte)(numFileRecrds>>24));	

		FileRcrdList.ForEach(delegate(FileRecord FR) 
			{
                FileRecrd.Write((byte)(FR.nameHash));
				FileRecrd.Write((byte)(FR.nameHash>>8));
                FileRecrd.Write((byte)(FR.fileRecordOffset));
                FileRecrd.Write((byte)(FR.fileRecordOffset>>8));
                FileRecrd.Write((byte)(FR.fileRecordOffset>>16));
                FileRecrd.Write((byte)(FR.fileRecordOffset>>24));
                FileRecrd.Write((byte)(FR.dynVarCntr));
                FileRecrd.Write((byte)(FR.dynVarCntr>>8));
                FileRecrd.Write((byte)(FR.dynVarCntr>>16));
                FileRecrd.Write((byte)(FR.dynVarCntr>>24));

			}
		);

        log.Add("\r\nGENERATED MPFS2 IMAGE: " + FileRecrd.ImageLength + " bytes");

		FileRecrd.Close();
		DynVarRecrd.Close();
     
    }
#endif

	bool FileMatchesExt(const xbString &fileName, const xbArray<xbString> &endings)
    {
		const xbString *ptr = endings.Begin();
		const xbString *pend = endings.End();
		while(ptr < pend)
		{
			if (ptr->IsValid()
				&& ::xbMatchPath(*ptr,fileName) == 0)
				return true;
//			if (fileName.EndWithNC(*ptr))
//				return true;
			ptr++;
		}
        return false;
    }

	class 
		MPFS2Writer:public xbObject
    {
	public:
        uint32 ImageLength;
        xbString imageName;

		MPFS2Writer()
			:ImageLength(0)
		{
		}
		virtual bool GetInterface(REFIID riid, void ** ppInterf)
		{
			xbImplInterface(MPFS2Writer);
			return xbObject::GetInterface(riid,ppInterf);
		}

        virtual void Write(const byte *pData,int cData) = 0;
        virtual void WriteCRCblock() = 0;
        virtual void Close() = 0;

        void Write(byte data)
        {
			Write(&data,1);
        }

        void Write(const xbString &data)
        {
			Write((const byte*)data.Ptr(),data.Length);
        }

        void Write(uint16 data)
        {
            Write((byte)(data));
            Write((byte)(data >> 8));
        }

        void Write(uint32 data)
        {
            Write((byte)(data));
            Write((byte)(data >> 8));
            Write((byte)(data >> 16));
            Write((byte)(data >> 24));
        }
    };

    class __declspec(uuid("{B117A7A6-B462-4f63-BA40-FE96CF0F9D1D}"))  
		MPFS2BINWriter : public MPFS2Writer
    {
	protected:
		xbFileStream fout;
		xbCRC32 m_crc;
		ulong	m_offsetCrcBlock;

	public:
		xbImplCreateObjectWithArg(MPFS2BINWriter,MPFS2Writer,xbString);

		virtual bool GetInterface(REFIID riid, void ** ppInterf)
		{
			xbImplInterface(MPFS2BINWriter);
			return MPFS2Writer::GetInterface(riid,ppInterf);
		}
		MPFS2BINWriter()
		{
			m_offsetCrcBlock = 0;
		}

		void Create(xbString filename)
        {
			MPFS2Writer::Create();
            if (!filename.EndWithNC(".bin") && !filename.EndWithNC(".mpfs2"))
                filename += ".mpfs2";
			fout.Open(filename,xbOpenCreateAlways,xbAccessW);
            imageName = filename;
			m_crc.Reset();
        }

        virtual void Write(const byte *pData,int cData)
		{
			if (m_offsetCrcBlock)
				m_crc.Push(pData,cData);
            ImageLength+=cData;
			fout.Write(pData,cData);
		}
        virtual void Close()
		{
			if (m_offsetCrcBlock)
			{			
				fout.Position = m_offsetCrcBlock;
				m_offsetCrcBlock = 0;
				uint32 cByte = ImageLength;
				uint32 crc = m_crc.GetCRC();
				MPFS2Writer::Write(cByte);
				MPFS2Writer::Write(crc);				
			}
			fout.Close();
		}

        virtual void WriteCRCblock()
		{
			if (m_offsetCrcBlock)
				eMpfs2::Throw("MPFS2 generation / WriteCRCblock already called !");

			ulong offsetCrcBlock = fout.Position;
			if (!offsetCrcBlock)
				eMpfs2::Throw("MPFS2 generation / invalid call to WriteCRCblock !");
			MPFS2Writer::Write((uint32)0);
			MPFS2Writer::Write((uint32)0);
			m_offsetCrcBlock = offsetCrcBlock; //crc calc will start when m_offsetCrcBlock != 0
		}
    };

    class __declspec(uuid("{3E7A95C8-AA57-4141-83CD-1D00EC8213D9}"))  
		MPFS2C18Writer : public MPFS2Writer
    {
	protected:
		xbFileStream fout;
        xbString ASCIILine;

	public:
		xbImplCreateObjectWithArg(MPFS2C18Writer,MPFS2Writer,xbString);

		virtual bool GetInterface(REFIID riid, void ** ppInterf)
		{
			xbImplInterface(MPFS2C18Writer);
			return MPFS2Writer::GetInterface(riid,ppInterf);
		}
		MPFS2C18Writer()
		{
		}
        virtual void WriteCRCblock()
		{
			MPFS2Writer::Write((uint32)0);
			MPFS2Writer::Write((uint32)0);
		}

		void Create(xbString filename)
        {
			MPFS2Writer::Create();
            if (!filename.EndWithNC(".c"))
                filename += ".c";
			fout.Open(filename,xbOpenCreateAlways,xbAccessW);
            imageName = filename;
            fout.WriteString(
                "/***************************************************************\r\n" 
                " * MPFSImg2.c\r\n" 
                " * Defines an MPFS2 image to be stored in program memory.\r\n" 
                " *\r\n" 
                " * NOT FOR HAND MODIFICATION\r\n" 
                " * This file is automatically generated by the MPFS2 Utility\r\n" 
                " * ALL MODIFICATIONS WILL BE OVERWRITTEN BY THE MPFS2 GENERATOR\r\n" 
				" * Generated " + xbDateTime::Now().ToString() + "\r\n" 
                " ***************************************************************/\r\n" 
                "\r\n#define __MPFSIMG2_C\r\n\r\n" 
                "#include \"TCPIPConfig.h\"\r\n" 
                "#if !defined(MPFS_USE_EEPROM) && !defined(MPFS_USE_SPI_FLASH)\r\n\r\n" 
                "#include \"TCPIP Stack/TCPIP.h\"\r\n" 
                "#if defined(STACK_USE_MPFS2)\r\n\r\n" 
                "\r\n" 
                "/**************************************\r\n" 
				" * MPFS2 Image Data\r\n" 
				" **************************************/"
            );
        }

	private:
		void WriteByte(byte data)
        {
            char ASCIIdata;

            ASCIIdata = '.';
            if (data >= 32 && data <= 126 && data != '*')   // * cannot be displayed because it would open the possibility of */ occuring in the sequence, which would mess up our block comment
                ASCIIdata = (char)data;

            if (ImageLength % 1024 == 0)
                ASCIILine = " " + ASCIILine;
            else
                fout.WriteString(",");
            if (ImageLength % 16 == 0)
            {
                if (ImageLength != 0)
                    fout.WriteString(ASCIILine + " */");
                ASCIILine = " /* ";
            }
            if (ImageLength % 1024 == 0)
                fout.WriteString("\r\n#define DATACHUNK" + xbString((ImageLength / 1024), xbStringNum_hexa).RetPadLeft(6, '0'));
            if (ImageLength % 16 == 0)
                fout.WriteString(" \\\r\n\t");
            ASCIILine += ASCIIdata;
            fout.WriteString("0x" + xbString((ulong)data,xbStringNum_hexa).RetPadLeft(2, '0'));
            ImageLength++;
        }
	public:
        virtual void Write(const byte *pData,int cData)
		{
			while(cData-- > 0)
			{
				WriteByte(*(pData++));
			}
		}

        virtual void Close()
        {
            if (ImageLength % 16 != 0)
                fout.WriteString(xbStringPad((16-(ImageLength % 16))*5+1, ' ') + ASCIILine.RetPad(20) + " */");

            fout.WriteString("\r\n\r\n\r\n");
            if (ImageLength != 0)
            {
                fout.WriteString("/**************************************\r\n" 
				           " * MPFS2 C linkable symbols\r\n" 
				           " **************************************/\r\n" 
                           "// For C18, these are split into seperate arrays because it speeds up compilation a lot.  \r\n" 
                           "// For other compilers, the entire data array must be defined as a single variable to \r\n" 
                           "// ensure that the linker does not reorder the data chunks in Flash when compiler \r\n" 
                           "// optimizations are turned on.\r\n" 
                           "#if defined(__18CXX)\r\n" 
                           "\tROM BYTE MPFS_Start[] = {DATACHUNK000000};\r\n");

                for (uint32 i = 1024; i < ImageLength; i += 1024)
                {
                    fout.WriteString("\tROM BYTE MPFS_" + xbString(i / 1024, xbStringNum_hexa).RetPadLeft(6, '0') + "[] = {DATACHUNK"+xbString((i / 1024), xbStringNum_hexa).RetPadLeft(6, '0')+ "};\r\n");
                }

                fout.WriteString("#else\r\n"
                           "\tROM BYTE MPFS_Start[] = {");
                for (uint32 i = 0; i < ImageLength; i += 1024)
                {
                    fout.WriteString("DATACHUNK" + xbString(i / 1024, xbStringNum_hexa).RetPadLeft(6, '0'));
                    if (i + 1024 < ImageLength)
                        fout.WriteString(", ");
                }
                fout.WriteString("};\r\n"
                           "#endif\r\n\r\n\r\n");
            }
            
            fout.WriteString(
                "/**************************************************************\r\n"
                " * End of MPFS\r\n"
                " **************************************************************/\r\n"
                "#endif // #if defined(STACK_USE_MPFS2)\r\n" 
                "#endif // #if !defined(MPFS_USE_EEPROM) && !defined(MPFS_USE_SPI_FLASH)\r\n"
            );
            fout.Close();
        }
    };

    class __declspec(uuid("{CA3A7E80-8D03-4568-84D5-456F2BD10552}"))  
		MPFS2ASM30Writer : MPFS2Writer
    {
	protected:
		xbFileStream fout;
	public:
		xbImplCreateObjectWithArg(MPFS2ASM30Writer,MPFS2Writer,xbString);

		virtual bool GetInterface(REFIID riid, void ** ppInterf)
		{
			xbImplInterface(MPFS2ASM30Writer);
			return MPFS2Writer::GetInterface(riid,ppInterf);
		}
		MPFS2ASM30Writer()
		{
		}

		void Create(xbString filename)
        {
			MPFS2Writer::Create();
            if (!filename.EndWithNC(".s"))
                filename += ".s";

			fout.Open(filename,xbOpenCreateAlways,xbAccessW);
            imageName = filename;
            fout.WriteString(
                ";**************************************************************\r\n"
                "; MPFSImg2.s\r\n"
                "; Defines an MPFS2 image to be stored in program memory.\r\n" 
                "; Defined in ASM30 assembly for optimal storage size.\r\n" 
                ";\r\n" 
                "; NOT FOR HAND MODIFICATION\r\n" 
                "; This file is automatically generated by the MPFS2 Utility\r\n" 
                "; ALL MODIFICATIONS WILL BE OVERWRITTEN BY THE MPFS2 GENERATOR\r\n" 
				"; Generated " + xbDateTime::Now().ToString() + "\r\n" 
                ";**************************************************************\r\n\r\n" 
                ".equ VALID_ID,0\r\n" 
                ".ifdecl __dsPIC30F\r\n" 
                "    .include \"p30fxxxx.inc\"\r\n" 
                ".endif\r\n" 
                ".ifdecl __dsPIC33F\r\n" 
                "    .include \"p33fxxxx.inc\"\r\n" 
                ".endif\r\n" 
                ".ifdecl __PIC24H\r\n" 
                "    .include \"p24hxxxx.inc\"\r\n" 
                ".endif\r\n" 
                ".ifdecl __PIC24F\r\n" 
                "    .include \"p24fxxxx.inc\"\r\n" 
                ".endif\r\n" 
                ".ifdecl __PIC24E\r\n" 
                "    .include \"p24exxxx.inc\"\r\n" 
                ".endif\r\n" 
                ".if VALID_ID <> 1\r\n" 
                "    .error \"Processor ID not specified in generic include files.  New ASM30 assembler needs to be downloaded?\"\r\n" 
                ".endif\r\n" 
                "	.text\r\n" 
                "	.section	MPFSData,code\r\n\r\n" 
                "	goto END_OF_MPFS	; Prevent accidental execution of constant data.\r\n" 
                "	.global BEGIN_MPFS\r\n" 
                "BEGIN_MPFS:"
            );
        }

        virtual void WriteCRCblock()
		{
			MPFS2Writer::Write((uint32)0);
			MPFS2Writer::Write((uint32)0);
		}
	private:
		void WriteByte(byte data)
        {
            if (ImageLength % 12 == 0)
                fout.WriteString("\r\n\t.pbyte\t");
            fout.WriteString("0x" + xbString((uint)data, xbStringNum_hexa).RetPadLeft(2, '0'));
            ImageLength++;
            if(ImageLength % 12 != 0)
                fout.WriteString(",");
        }

	public:
        virtual void Write(const byte *pData,int cData)
		{
			while(cData-- > 0)
			{
				WriteByte(*(pData++));
			}
		}

        virtual void Close()
        {
            if (ImageLength % 12 == 0)
                fout.WriteString(",");
            fout.WriteString(
                "0x00\r\n" 
                "END_OF_MPFS:\r\n\r\n" 
                "	.section	.const,psv\r\n" 
                "	.global	_MPFS_Start\r\n" 
                "_MPFS_Start:\r\n" 
                "	.long	paddr(BEGIN_MPFS)\r\n\r\n" 
                "	.section	MPFSHelpers,code\r\n\r\n" 
                "	.global _ReadProgramMemory\r\n" 
                "_ReadProgramMemory:\r\n" 
                "	push		_TBLPAG\r\n" 
                "	mov			w1,_TBLPAG\r\n" 
                "	mov			w0,w5\r\n" 
                "	tblrdl		[w5],w0\r\n" 
                "	tblrdh		[w5],w1\r\n" 
                "	pop			_TBLPAG\r\n"
                "	return\r\n"
            );
            fout.Close();
        }
    };
};

#define TIME_YEARSTART          2000
#define TIME_YEAREND            2135
#define TIME_MONTHSTART         1
#define TIME_MONTHEND           12
#define TIMEDAY_SEC           (24ul*60*60)
#define TIMEDAY_COUNT(y,m,d)    (((y - TIME_YEARSTART)*365ul \
                                    +(y - TIME_YEARSTART+3)/4 /*Années bisextiles*/ \
                                    +(y - TIME_YEARSTART+399)/400 /*Années bisextiles / ajout multiple de 400 (superflu)*/\
                                    -(y - TIME_YEARSTART+99)/100 /*Années bisextiles / retrait multiple de 100*/\
                                    +(m-TIME_MONTHSTART)*30 \
                                    +(m-TIME_MONTHSTART+1)/2 /*Ajout d'un jour tous les 2 mois dés février*/\
                                    +(m-TIME_MONTHSTART+4)/12 /*Ajout d'un jour après aout */ \
                                    -(m-TIME_MONTHSTART+10)/12*(2-((y%4)==0&&((y%400)==0||(y%100)!=0)?1:0)) /*Correcion pour février à partir de mars*/\
                                    +d/*-1+1*/)*TIMEDAY_SEC)
#define TIMEHOUR_COUNT(h,m,s)   (h*60ul*60+m*60+s)
#define TIME_COUNT(y,mo,d,h,mi,s)   (TIMEDAY_COUNT(y,mo,d) + TIMEHOUR_COUNT(h,mi,s))

uint32 dateTimeToPicTime(const xbDateTime &dt);
#endif
