/*
This product contains certain software code or other information
("AT&T Software") proprietary to AT&T Corp. ("AT&T").  The AT&T
Software is provided to you "AS IS".  YOU ASSUME TOTAL RESPONSIBILITY
AND RISK FOR USE OF THE AT&T SOFTWARE.  AT&T DOES NOT MAKE, AND
EXPRESSLY DISCLAIMS, ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND
WHATSOEVER, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, WARRANTIES OF
TITLE OR NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS, ANY
WARRANTIES ARISING BY USAGE OF TRADE, COURSE OF DEALING OR COURSE OF
PERFORMANCE, OR ANY WARRANTY THAT THE AT&T SOFTWARE IS "ERROR FREE" OR
WILL MEET YOUR REQUIREMENTS.

Unless you accept a license to use the AT&T Software, you shall not
reverse compile, disassemble or otherwise reverse engineer this
product to ascertain the source code for any AT&T Software.

(c) AT&T Corp. All rights reserved.  AT&T is a registered trademark of AT&T Corp.

***********************************************************************

History:

      11/30/99  - initial release by Hartmut Liefke, liefke@seas.upenn.edu
                                     Dan Suciu,      suciu@research.att.com
*/

//**************************************************************************
//**************************************************************************

#include "stdafx.h"

#ifdef NOTHREAD
/* filthy global! */
Session *session = NULL;
#endif

level_settings ppmsettings[XMILL_PPMDI_IDXS] = { 
  {1,6},
  {1,7},
  {1,8},
  {1,9},
  {4,9},
  {1,10},
  {2,10},
  {5,10},
  {6,10},
  {8,10},
  {10,10},
  {20,10},
  {1,12},
  {2,12},
  {1,16},
  {2,16},
  {3,16},
  {4,16},
  {8,16}
};

char* str_save(char *str)
{
	int len = strlen(str);
	char *s = new char[len+1];
	strcpy(s, str);
	s[len] = '\0';
	return s;
}

static bool strempty(char *s)
{
	return (s == NULL || s[0] == '\0');
}

/* implementations for Settings & Session classes */
Settings::Settings(Session *s)
{
	session = s;

	/* default settings */
	//outputtype = XMILL_OUT_MSXML;					// decompress to MSXML
	outputtype = XMILL_OUT_STRING_OR_FILE;		// decompress to text
	setPrintCompressorException(TRUE);			// generate exceptions when the 'p' compressor is hit
	noincrease = TRUE;								// don't allow XMI files to grow larger than the original XML
	copyxml = TRUE;									// copy XMI to XML when the XMI seems to be XML already
	delete_inputfiles = FALSE;						// don't delete input files
	use_bzip = XMILL_GPC_BZIP;		// use BZIP2 or GZIP as 2nd level compressor
	usestdout = FALSE;				// Don't use the standard output
	no_output = FALSE;				// No output
	timing = FALSE;					// Do timing (only used #ifdef TIMING)
   compressidx = XMILL_GZIP_IDX;
   overwrite_files = FALSE; // Is 1, if the user wants to overwrite all files
   skip_all_files = FALSE;  // Is 1, if the user want to skip all remaining files
   verbose = XMILL_VERBOSE_SILENT;            // (Don't) use verbose mode
#ifdef WIN32
	usedosnewline = TRUE;	// (Don't) use CR/LF i.s.o just LF
#else
	usedosnewline = FALSE;	// (Don't) use CR/LF i.s.o just LF
#endif

	/* default whitespace settings */
	globalleftwhitespacescompress     = WHITESPACE_IGNORE;
   globalrightwhitespacescompress    = WHITESPACE_IGNORE;
   globalattribwhitespacescompress   = WHITESPACE_IGNORE;
   globalfullwhitespacescompress     = WHITESPACE_IGNORE;

	/* default XML parts settings */
   ignore_comment = FALSE;
   ignore_cdata = FALSE;
   ignore_doctype = FALSE;
   ignore_pi = FALSE;
}

Settings::~Settings() {}

void Session::SetSettings (Settings *se)
{
	settings = se;
}

Session::Session()
{
	exprs = NULL;

   ppmdidata = new PPMDIData();

	for (int i=0; i<BLOCKSIZE_NUM; i++) {
		freeblocklists[i] = NULL;
	}

	/* initialize memory streamers */
	thetmpmem.Initialize(this, 5);
	themainmem.Initialize(this, 1000);
	theblockmem.Initialize(this, 1000);
	tmpmem = &thetmpmem;
	mainmem = &themainmem;
	blockmem = &theblockmem;

	/* some instances */
	enumhashtable = new EnumHashTable();
	uncomprcont = new UncompressContainerMan(this);
	compressman = new CompressMan(this);
	decompressman = new DecompressMan (this);

	/* initialize compressor list */
	numcompressors = 0;
	curcompressorlistsize = 25;
	compressorlist = (UserCompressorFactory **)mymalloc(curcompressorlistsize*sizeof(UserCompressorFactory *));

	/* compressor factories (self-registering) */
	/* the first four are a bit special, that's why the registering is different */
	AddCompressFactory((constantcompressor = new ConstantCompressorFactory(this, XMILL_COMP)));
	AddCompressFactory(new ConstantCompressorFactory(this, XMILL_DECOMP));
	AddCompressFactory((enumccompressfactory = new CompEnumerationCompressorFactory (this)));
	AddCompressFactory((enumdcompressfactory = new DecompEnumerationCompressorFactory (this)));
	/* normal registration */
	RegisterCompressorFactories();

	/* some more instances */
	curpath = new CurPath();
	dpathexprman = new DecompVPathExprMan(this);
	cpathexprman = NULL;
	globalclabeldict = NULL;
	globaldlabeldict = NULL;
	compresscontman = new CompressContainerMan(this);
	pathdict = new PathDict(this);

	pathtree = NULL;
	InitPathTree();

	/* set member variables */
	memory_cutoff = XMILL_MEMORY_CUTOFF;
	allocsize = 0;
	do_compression = TRUE;	// (Don't) initialize as compressor
	output_initialized = FALSE; // Output has (not) been initalized
	allocatedmemory = 0;

	/* initialize header state */
	fileheader_isread = FALSE;
	fileheader_iswritten = FALSE;

	/* initialize memory manager */
	#ifdef USE_FORWARD_DATAGUIDE
		curpathtreenode=NULL;
		pathtreemem=&mypathtreemem;
	#else
	pathtreemem=blockmem;
	#endif
	#if !defined(USE_FORWARD_DATAGUIDE) || defined(USE_NO_DATAGUIDE)
		pathdictmem=blockmem;
	#else
		pathdictmem=pathtreemem;
	#endif

	/* initialize default labels */
   elementpoundlabelid=LABEL_UNDEFINED;
	attribpoundlabelid=LABEL_UNDEFINED;

	/* initialize counters */
	structcontsizeorig=0;
	structcontsizecompressed=0;
	whitespacecontsizeorig=0;
	whitespacecontsizecompressed=0;
	specialcontsizeorig=0;
	specialcontsizecompressed=0;
	fileheadersize_orig=0;
	fileheadersize_compressed=0;
	compressorcontsizeorig=0;
	compressorcontsizecompressed=0;
	datacontsizeorig=0;
	datacontsizecompressed=0;

	/* initialize instances */
	parse = NULL;
	memoutput = NULL;
	memoutput2 = NULL;
	meminput = NULL;
	xmloutput = NULL;

	/* initialize memory */
	memoryalloc_buf=NULL;
	memoryalloc_curptr=NULL;
	memoryalloc_bufsize=0;
}

void Session::RegisterCompressorFactories()
{
	/* Add your own user compressor here. It's freed automatically. */

	/* combinator / separator compressors */
	AddCompressFactory(new DivSepCompressorFactory(this));		// 'seq'
	AddCompressFactory(new DivCombineCompressorFactory(this));	// 'seqcomb'
	AddCompressFactory(new OrSepCompressorFactory(this));			// 'or'
	AddCompressFactory(new RepeatSepCompressorFactory(this));	// 'rep'

	/* print compressor: compresses nothing, just printf()'s rejected elements */
	AddCompressFactory(new PrintCompressorFactory(this));		// 'p'

	/* default compressor */
	AddCompressFactory(new PlainTextCompressorFactory(this));	// 't'

	/* number compressors */
	AddCompressFactory(new UnsignedIntCompressorFactory(this));	// 'u'
	AddCompressFactory(new UnsignedInt8CompressorFactory(this));	// 'u8'
	AddCompressFactory(new SignedIntCompressorFactory(this));	// 'i'
	AddCompressFactory(new DeltaCompressorFactory(this));	// 'di'

	/* think 'PCX images' :-) */
	AddCompressFactory(new RunLengthCompressorFactory(this));	// 'rl'

	/* new compressors for version 0.8: arbitrary-length positive numbers */
	AddCompressFactory(new BinCompressorFactory(this));	// 'b'	base-2
	AddCompressFactory(new HexCompressorFactory(this));	// 'x'	base-16
	AddCompressFactory(new HexCaseCompressorFactory(this));	//	'X'	base-22 (10+6+6)
	AddCompressFactory(new BaseXNumCompressorFactory(this));	// 'basex'

	/* accepts RFC2045 compliant base64 data which is often used to store binary data in XML */
	AddCompressFactory(new Base64CompressorFactory(this));	// 'base64'
}

void Session::AddCompressFactory(UserCompressorFactory *compressor)
{
	if (++numcompressors > curcompressorlistsize) {
		/* list too small, extend it */
		curcompressorlistsize *= 2;
		compressorlist = (UserCompressorFactory **)
									realloc(compressorlist, 
												  curcompressorlistsize*sizeof(UserCompressorFactory *));
	}

	/* add compressor */
	compressorlist[numcompressors-1] = compressor;
}

void Session::FreeCompressFactories()
{
	for (int i=0; i<numcompressors; i++) {
		trydel (compressorlist[i]);
	}
	tryfree (compressorlist);
}

void Session::dropExprs()
{
	int i = 0;
	while(exprs && !strempty(exprs[i])) {
		trydel(exprs[i]);
		i++;
	}
	trydela(exprs);
}

void Session::PrintCompressorOutput(char *s)
{
	if (settings->printcompressorexception) {
		throw new XMillException(XMILL_ERR_PRINTCOMPRESSOR, s);
	} else {
		printf("%s\n", s);
	}
}

Session::~Session()
{
	DeInit();

   trydel(ppmdidata);

	dropExprs();

	trydel(enumhashtable);
	trydel(uncomprcont);
	trydel(compressman);
	trydel(decompressman);

	FreeCompressFactories();

	trydel(curpath);
	trydel(dpathexprman);
	trydel(compresscontman);
	trydel(pathdict);
	trydel(pathtree);

	trydel(parse);
	trydel(memoutput);
	trydel(memoutput2);
	trydel(meminput);
	trydel(xmloutput);
}

void Session::FSMInit()
   // This assigns the first two label IDs to '#' and '@#'
{
	elementpoundlabelid=globalclabeldict->CreateLabelOrAttrib("#",1,0);
	attribpoundlabelid=globalclabeldict->CreateLabelOrAttrib("#",1,1);
}

void Session::AddEnumCompressState(EnumCompressState *state)
   // This auxiliary function registers a new compressor state 'state'
   // within the EnumCompressFactory
{
	enumccompressfactory->AddEnumCompressState(state);
}

void Session::InitPathTree()
{
	/* get fresh pathtree instance */
	trydel(pathtree);
	pathtree = new PathTree(this);

	/* initialize pathtree hashtable */
	pathtree->InitPathTree();
}

void Session::DeInit()
{
/*	
	ReInit();
	cpathexprman->DropAllVPathExprs();
*/
	trydel(cpathexprman);
	trydel(globalclabeldict);
	trydel(globaldlabeldict);
	InitPathTree();
	curpath->RemoveAllLabels();

   mainmem->ReleaseMemory(0);
	blockmem->ReleaseMemory(0);
	tmpmem->ReleaseMemory(0);
	trydel (parse);
	trydel (memoutput);
	trydel (meminput);
	/* we must take care with deletion since 
		xmloutput->theoutput == memoutput2 
	 */
	if (xmloutput) {
		switch (settings->outputtype) {
			case XMILL_OUT_STRING_OR_FILE:
				((XMLStringOutput*)xmloutput)->SetOutput(NULL);
				memoutput2 = NULL;
				trydel(xmloutput);
				break;

			default:
				trydel (xmloutput);
				break;
		}
	}
	FreeBlocks();
}

/* type:
 *  l = left
 *  r = right
 *  a = attrib
 *  f = full
 *  e = all of the above ('e[verything]')
 * value: 
 *  WHITESPACE_STORETEXT
 *  WHITESPACE_STOREGLOBAL 
 *  WHITESPACE_IGNORE
 */
void Settings::setWhiteSpaceComp(char t, int value)
{
	if (t == 'l' || t == 'e')
		globalleftwhitespacescompress = value;
	if (t == 'r' || t == 'e')
	   globalrightwhitespacescompress = value;
	if (t == 'f' || t == 'e')
	   globalfullwhitespacescompress = value;
	if (t == 'a' || t == 'e')
	   globalattribwhitespacescompress = value;
}

/* type:
 *  r = comment ('r[emarks]')
 *  c = CDATA
 *  d = DOCTYPE
 *  p = PI
 *  e = all of the above ('e[verything]')
 *  other: no action (recommendation: 'n[one]')
 */
void Settings::setIgnore(char type, bool ignore) 
{
	if (type == 'r' || type == 'e')
		ignore_comment = (char)ignore;
	if (type == 'c' || type == 'e')
		ignore_cdata = (char)ignore;
	if (type == 'd' || type == 'e')
		ignore_doctype = (char)ignore;
	if (type == 'p' || type == 'e')
		ignore_pi = (char)ignore;
}

void Settings::setPrintCompressorException(char e)
{
	printcompressorexception = e;
}

void Session::SetCompress(bool compress)
{
	do_compression = (char)compress;
}

void Session::setExprs(char **pathexprs)
{
	int i = 0, len = 0;

	dropExprs();

	/* calculate # exprs */
	while(pathexprs && pathexprs[len++]);

	if (len > 0) {
		i = 0;
		exprs = new char*[len];
		while(!strempty(pathexprs[i])) {
			exprs[i] = str_save(pathexprs[i]);
			i++;
		}
		exprs[i] = NULL;
	}
}

void Session::InitMore()
{
	cpathexprman = new CompVPathExprMan (this);
	globalclabeldict = new CompLabelDict(this);
	globaldlabeldict = new DecompLabelDict(this);
}

void Session::Init(int type, int indenttype, int indentcount, char **pathexpr)
{
	if (type != XMILL_INIT_REINIT) {
		setExprs(pathexpr);
	}

	InitMore();

	globalclabeldict->Init(); // Initialize the label dictionary
	globaldlabeldict->Init(); // Initialize the label dictionary

	// Initializes the FSM structures.
	// It creates two labels '#' and '@#'
	FSMInit();

	//read .xmill path options
	if (exprs) {
		SetCompress();
		int i = 0;
		char *s = NULL;
		while (!strempty(exprs[i])) {
			s = exprs[i];
			cpathexprman->AddNewVPathExpr(s, s+strlen(s));
			i++;
		}
	}
	
#ifdef USE_FORWARD_DATAGUIDE
	extern void InitForwardDataGuide();

	InitForwardDataGuide();
#endif

	// In the compressor, we append two default paths: '//#' and '/'
	// to take care of all paths
	char *pathptr="//#";
	cpathexprman->AddNewVPathExpr(pathptr,pathptr+strlen(pathptr));
	pathptr="/";
	cpathexprman->AddNewVPathExpr(pathptr,pathptr+strlen(pathptr));

	globalclabeldict->FinishedPredefinedLabels();
		// We remember which labels are predefined (i.e. labels defined through FSMs)
		// All labels that are inserted later will be eliminated
		// between two parses of two input files.

	cpathexprman->InitWhitespaceHandling();
      // If the default white space handling for the path expression
      // is the global setting, then we replace that reference
      // by the global default value.
      // This is done after all options are parsed,
      // since the global white space options could come *after*
      // the path expressions have been inserted.

	if (type != XMILL_INIT_REINIT) {
		/* create compressing instances */
		parse = new XMLMemParse(this);
		memoutput = new MemOutput(settings);

		/* create decompressing instance(s) */
		meminput = new MemInput(this);
		switch (settings->outputtype) {
			case XMILL_OUT_STRING_OR_FILE:
				xmloutput = new XMLStringOutput(settings);
				memoutput2 = new MemOutput(settings);
				((XMLStringOutput*)xmloutput)->SetOutput(memoutput2);
				break;

			case XMILL_OUT_MSXML:
#ifdef WIN32
				memoutput2 = NULL;
				xmloutput = new MSXMLOutput();
#endif
				break;
		}

		/* set indentation type */
		if (indenttype > -1) {
			xmloutput->Init(indenttype, 0, indentcount);
			output_initialized = TRUE;
		}
	}

	fileheader_iswritten = FALSE;
	fileheader_isread = FALSE;

	structcontsizeorig=0;
	structcontsizecompressed=0;
	whitespacecontsizeorig=0;
	whitespacecontsizecompressed=0;
	specialcontsizeorig=0;
	specialcontsizecompressed=0;
	fileheadersize_orig=0;
	fileheadersize_compressed=0;
	compressorcontsizeorig=0;
	compressorcontsizecompressed=0;
	datacontsizeorig=0;
	datacontsizecompressed=0;

	tryfree (memoryalloc_buf);
	memoryalloc_curptr = NULL;
	memoryalloc_bufsize = 0;
}

void Settings::Init(int outtype, bool lossy, 
				  char usebzip,
				  bool usedos, 
				  char igntype, bool ignore, 
				  int gpcidx,
				  bool copyxml)
{
   outputtype = outtype;

	if (lossy) {
		setWhiteSpaceComp('e', WHITESPACE_IGNORE);
	} else {
		setWhiteSpaceComp('e', WHITESPACE_STOREGLOBAL);
	}

	/* init global stuph */
	this->copyxml = (char)copyxml;
	use_bzip = usebzip;
	usedosnewline = (char)usedos;
	setIgnore(igntype, ignore);
	compressidx = gpcidx;
};
