/*
 * Copyright (c) 1994 Paul Vojta.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * NOTE:
 *	xdvi is based on prior work as noted in the modification history, below.
 */

/*
 * DVI previewer for X.
 *
 * Eric Cooper, CMU, September 1985.
 *
 * Code derived from dvi-imagen.c.
 *
 * Modification history:
 * 1/1986	Modified for X.10	--Bob Scheifler, MIT LCS.
 * 7/1988	Modified for X.11	--Mark Eichin, MIT
 * 12/1988	Added 'R' option, toolkit, magnifying glass
 *					--Paul Vojta, UC Berkeley.
 * 2/1989	Added tpic support	--Jeffrey Lee, U of Toronto
 * 4/1989	Modified for System V	--Donald Richardson, Clarkson Univ.
 * 3/1990	Added VMS support	--Scott Allendorf, U of Iowa
 * 7/1990	Added reflection mode	--Michael Pak, Hebrew U of Jerusalem
 * 1/1992	Added greyscale code	--Till Brychcy, Techn. Univ. Muenchen
 *					  and Lee Hetherington, MIT
 * 4/1994	Added DPS support, bounding box
 *					--Ricardo Telichevesky
 *					  and Luis Miguel Silveira, MIT RLE.
 */



#include "dvi_init.h"
#include "dviwin.h"



#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <qbitmap.h> 
#include <qfileinfo.h>
#include <stdlib.h>


extern "C" {
#include "dvi.h"
}

#include "fontpool.h"
#include "glyph.h"
#include "oconfig.h"

extern char *xmalloc (unsigned, const char *);

#ifndef	DVI_BUFFER_LEN
#define	DVI_BUFFER_LEN	512
#endif

extern unsigned char	dvi_buffer[DVI_BUFFER_LEN];
extern struct frame	*current_frame;
extern struct frame	frame0;	/* dummy head of list */

#define	PK_PRE		247
#define	PK_ID		89
#define	PK_MAGIC	(PK_PRE << 8) + PK_ID
#define	GF_PRE		247
#define	GF_ID		131
#define	GF_MAGIC	(GF_PRE << 8) + GF_ID
#define	VF_PRE		247
#define	VF_ID_BYTE	202
#define	VF_MAGIC	(VF_PRE << 8) + VF_ID_BYTE

#define	dvi_oops(str)	(dvi_oops_msg = (str.utf8()), longjmp(dvi_env, 1))

static	Boolean	font_not_found;

/*
 * DVI preamble and postamble information.
 */

static	long	numerator, denominator;

/*
 * Offset in DVI file of last page, set in read_postamble().
 */
static	long	last_page_offset;






/** define_font reads the rest of the fntdef command and then reads in
 *  the specified pixel file, adding it to the global linked-list
 *  holding all of the fonts used in the job.  */

font *define_font(FILE *file, unsigned int cmnd, font *vfparent, QIntDict<struct font> *TeXNumberTable, class fontPool *font_pool)
{
  int   TeXnumber = num(file, (int) cmnd - FNTDEF1 + 1);
  long  checksum  = four(file);
  int   scale     = four(file);
  int   design    = four(file);
  int   len       = one(file) + one(file); /* sequence point in the middle */
  char *fontname  = xmalloc((unsigned) len + 1, "font name");
  Fread(fontname, sizeof(char), len, file);
  fontname[len] = '\0';

#ifdef DEBUG_FONTS
  kdDebug() << "Define font \"" << fontname << "\" scale=" << scale << " design=" << design << endl;
#endif

  //  struct font *fontp = font_pool->appendx(fontname, fsize, checksum, scale * scale_dimconv / (1<<20));
  struct font *fontp = font_pool->appendx(fontname, checksum, scale, design, vfparent);

  // Insert font in dictionary and make sure the dictionary is big
  // enough.
  if (TeXNumberTable->size()-2 <= TeXNumberTable->count())
    // Not quite optimal. The size of the dictionary should be a
    // prime. I don't care
    TeXNumberTable->resize(TeXNumberTable->size()*2); 
  TeXNumberTable->insert(TeXnumber, fontp);
  return fontp;
}


void dvifile::process_preamble(void)
{
  unsigned char   k;

  if (one(file) != PRE)
    dvi_oops(i18n("DVI file doesn't start with preamble."));
  if (one(file) != 2)
    dvi_oops(i18n("Wrong version of DVI output for this program."));

  numerator     = four(file);
  denominator   = four(file);
  magnification = four(file);
  dimconv       = (((double) numerator * magnification) / ((double) denominator * 1000.));
  dimconv       = dimconv * (((long) pixels_per_inch)<<16) / 254000;

  // Read the generatorString (such as "TeX output ..." from the DVI-File)
  char	job_id[300];
  k             = one(file);
  k             = (k > 299) ? 299 : k;
  Fread(job_id, sizeof(char), (int) k, file);
  job_id[k] = '\0';
  generatorString = job_id;
}

/*
 *      find_postamble locates the beginning of the postamble
 *	and leaves the file ready to start reading at that location.
 */
#define	TMPSIZ	516	/* 4 trailer bytes + 512 junk bytes allowed */
void dvifile::find_postamble(void)
{
  long	pos;
  unsigned char	temp[TMPSIZ];
  unsigned char	*p;
  unsigned char	*p1;
  unsigned char	byte;

  Fseek(file, (long) 0, 2);
  pos = ftell(file) - TMPSIZ;
  if (pos < 0)
    pos = 0;
  Fseek(file, pos, 0);
  p = temp + fread((char *) temp, sizeof(char), TMPSIZ, file);
  for (;;) {
    p1 = p;
    while (p1 > temp && *(--p1) != TRAILER)
      ;
    p = p1;
    while (p > temp && *(--p) == TRAILER)
      ;
    if (p <= p1 - 4)
      break;	/* found 4 TRAILER bytes */
    if (p <= temp)
      dvi_oops(i18n("DVI file corrupted"));
  }
  pos += p - temp;
  byte = *p;
  while (byte == TRAILER) {
    Fseek(file, --pos, 0);
    byte = one(file);
  }
  if (byte != 2)
    dvi_oops(i18n("Wrong version of DVI output for this program"));
  Fseek(file, pos - 4, 0);
  Fseek(file, sfour(file), 0);
}


void dvifile::read_postamble(void)
{
  unsigned char   cmnd;

  if (one(file) != POST)
    dvi_oops(i18n("Postamble doesn't begin with POST"));
  last_page_offset = four(file);
  if (numerator != four(file) || denominator != four(file) || magnification != four(file))
    dvi_oops(i18n("Postamble doesn't match preamble"));
  /* read largest box height and width */
  int unshrunk_page_h = (spell_conv(sfour(file)) >> 16);//@@@ + basedpi;
  int unshrunk_page_w = (spell_conv(sfour(file)) >> 16);//@@@ + basedpi;
  (void) two(file);	/* max stack size */
  total_pages = two(file);
  font_not_found = False;
  while ((cmnd = one(file)) >= FNTDEF1 && cmnd <= FNTDEF4)
    (void) define_font(file, cmnd, (struct font *) NULL, &tn_table, font_pool);
  if (cmnd != POSTPOST)
    dvi_oops(i18n("Non-fntdef command found in postamble"));
  if (font_not_found)
    KMessageBox::sorry( 0, i18n("Not all pixel files were found"));

  // Now we remove all those fonts from the memory which are no longer
  // in use.
  font_pool->release_fonts();
}

void dvifile::prepare_pages()
{
#ifdef DEBUG_DVIFILE
  kdDebug() << "prepare_pages" << endl;
#endif

  page_offset = (long *) xmalloc((unsigned) total_pages * sizeof(long), "page directory");
  int i = total_pages;
  page_offset[--i] = last_page_offset;
  Fseek(file, last_page_offset, 0);
  /* Follow back pointers through pages in the DVI file, storing the
     offsets in the page_offset table. */
  while (i > 0) {
    Fseek(file, (long) (1+4+(9*4)), 1);
    Fseek(file, page_offset[--i] = four(file), 0);
  }
}


dvifile::dvifile(QString fname, fontPool *pool)
{
#ifdef DEBUG_DVIFILE
  kdDebug() << "init_dvi_file: " << fname << endl;
#endif

  file        = NULL;
  page_offset = NULL;
  font_pool   = pool;

  file = fopen(fname.ascii(), "r");
  if (file == NULL) {
    /*@@@    KMessageBox::error( this,
			i18n("File error!\n\n") +
			i18n("Could not open the file\n") + 
			fname);
    */		
    return;
  }

  filename = fname;
  tn_table.clear();
  
  process_preamble();
  find_postamble();
  read_postamble();
  prepare_pages();

  return;
}

dvifile::~dvifile()
{
#ifdef DEBUG_DVIFILE
  kdDebug() << "destroy dvi-file" << endl;
#endif

  if (font_pool != 0)
    font_pool->mark_fonts_as_unused();

  if (page_offset != NULL)
    free(page_offset);
  if (file != NULL)
    fclose(file);
}
