/*
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::UncompressFileHeader(SmallBlockUncompressor *uncompressor)
{
   char iswhitespaceignore;
   unsigned int magic = uncompressor->LoadSInt32(&iswhitespaceignore);
	unsigned int version = (magic - MAGIC_KEY_BASE) & 0xffff;
	magic -= version;

   if (magic>>16 != MAGIC_KEY_HIGH) {
      throw new XMillException(XMILL_ERR_NO_XMI, "The file is not a compressed XMill file!");
   }
   if (version > XMILL_CURRENT_VERSION) {
      throw new XMillException(XMILL_ERR_NO_XMI, "The XMI file is created by a higher version compressor!");
   }
#if 0
	/* add version compliancy semantics here, e.g., 
		distinct checks for major and minor versions.
		Current known versions:
			version 0x00.00	XMill original release (feb 1999 ??)
			version 0x01.00   XMill rewrite by Hedzer Westra (mar 2003)
									 contains int32 storage bugfix which changes encoded number format,
                            adds an extra uint32 for the compression index (ppmdi needs it for decompresion),
									 and adds BaseXNum compressors (basex, x, X, b, base64)
									should decompress version 0 files OK.
    */
   if (version < XMILL_CURRENT_VERSION) {
      throw new XMillException(XMILL_ERR_NO_XMI, "The XMI file is created by a lower version compressor!");
   }
#endif

   if (version >= XMILL_VERSION_1_0) {
      /* read extra uint32 which was not defined in v0.7 (XMI file version 0.0) */
      unsigned int extra = uncompressor->LoadUInt32();
   }

   if(iswhitespaceignore) {
      settings->globalfullwhitespacescompress=WHITESPACE_IGNORE;
      if(output_initialized==0)
         xmloutput->Init(XMLINDENT_SPACES,0,1);
   } else {
      settings->globalfullwhitespacescompress=WHITESPACE_STOREGLOBAL;
      if(output_initialized==0)
         xmloutput->Init(XMLINDENT_NONE,0,1);
   }
   dpathexprman->Load(uncompressor);
}

char Session::UncompressBlockHeader(Input *input)
{
   SmallBlockUncompressor  uncompressor(this, input);

   if (!fileheader_isread) {
      UncompressFileHeader(&uncompressor);
      fileheader_isread = TRUE;
   }
   else
   {
      if(input->IsEndOfFile())
         return TRUE;
   }

   memory_cutoff=uncompressor.LoadUInt32();
   SetMemoryAllocationSize(memory_cutoff);
   uncomprcont->Load(&uncompressor);
   globaldlabeldict->Load(&uncompressor);
   decompressman->UncompressSmallGlobalData(&uncompressor);
   uncomprcont->AllocateContMem();
   uncomprcont->UncompressSmallContainers(&uncompressor);

   return FALSE;
}

void XMill::UncompressIt(Input *input, XMLOutput *xoutput)
{
   unsigned long blockidx=0;
   UncompressContainer  *uncomprtreecont = NULL;
   UncompressContainer  *uncomprwhitespacecont = NULL;
   UncompressContainer  *uncomprspecialcont = NULL;
   char header[XMILL_HEADER_PEEK_SIZE];
   unsigned int ppmdistart = XMILL_HEADER_PPMDI_START;

#ifdef TIMING
   c1=clock();
#endif

	/* check for gp compressor header, assume GZIP (which has no header) */
	settings->use_bzip = XMILL_GPC_GZIP;
	input->PeekData(header, XMILL_HEADER_PEEK_SIZE);
	if (!strncmp(header, XMILL_HEADER_BZIP, sizeof(XMILL_HEADER_BZIP)-1)) {
		settings->use_bzip = XMILL_GPC_BZIP;
	} else if (!strncmp(header, XMILL_HEADER_NOZIP, sizeof(XMILL_HEADER_NOZIP)-1)) {
		settings->use_bzip = XMILL_GPC_NONE;
	} else if (!strncmp(header, XMILL_HEADER_XML, sizeof(XMILL_HEADER_XML)-1)) {
		if (settings->copyxml && settings->outputtype == XMILL_OUT_STRING_OR_FILE) {
			/* we have to copy the XML file now */
			char isend = FALSE;
			char buffer[STRLEN];
			do {
				isend = input->ReadData(buffer, STRLEN);
				((XMLStringOutput*)xoutput)->GetOutput()->StoreData(buffer, 
					isend ? strlen(buffer) : STRLEN);
			} while (!isend);
			goto cleanup;
		} else {
			/* just fail with an error */
			throw new XMillException(XMILL_ERR_XMI_IS_XML, 
				"This is already an XML file. "
				"Enable 'copyxml' XMill setting to have XMill handle this case transparantly.");
		}
	} else if (!memcmp(header, &ppmdistart, sizeof(ppmdistart)-1)) {
      /* we only checked the first 3 Bytes because the compression index is 'hidden' in the last Byte. 
         Now extract this info */
      memcpy(&ppmdistart, header, sizeof(ppmdistart));
      if (ppmdistart >> 24 < XMILL_PPMDI_IDXS) {
         settings->compressidx = ppmdistart >> 24;
   		settings->use_bzip = XMILL_GPC_PPMDI;
      }
	}

	session->SetCompress(false);

   session->globaldlabeldict->Init();

   session->mainmem->StartNewMemBlock();

   while(session->UncompressBlockHeader(input)==0)
   {
      session->decompressman->UncompressLargeGlobalData(input);
      session->uncomprcont->UncompressLargeContainers(input);

      session->uncomprcont->Init();

      uncomprtreecont      =session->uncomprcont->GetContBlock(0)->GetContainer(0);
      uncomprwhitespacecont=session->uncomprcont->GetContBlock(0)->GetContainer(1);
      uncomprspecialcont   =session->uncomprcont->GetContBlock(0)->GetContainer(2);
#ifdef TIMING
      c2=clock();
#endif

      session->DecodeTreeBlock(uncomprtreecont,uncomprwhitespacecont,uncomprspecialcont,xoutput);
#ifdef TIMING
      c3=clock();
#endif
/*
      if(settings->verbose >= XMILL_VERBOSE_STATS)
         printf("#%3lu  => Uncompress: %f sec   Decode: %f sec\n",
            blockidx,
            (float)(c2-c1)/(float)CLOCKS_PER_SEC,
            (float)(c3-c2)/(float)CLOCKS_PER_SEC);
*/
#ifdef TIMING
      ct1+=c2-c1;
      ct2+=c3-c2;
#endif

      session->uncomprcont->FinishUncompress();
      session->uncomprcont->ReleaseContMem();
      session->decompressman->FinishUncompress();
      session->blockmem->ReleaseMemory(1000);
#ifdef TIMING
      c1=clock();
#endif
      blockidx++;
   }
#ifdef TIMING
   if(settings->verbose >= XMILL_VERBOSE_STATS)
      printf("%fs + %fs = %fs\n",(float)ct1/(float)CLOCKS_PER_SEC,
                                 (float)ct2/(float)CLOCKS_PER_SEC,
                                 (float)(ct1+ct2)/(float)CLOCKS_PER_SEC);
#endif

   session->mainmem->RemoveLastMemBlock();

cleanup:
	/* reset */
   session->globaldlabeldict->Reset();
   session->fileheader_isread = FALSE;
}

void XMill::Uncompress(char *sourcefile,char *destfile)
{
   Input                input(session);
#ifdef TIMING
   clock_t              c1,c2,c3,ct1=0,ct2=0;
#endif
	char saved_use_bzip = settings->use_bzip;

	/* create/open files */
   if(settings->AskOverwriteFile(destfile)==0)
      return;

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

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

	/* decompress */
   UncompressIt(&input, session->xmloutput);

	/* close files */
   input.CloseFile();
   session->xmloutput->CloseFile();

   if(settings->delete_inputfiles)
      RemoveFile(sourcefile);

	settings->use_bzip = saved_use_bzip;
}

