/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

!!IMPORTANT!!
This software(mcTool) uses parts of sources owned by Microchip Technology, Inc, 
and so these parts are not covered by this licence. 
See header of concerned files (mpfs2dynVar.cpp, mpfs2lib.cpp) for their own licence. 
////////////////////////////////////////////////////////////////////////////////
#Title: mcTool_mpfs2
#Project: mcTool
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<08/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"
#include "mcTool_mpfs2.h"
#include "mpfs2mLib.h"

void mcTool_MPFS2_help(mcTool &mcTool)
{
	mcTool.fout << (LPCSTR)xbGetProductName() << " HELP -MPFS (MPFS2 generation):\n";
	mcTool.fout << "SYNTAX :" << (LPCSTR)mcTool.GetAppName() << " -mpfs [options] <SourceDir> <ProjectDir> <OutputFile>\n";
	mcTool.fout << "  -V2   : MPFS version 2.1 (Default)\n";
	mcTool.fout << "  -V2m  : MPFS version 2.1m\n";
	mcTool.fout << "  -BIN  : Output a BIN image (Default)\n";
	mcTool.fout << "  -C18  : Output a C18 image\n";
	mcTool.fout << "  -ASM30: Output an ASM30 image\n";
	mcTool.fout << "  -C32  : Output a C32 image\n";
	mcTool.fout << "  -DYNA <extList> : Dynamic file types\n";
	mcTool.fout << "          - Default for MPFS2: \"" << mcTool.m_htmlTypes << "\"\n";
	mcTool.fout << "          - For MPFS2m: \"" << mcTool.m_htmlTypes2m << "\"\n";
	mcTool.fout << "  -XGZIP <extList> : Non-compressible types (Default: \"" << mcTool.m_noGZipTypes << "\")\n";
	mcTool.fout << "  -UTF8 <extList> : file types to encode in utf8\n";
	mcTool.fout << "          - Default for MPFS2: \"" << mcTool.m_utf8types << "\"\n";
	mcTool.fout << "          - For MPFS2m: \"" << mcTool.m_utf8types2m << "\"\n";
	mcTool.fout << "  -EXCLUDE <extList> : Types to exclude from MPFS file\n";
	mcTool.fout << "Firmware update included (tested: pic 24bits only):\n";
	mcTool.fout << "  -HEX <extList> : Hex types (Default: \"" << mcTool.m_hexTypes << "\")\n";
	mcTool.fout << "  -HEXROW: row size(in dword) for pic (Default: 128)\n";
	mcTool.fout << "  -HEXROWLIM: row limit for pic (Default: 0x7FFFFE)\n";
	mcTool.fout << "  -HEXFORMAT [24bits|32bits|16bits|8bits|off]:\n";
	mcTool.fout << "      data format for pic (Default: 24bits)\n";
	mcTool.fout << "  -TABSIZE: tab size for sources locations (Default: " << xbString(mcTool.m_tabSize) << ")\n";
	mcTool.fout << "  -HPHDR <text>: include text after HTTPPrint.h header\n";
	mcTool.fout << "      (if text starts with @, text must be a file to include)\n";
	mcTool.fout << "  -HPARGNUM <ctype> : C type for token number arguments (Default: \"" << mcTool.m_HTTPPrint_TokenArgType_Number << "\")\n";
	mcTool.fout << "  -HPARGSTR <ctype> : C type for token string arguments (Default: \"" << mcTool.m_HTTPPrint_TokenArgType_String << "\")\n";
	mcTool.fout << "  -LIST : Dump the list of used tokens and their locations in sources\n";
	mcTool.fout << "  -LISTF : Same as -LIST but dump also no more used tokens\n";
	mcTool.fout << "  ? or help : this help screen\n\n";
	mcTool.fout << "SourceDir, ProjectDir, and OutputFile are required\n";
	mcTool.fout << "  and should be enclosed in quotes.\n";
	mcTool.fout << "OutputFile is placed relative to ProjectDir and *CANNOT* be a full path name.\n\n";
}

bool mcTool_MPFS2(mcTool &mcTool)
{
	uint32 hexRow = 128;
	uint32 hexRowLimit = 0x7FFFFE;
	bool bListTokens = false,bListTokensFull = false;

	xbCfgKey ckeyRoot2m(xbCfgAppli,"MPFS2m");//Settings specifics to MPFS2m
	{
		xbCfgKey ckey(ckeyRoot2m,"ContentTypes");
		if (!ckey.Exists())
		{
			ckey.SetValue("example","text/example",xbCfgVal_AsSectionValue);
			ckey.SetValue("csv","text/csv",xbCfgVal_AsSectionValue);
			ckey.SetValue("bib","application/data",xbCfgVal_AsSectionValue);
		}
		if (!ckeyRoot2m.ValueExists("DynamicTypes"))
			ckeyRoot2m.SetValue("DynamicTypes",mcTool.m_htmlTypes2m="*.htm, *.html, *.xml, *.cgi, *.inc, *.js",xbCfgVal_AsSectionValue);
		else
			mcTool.m_htmlTypes2m = ckeyRoot2m.Value["DynamicTypes"].AsString;

		if (!ckeyRoot2m.ValueExists("Utf8types"))
			ckeyRoot2m.SetValue("Utf8types",mcTool.m_utf8types2m="*.htm, *.html, *.xml, *.cgi, *.inc, *.js",xbCfgVal_AsSectionValue);
		else
			mcTool.m_utf8types2m = ckeyRoot2m.Value["Utf8types"].AsString;
	}
	xbCfgKey ckeyRoot2(xbCfgAppli,"MPFS2");//Settings specifics to MPFS2
	{
		if (!ckeyRoot2.ValueExists("DynamicTypes"))
			ckeyRoot2.SetValue("DynamicTypes",mcTool.m_htmlTypes = "*.htm, *.html, *.xml, *.cgi",xbCfgVal_AsSectionValue);
		else
			mcTool.m_htmlTypes = ckeyRoot2.Value["DynamicTypes"].AsString;

		if (!ckeyRoot2.ValueExists("Utf8types"))
			ckeyRoot2.SetValue("Utf8types",mcTool.m_utf8types="",xbCfgVal_AsSectionValue);
		else
			mcTool.m_utf8types = ckeyRoot2.Value["Utf8types"].AsString;
	}
	xbCfgKey ckeyRootAll(xbCfgAppli,"MPFSCOMMON");//Settings for MPFS2m & MPFS2
	{
		if (ckeyRoot2.ValueExists("NoGzipTypes"))
		{//For old config files (version <= 6.0.1.5)
			ckeyRootAll.SetValue("NoGzipTypes",ckeyRoot2.Value["NoGzipTypes"].AsString,xbCfgVal_AsSectionValue);
			ckeyRoot2.DeleteValue("NoGzipTypes");
		}
		if (ckeyRoot2.ValueExists("HexTypes"))
		{//For old config files (version <= 6.0.1.5)
			ckeyRootAll.SetValue("HexTypes",ckeyRoot2.Value["HexTypes"].AsString,xbCfgVal_AsSectionValue);
			ckeyRoot2.DeleteValue("HexTypes");
		}

		if (!ckeyRootAll.ValueExists("NoGzipTypes"))
			ckeyRootAll.SetValue("NoGzipTypes",mcTool.m_noGZipTypes = "*.inc, snmp.bib",xbCfgVal_AsSectionValue);
		else
			mcTool.m_noGZipTypes = ckeyRootAll.Value["NoGzipTypes"].AsString;
		if (!ckeyRootAll.ValueExists("HexTypes"))
			ckeyRootAll.SetValue("HexTypes",mcTool.m_hexTypes="*.hex",xbCfgVal_AsSectionValue);
		else
			mcTool.m_hexTypes = ckeyRootAll.Value["HexTypes"].AsString;
		if (!ckeyRootAll.ValueExists("TabSize"))
			ckeyRootAll.SetValue("TabSize",xbVariant((long)(mcTool.m_tabSize=MCTOOL_TABSIZE_DEFAULT),VT_UI4),xbCfgVal_AsSectionValue);
		else
		{
			eProtectBegin();
			try
			{
				mcTool.m_tabSize = ckeyRootAll.Value["TabSize"].AsUlong;
				if (mcTool.m_tabSize<=0 || mcTool.m_tabSize>=128)
					xbEuser::Throw("Invalid tabSize in config file");
			}
			catch(...)
			{
				mcTool.m_tabSize = MCTOOL_TABSIZE_DEFAULT;
				throw;
			}
			eProtectEndTrace();
		}

		xbCfgKey ckeyRootAllHttpPrint(ckeyRootAll,"HTTPPrint");
		if (!ckeyRootAllHttpPrint.ValueExists("HeaderAdd"))
			ckeyRootAllHttpPrint.SetValue("HeaderAdd",mcTool.m_HTTPPrint_headerAdd=
        "/**************************************************************\r\n"
        " * File generated by mcTool\r\n"
        " **************************************************************/\r\n\r\n"
			,xbCfgVal_AsSection);
		else
			mcTool.m_HTTPPrint_headerAdd = ckeyRootAllHttpPrint.Value["HeaderAdd"].AsString;

		if (!ckeyRootAllHttpPrint.ValueExists("TokenArgType_Number"))
			ckeyRootAllHttpPrint.SetValue("TokenArgType_Number",
			mcTool.m_HTTPPrint_TokenArgType_Number=MCTOOL_TOKEN_ARGTYPE_NUMBER
			,xbCfgVal_AsSectionValue);
		else
			mcTool.m_HTTPPrint_TokenArgType_Number = ckeyRootAllHttpPrint.Value["TokenArgType_Number"].AsString;

		if (!ckeyRootAllHttpPrint.ValueExists("TokenArgType_String"))
			ckeyRootAllHttpPrint.SetValue("TokenArgType_String",
			mcTool.m_HTTPPrint_TokenArgType_String=MCTOOL_TOKEN_ARGTYPE_STRING
			,xbCfgVal_AsSectionValue);
		else
			mcTool.m_HTTPPrint_TokenArgType_String = ckeyRootAllHttpPrint.Value["TokenArgType_String"].AsString;
	}
	MPFSOutputFormat fmt = MPFSOutputFormat_BIN;
	MPFShexFormat hexFmt = MPFShexFormat_24bits;
	xbString srcDir,prjDir,outFile;
	int cmdStatus = -1;
	MPFStokenVersion version = MPFStokenVersion_2;
	for(;;)
	{
		if (!mcTool.hasSwitch())
		{
			xbString arg;
			if(cmdStatus!=-1 || mcTool.hasArg())
				arg = mcTool.getSwitchArg();
			else
				arg = "?";

			if (cmdStatus==-1 && (arg.CompareNC("?") == 0 || arg.CompareNC("help") == 0
				|| arg.CompareNC("h") == 0))
			{
				mcTool_MPFS2_help(mcTool);
				return true;
			}
			switch(cmdStatus)
			{
			case -1:
				srcDir = arg.RetTrim();
				cmdStatus = 1;
				break;
			case 1:
				prjDir = arg.RetTrim();
				cmdStatus++;
				break;
			case 2:
				outFile = arg.RetTrim();
				cmdStatus++;
				break;
			default:
				xbEuser::Throw("Unexpected argument ("+arg+") on MPFS command line ! (-mpfs ? to show help)");
			}

			if (cmdStatus < 3)
				continue;


			if (mcTool.hasSwitchArg())
				xbEuser::Throw("Unexpected argument ("+mcTool.getSwitchArg()+") on MPFS command line ! (-mpfs ? to show help)");

			break;
		}

		xbString mode(mcTool.getSwitch());
		if (mode.CompareNC("?") == 0 || mode.CompareNC("help") == 0)
		{
			mcTool_MPFS2_help(mcTool);
			return true;
		}
		if (mode.CompareNC("BIN") == 0 || mode.CompareNC("B") == 0)
		{
			fmt = MPFSOutputFormat_BIN;
			continue;
		}
		if (mode.CompareNC("C18") == 0 || mode.CompareNC("C") == 0)
		{
			fmt = MPFSOutputFormat_C18;
			continue;
		}
		if (mode.CompareNC("ASM30") == 0 || mode.CompareNC("S") == 0)
		{
			fmt = MPFSOutputFormat_ASM30;
			continue;
		}
		if (mode.CompareNC("C32") == 0 || mode.CompareNC("X") == 0)
		{
			fmt = MPFSOutputFormat_C32;
			continue;
		}

		if (mode.CompareNC("HTML") == 0 || mode.CompareNC("H") == 0
			|| mode.CompareNC("DYNA") == 0 || mode.CompareNC("D") == 0)
		{
			mcTool.m_htmlTypes = mcTool.m_htmlTypes2m = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("UTF8") == 0)
		{
			mcTool.m_utf8types = mcTool.m_utf8types2m = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("XGZIP") == 0 || mode.CompareNC("Z") == 0)
		{
			mcTool.m_noGZipTypes = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("EXCLUDE") == 0 || mode.CompareNC("EXCL") == 0)
		{
			mcTool.m_excludeTypes = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("HEXFORMAT") == 0)
		{
			xbString sfmt(mcTool.getSwitchArg());
			if (sfmt.CompareNC("off") == 0)
			{
				hexFmt = MPFShexFormat_disabled;
				continue;
			}
			if (sfmt.CompareNC("8bits") == 0)
			{
				hexFmt = MPFShexFormat_8bits;
				continue;
			}
			if (sfmt.CompareNC("16bits") == 0)
			{
				hexFmt = MPFShexFormat_16bits;
				continue;
			}
			if (sfmt.CompareNC("24bits") == 0)
			{
				hexFmt = MPFShexFormat_24bits;
				continue;
			}
			if (sfmt.CompareNC("32bits") == 0)
			{
				hexFmt = MPFShexFormat_32bits;
				continue;
			}

			xbEuser::Throw("Unknown hex format ("+sfmt+") on MPFS command line ! (-mpfs ? to show help, must be 8bits, 16bits, 24bits, 32bits or off)");
			continue;
		}
		if (mode.CompareNC("HEX") == 0)
		{
			mcTool.m_hexTypes = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("HEXROW") == 0)
		{
			hexRow = xbStrToLong(mcTool.getSwitchArg(),0);
			continue;
		}
		if (mode.CompareNC("HEXROWLIM") == 0)
		{
			hexRowLimit = xbStrToLong(mcTool.getSwitchArg(),0);
			continue;
		}
		if (mode.CompareNC("V2") == 0)
		{
			version = MPFStokenVersion_2;
			continue;
		}
		if (mode.CompareNC("V2m") == 0)
		{
			version = MPFStokenVersion_2m;
			continue;
		}
		if (mode.CompareNC("TABSIZE") == 0)
		{
			try
			{
				mcTool.m_tabSize = xbStrToUlong(mcTool.getSwitchArg(),0);
				if (mcTool.m_tabSize<=0 || mcTool.m_tabSize>=128)
					xbEuser::Throw("Invalid tabSize");
			}
			catch(...)
			{
				mcTool.m_tabSize = MCTOOL_TABSIZE_DEFAULT;
				throw;
			}
			continue;
		}
		if (mode.CompareNC("HPHDR") == 0)
		{
			mcTool.m_HTTPPrint_headerAdd = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("HPARGNUM") == 0)
		{
			mcTool.m_HTTPPrint_TokenArgType_Number = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("HPARGSTR") == 0)
		{
			mcTool.m_HTTPPrint_TokenArgType_String = mcTool.getSwitchArg();
			continue;
		}
		if (mode.CompareNC("LIST") == 0)
		{
			bListTokens = true;
			bListTokensFull = false;
			continue;
		}
		if (mode.CompareNC("LISTF") == 0)
		{
			bListTokens = true;
			bListTokensFull = true;
			continue;
		}

		xbEuser::Throw("Unknown option '"+mode+"' for MPFS2 on command line ! (-mpfs ? to show help)");
	}

	if (srcDir.IsEmpty()
		|| prjDir.IsEmpty()
		|| outFile.IsEmpty())
		xbEuser::Throw("<SourceDir>, <ProjectDir> or <OutputFile> on command line of MPFS2 is empty ! (-mpfs ? to show help)");

	xbRefPtr<MPFSBuilder> prBuilder;
	if (version == MPFStokenVersion_2m)
	{
		xbRefPtr<MPFS2mBuilder> prB;
		MPFS2mBuilder::CreateObject(&prB);

		prB->Init(&mcTool,prjDir,srcDir,outFile);

		prB->DynamicTypes = mcTool.m_htmlTypes2m;
		prB->NonGZipTypes = mcTool.m_noGZipTypes;
		prB->HexTypes = mcTool.m_hexTypes;
		prB->HexRow = hexRow;
		prB->HexRowLimit = hexRowLimit;
		prB->HexFormat = hexFmt;
		prB->ExcludeTypes = mcTool.m_excludeTypes;
		prB->Utf8types = mcTool.m_utf8types2m;

		prBuilder = xbRefQIPtr<MPFSBuilder>(prB);
	}
	else
	{
		xbRefPtr<MPFS2Builder> prB;
		MPFS2Builder::CreateObject(&prB);

		prB->Init(&mcTool,prjDir,srcDir,outFile);

		prB->DynamicTypes = mcTool.m_htmlTypes;
		prB->NonGZipTypes = mcTool.m_noGZipTypes;
		prB->HexTypes = mcTool.m_hexTypes;
		prB->HexRow = hexRow;
		prB->HexRowLimit = hexRowLimit;
		prB->HexFormat = hexFmt;
		prB->ExcludeTypes = mcTool.m_excludeTypes;
		prB->Utf8types = mcTool.m_utf8types;

		prBuilder = xbRefQIPtr<MPFSBuilder>(prB);
	}

	prBuilder->AddDirectory(srcDir,"");

	prBuilder->Generate(fmt);

	if (bListTokens)
		prBuilder->ListUsedTokens(bListTokensFull);

	return true;
}

bool mcTool::confirmDynaVarAdd(const xbString &varName, MPFSvarType varType,const FileLoc &fileLoc)
{
	if (m_bDynaVarChangesOk)
		return true;

	xbMBretType ret;
	if (questionAnswer != 0)
		ret = questionAnswer>0?xbMBret_Yes:xbMBret_No;
	else
		ret = ::xbMBox(NULL,"A new dynamic call in html has been detected, please confirm that it is what you want:\n\n"
		"New call found: "+varName+"\n"
		"Used in:\n"+fileLoc.locationStr()
		+"\n\nIf you confirm, HTTPPrint.h will be changed, so your project too and the pic should be reprogrammed.\n"
		"  Yes    -> To confirm.\n"
		"  No     -> To reject changes and continue.\n"
		"  Cancel -> To reject changes and stop process.\n"
		,GetAppName()+" - Question",xbMBicon_Question,xbMBbtn_YesNoCancel);
	if (ret == xbMBret_Yes)
	{
		m_bDynaVarChangesOk = true;
		return true;
	}
	if (ret != xbMBret_No)
		xbEuser::Throw("Changes on dynamic calls rejected",fileLoc.locationStr()+":error:call to "+varName+" rejected");

	return false;
}

bool mcTool::confirmDynaVarDel(const xbString &varName, MPFSvarType varType)
{
	if (m_bDynaVarChangesOk)
		return true;

	xbMBretType ret;
	if (questionAnswer != 0)
		ret = questionAnswer>0?xbMBret_Yes:xbMBret_No;
	else
		ret = ::xbMBox(NULL,"An old dynamic call is no more used in html, please confirm that it is what you want:\n\n"
		"Call no more used: "+varName+"\n"
		+"\nIf you confirm, HTTPPrint.h will be changed to remove call.\n"
		"  Yes    -> To confirm.\n"
		"  No     -> To reject changes and continue.\n"
		"  Cancel -> To reject changes and stop process.\n"
		,GetAppName()+" - Question",xbMBicon_Question,xbMBbtn_YesNoCancel);
	if (ret == xbMBret_Yes)
	{
		m_bDynaVarChangesOk = true;
		return true;
	}
	if (ret != xbMBret_No)
		xbEuser::Throw("Changes on dynamic calls rejected","No more used call: "+varName);

	return false;
}

void mcTool::infoLog(const xbString &log)
{
	if (bSilent)
		return;
	fout << /*"INFO:" <<*/ log << "\n";
}

void mcTool::warningLog(const xbString &log,const FileLoc &fileLoc)
{
	ferr << fileLoc.locationStr();
	ferr << ":warning:" << log << "\n";
	m_cWarning++;
}

void mcTool::buildStart(const xbString &msg)
{
	m_bDynaVarChangesOk = false;
	if (bSilent)
		return;
	fout << msg << "\n";
}

void mcTool::dynaVarStart(bool bHttpPrintIdxLoaded)
{
	if (!bHttpPrintIdxLoaded)
		m_bDynaVarChangesOk = true;

	if (bSilent)
		return;
	fout << " -> HTTPPrint.idx loaded="<<(bHttpPrintIdxLoaded?"yes":"no") << "\n";
}
