/*
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"

void Session::StoreFileHeader(Compressor *compressor)
{
   MemStreamer tmpoutputstream(this, 1);

   tmpoutputstream.StoreSInt32(
      (settings->globalfullwhitespacescompress==WHITESPACE_IGNORE) ? 1 : 0,
      MAGIC_KEY);

   /* store extra uint32, all bits still reserved */
   tmpoutputstream.StoreUInt32(0);

   cpathexprman->Store(&tmpoutputstream);

   compressor->CompressMemStream(&tmpoutputstream);
}

void Session::CompressBlockHeader(Compressor *compressor,unsigned long totaldatasize)
{
   MemStreamer memstream(this);

   memstream.StoreUInt32(totaldatasize);
// First, we put info about path expressions, containers, and labels into
// container 'tmpoutputstream'

   compresscontman->StoreMainInfo(&memstream);

   compressor->CompressMemStream(&memstream);

   // Let's store the new labels from the label dictionary
   globalclabeldict->Store(compressor);

   compressman->CompressSmallGlobalData(compressor);

   compresscontman->CompressSmallContainers(compressor);
}

void Session::CompressCurrentBlock(Output *output,unsigned long totaldatasize)
{
   {
      Compressor     compressor(settings, output);
      unsigned long  headersize,headersize_compressed;

      if(!fileheader_iswritten)
      {
         StoreFileHeader(&compressor);
         fileheader_iswritten = TRUE;
      }
      CompressBlockHeader(&compressor,totaldatasize);
      compressor.FinishCompress(&headersize,&headersize_compressed);

      fileheadersize_orig        +=headersize;
      fileheadersize_compressed  +=headersize_compressed;
   }

   compressman->CompressLargeGlobalData(output);

   // Let's compress the actual containers
   compresscontman->CompressLargeContainers(output);
}

void XMill::EndCompressBlock()
{
	#ifndef USE_FORWARD_DATAGUIDE
		session->pathtree->ReleaseMemory();
	#endif

	session->compresscontman->ReleaseMemory();
	session->blockmem->ReleaseMemory(1000);
}

/* externally available methods */
int XMill::CompressIt(XMLParse *xmlparse, Output *output)
{
   char endmarker;
	bool initrun = false;

	try {
		if (!saxclient) {
			/* initialize parser etc. */
			InitCompressIt();
			initrun = true;
		}
		do {
			if (initrun) {
				/* initialize containers */
				InitCompressItRun();
			}
			/* compress a run until we reach the end of the input block */
			if ((endmarker = CompressItRun(xmlparse)) != XMILL_END_BLOCK) {
				/* end of data reached, or internal data was cut off */
				/* compress run data into output */
				EndCompressItRun(output);
				/* run data must be re-initialized */
				initrun = true;
			}
		} while (endmarker == XMILL_END_NONE);
	} catch (XMillException *e) {
		/* reset compressor */
		EndCompressBlock();
		EndCompressIt();
		throw e;
	}

	if (endmarker == XMILL_END_DATA) {
		/* clear memory and release parser instance */
		EndCompressIt();
	}

	return endmarker;
}

void XMill::EndCompressIt()
{
	trydel(saxclient);
	session->mainmem->RemoveLastMemBlock();
}

void XMill::InitCompressIt()
{
	trydel(saxclient);
   saxclient = new SAXClient(session);

	session->SetCompress();
   session->mainmem->StartNewMemBlock();
   session->curpath->curdepth=0;

	#ifdef USE_FORWARD_DATAGUIDE
		session->pathdict->Init();
		session->pathtree->CreateRootNode();
	#endif
}

void XMill::InitCompressItRun()
{
#ifndef USE_FORWARD_DATAGUIDE
	session->pathdict->Init();
	session->pathtree->CreateRootNode();
#else
	session->pathdict->ResetContBlockPtrs();
#endif

	session->globalcontblock      = session->compresscontman->CreateNewContainerBlock(3,0,NULL,NULL);
	session->globaltreecont       = session->globalcontblock->GetContainer(0);
	session->globalwhitespacecont = session->globalcontblock->GetContainer(1);
	session->globalspecialcont    = session->globalcontblock->GetContainer(2);
#ifdef TIMING
	if(settings->timing)
		c1=clock();
#endif
}

char XMill::CompressItRun(XMLParse *xmlparse)
{
	return xmlparse->DoParsing(saxclient);
}

void XMill::EndCompressItRun(Output *output)
{
   unsigned long  totaldatasize;

#ifdef TIMING
	if(settings->timing)
		c2=clock();
#endif

	session->compresscontman->FinishCompress();

	totaldatasize= session->compresscontman->GetDataSize()+
						session->compressman->GetDataSize();

	session->CompressCurrentBlock(output,totaldatasize);
#ifdef TIMING
	if(settings->timing) {
		c3=clock();
		parsetime+=(c2-c1);
		compresstime+=(c3-c2);
	}
#endif
#ifdef PROFILE
	if(settings->verbose >= XMILL_VERBOSE_STATS) {
		printf("Pathtree size: %lu\n",session->pathtreemem.GetSize());
		session->pathtree->PrintProfile();
		session->pathdict->PrintProfile();
	}
#endif

	EndCompressBlock();
}

void XMill::Compress(char *srcfile,char *destfile)
{
   XMLParse       xmlparse(session);
   Output         output(settings);
#ifdef TIMING
   clock_t        c1,c2,c3;
#endif
   int            parsetime=0,compresstime=0;

   // We count the overal sizes of the compressed/uncompressed
   // structure, white space, and special (DTD...) containers
   // We initialize the counters here
   session->InitSpecialContainerSizeSum();

   session->fileheader_iswritten = FALSE;

   if(!settings->AskOverwriteFile(destfile))
      return;

   if(!xmlparse.OpenFile(srcfile))
   {
		XMillException *e = new XMillException(XMILL_ERR_FILENOTFOUND, "Could not find file '");
      e->ErrorCont(srcfile);
      e->ErrorCont("'!");
      /*e->PrintErrorMsg();
		delete e;
      return;*/
		throw e;
   }

	if(!output.myCreateFile(settings->no_output ? (char*)"" : destfile))
   {
      XMillException *e = new XMillException(XMILL_ERR_CREATE, "Could not create output file '");
      e->ErrorCont(destfile);
      /*e->PrintErrorMsg();
		delete e;*/
      xmlparse.CloseFile();
		throw e;      
		//return;
   }

   try {
		if (CompressIt(&xmlparse, &output) != XMILL_END_DATA) {
         /* XML file not valid! */
			throw new XMillException(XMILL_ERR_PARSE, "There are unclosed XML tags!");
		}
   } catch(XMillException *e) {
      output.CloseAndDeleteFile();
      xmlparse.CloseFile();
      throw e;
   }

#ifdef TIMING
   if(settings->timing)
      printf("%fs + %fs = %fs\n",(float)parsetime/(float)CLOCKS_PER_SEC,
                                 (float)compresstime/(float)CLOCKS_PER_SEC,
                                 (float)(parsetime+compresstime)/(float)CLOCKS_PER_SEC);
#endif

	if(settings->verbose >= XMILL_VERBOSE_STATS) {
      session->PrintSpecialContainerSizeSum();
#ifdef PROFILE
      session->curpath->PrintProfile();
#endif
	}

	/* close & delete some files */
   xmlparse.CloseFile();
   output.CloseFile();
   if(settings->delete_inputfiles)
      RemoveFile(srcfile);

	/* reset some stuph */
	session->Init(XMILL_INIT_REINIT);
}
