/* specialhost.c */

  /**************************************************************/
  /* 								*/
  /* Bitte alle Aenderungen im Programm deutlich		*/
  /* markieren und mir zusenden. Ich werde dann			*/
  /* versuchen immer eine einheitliche Version 			*/
  /* zusammenzubasteln.						*/
  /*	30.07.90		(c) Georg Hessmann		*/
  /*								*/
  /* Bug's und Anregungen bitte an:				*/
  /*		     hessmann@unipas.fmi.uni-passau.de		*/
  /* oder an:							*/
  /*				Georg Hessmann			*/
  /*				Oberer Markt 7			*/
  /*				8712 Volkach			*/
  /*				Germany				*/
  /*								*/
  /*								*/
  /* Program zur Auswertung der special Strings von TeX		*/
  /* wird von "ShowDVI" und "DVIprint" verwendet.		*/
  /* Das Programm muss im Hintergrund laufen noch bevor		*/
  /* die anderen Programme gestartet werden.			*/
  /* Die ist nur ein Beispiel, das die Arbeitsweise		*/
  /* dokumentieren soll.					*/
  /*								*/
  /* Das bisherige Format des special Kommandos:		*/
  /*	\special{ifffile=name					*/
  /*		 hoffset=fxx					*/
  /*		 voffset=fxx					*/
  /*		 hsize=fxx					*/
  /*		 vsize=fxx }					*/
  /* wobei f eine real-Zahl ist und xx eine Einheit wie		*/
  /* in den anderen Programmen definiert.			*/
  /*								*/
  /* Das Programm benoetigt die "iff.library"!			*/
  /*								*/
  /* Version:							*/
  /*								*/
  /* -- 0.70	30.07.90	(hes) Georg Hessmann		*/
  /* -- 0.71	16.11.90	Verbesserungen von 'flextr.c'	*/
  /*				Michael Illgner			*/
  /*								*/
  /**************************************************************/
  
  
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/exec.h>
#include <exec/ports.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>

#include "iff.h"
#include "special.h"
#include "specialparse.h"
#include "specialhost.h"


#ifdef LATTICE
#  include <proto/intuition.h>
#  include <proto/graphics.h>
#  include <proto/exec.h>
#  include <proto/dos.h>
#endif

#ifdef AZTEC_C
#  include <functions.h>
#endif

#define DOSNAMESIZE	150

#ifndef ZERO
#  define ZERO		0L
#endif


#ifdef LATTICE
long	_stack		= 4096;
char *	_procname	= "SpecialHost";
long	_priority	= 0;
long	_BackGroundIO	= 0;	/* no IO => no Fatal messages :-( */
extern BPTR _Backstdout;
#endif


/* global variable */
static struct special_map map;
static char		bitfilename[DOSNAMESIZE];

struct MsgPort 		*special_port	= NULL;
IFFFILE			ifffile		= NULL;
struct BitMap		*bmap		= NULL;


extern struct Library		*IFFBase;		/* defined in specialwin.c */
extern struct GfxBase		*GfxBase;
extern struct IntuitionBase	*IntuitionBase;



/* local funktions */
static 	      void	free_bmap		(struct BitMap *bmap);
static	      int	get_file_date		(char *name,
						 long *days,
						 long *mins,
						 long *ticks);
static	      int	test_date		(char *iffile, char *bitfile, long *ssum);
static        void	write_bmap		(char *name,
						 struct BitMap *bmap,
						 long ssum,
						 long width, long height);
static	      int	get_iff_size		(char *name,
						 long *width,
						 long *height);
static struct BitMap   *load_iff_picture	(char *name,
						 long *width,
						 long *height);
static struct BitMap   *work_with_message	(struct special_msg *message,
						 int draw_modus, int base_dpi);
static 	      void	scale_bmap		(struct special_map *map,
						 struct BitMap **old_bmap,
						 struct parse_result *res,
						 int base_dpi);



void close_port_bitm(void)
{
  if (special_port != NULL) {
    DeletePort(special_port);
  }
  free_bmap(bmap);
}

static void free_bmap(struct BitMap *bmap)
{
  int i;

  if (bmap != NULL) {
    for (i=0; i< bmap->Depth; i++) {
      if (bmap->Planes[i] != NULL) {
	FreeMem(bmap->Planes[i],bmap->BytesPerRow*bmap->Rows);
        bmap->Planes[i] = NULL;
      }
      free(bmap);
    }
  }
}

static int get_file_date(char *name, long *days, long *mins, long *ticks)
{
   struct FileInfoBlock *fib;
   int ret = TRUE;
   BPTR lock;

   lock = Lock(name,ACCESS_READ);
   if (lock != ZERO) {
     fib = (struct FileInfoBlock *)malloc(sizeof(struct FileInfoBlock));
     if (fib != NULL) {
       if (Examine(lock,fib)!=0) {
         *days  = fib->fib_Date.ds_Days;
         *mins  = fib->fib_Date.ds_Minute;
         *ticks = fib->fib_Date.ds_Tick;
       }
       else {
         ret = FALSE;
       }
       free(fib);
       UnLock((BPTR)lock);
     }
     else {
       ret = FALSE;
     }
   }
   else {
     ret = FALSE;
   }
   
   return ret;
}

static int test_date(char *iffile, char *bitfile, long *ssum)
{
  long days1, days2, mins1, mins2, ticks1, ticks2;
  int exist1, exist2;
  int ret;
  FILE *file;
  long buffer;

  days1 = days2 = mins1 = mins2 = ticks1 = ticks2 = 0L;

  exist1 = get_file_date(iffile, &days1, &mins1, &ticks1);
  exist2 = get_file_date(bitfile, &days2, &mins2, &ticks2);

  if (!exist2) {			/* exist no bitmap-file */
    ret = FALSE;
  }
  else {
    if (!exist1 && exist2) {		/* exist bitmap but no iff-file */
      ret = TRUE;
    }
    else {
      if (days2 > days1) {		/* is bitmap-file newer than iff-file? */
        ret = TRUE;
      }
      else {
        if (days2 == days1 && mins2 > mins1) {
          ret = TRUE;
        }
        else {
          if (mins2 == mins1 && ticks2 > ticks1) {
            ret = TRUE;
          }
          else {
            ret = FALSE;	/* -> iff-file is newar than the bitmap-file */
          }
        }
      }
    }
  }

  if (ret) {
    if ((file = fopen(bitfile,"r")) != NULL) {
      if (fread((char *)&buffer,4,1,file) == 1) {
        if (buffer != MAGIC_WORD) {
          ret = FALSE;
          pline("  wrong magic word in file %s!",bitfile);
        }
        else {
          if (fread((char *)&buffer,4,1,file) != 1) {
            ret = FALSE;
          }
          else {
            *ssum = buffer;
          }
        }
      }
      fclose(file);
    }
  }

  return ret;
}


static void write_bmap(char *name, struct BitMap *bmap, long ssum, long width, long height)
{
  FILE *bmap_file;
  long buffer;
  int i;

  /* File Format: 4Bytes - magic word "SPEC"			*/
  /*		  4Bytes - sum of \special-string		*/
  /*		  4Bytes - length of rows in bits		*/
  /*		  4Bytes - number of rows			*/
  /*		  rows						*/
  /*		    length of one row is word (16Bit) aligned!!	*/

  if ((bmap_file = fopen(name,"w")) == NULL) {
  	pline("Can't write to bitmap-file (%s)!",name);
  	return;
  }
  buffer = (long)MAGIC_WORD;
  fwrite((char *)&buffer,4,1,bmap_file);
  buffer = ssum;
  fwrite((char *)&buffer,4,1,bmap_file);
  buffer = width;
  fwrite((char *)&buffer,4,1,bmap_file);
  buffer = height;
  fwrite((char *)&buffer,4,1,bmap_file);
  for (i=0; i<bmap->Rows; i++) {
    fwrite(bmap->Planes[0]+(i*bmap->BytesPerRow),1,bmap->BytesPerRow,bmap_file);
  }
  fclose(bmap_file);
}

static int get_iff_size(char *name, long *width, long *height)
{
  struct BitMapHeader *bmhd;

  if (IFFBase == NULL) {
    if(!(IFFBase = OpenLibrary(IFFNAME,IFFVERSION))) {
  	pline("Copy the iff.library (Ver: %ld) to your LIBS: directory!",IFFVERSION);
  	return FALSE;
    }
  }
  
  if(!(ifffile=OpenIFF(name))) {
	pline("Error opening iffile \"%s\"!",name);
	CloseLibrary(IFFBase);
	return FALSE;
  }

  if(!(bmhd=GetBMHD(ifffile))) {
	pline("BitMapHeader not found in iffile \"%s\"",name);
	CloseLibrary(IFFBase);
	return FALSE;
  }
  *width = bmhd->w;
  *height = bmhd->h;

  CloseIFF(ifffile);
  ifffile = NULL;

  return TRUE;
}


static struct BitMap *load_iff_picture (char *name, long *width, long *height)
{
  struct BitMapHeader *bmhd;
  struct BitMap *bmap;
  int i;
  
  if ((bmap = malloc(sizeof(struct BitMap))) == NULL) {
	pline("No memory for bitmap-struct!!");
	return NULL;
  }
  for (i=0; i<8; i++) {		/* importand */
    bmap->Planes[i] = NULL;
  }

  if (IFFBase == NULL) {
    if(!(IFFBase = OpenLibrary(IFFNAME,IFFVERSION))) {
  	pline("Copy the iff.library (Ver: %ld) to your LIBS: directory!",IFFVERSION);
	free(bmap);
  	return NULL;
    }
  }
  
  if(!(ifffile=OpenIFF(name))) {
	pline("Error opening iffile \"%s\"!",name);
	free(bmap);
	CloseLibrary(IFFBase);
	return NULL;
  }

  if(!(bmhd=GetBMHD(ifffile))) {
	pline("BitMapHeader not found in iffile \"%s\"",name);
	free(bmap);
	CloseLibrary(IFFBase);
	return NULL;
  }

  bmap->BytesPerRow = ((bmhd->w + 15) / 16) * 2;   /* in Bytes / Word alignment */
  bmap->Rows  = bmhd->h;
  bmap->Flags = 0;
  bmap->Depth = bmhd->nPlanes;

  *width = bmhd->w;	/* in pixel */
  *height = bmhd->h;	/* in pixel */

  for (i=0; i<bmap->Depth; i++) {
	bmap->Planes[i] = AllocMem(bmap->BytesPerRow*bmap->Rows,MEMF_PUBLIC|MEMF_CLEAR);
	if (bmap->Planes[i] == NULL) { 
	  free_bmap(bmap);
	}
  }

  if (bmap->Depth > 1) {
	pline("Warning: This program can't work with more than 2 colors!");
  }
  
  if(!DecodePic(ifffile,bmap)) {
	pline("Can't decode picture \"%s\"!",name);
	free_bmap(bmap);
	return NULL;
  }

  return bmap;
}


/* main function: verarbeitet die message und generiert eine Bitmap */

static struct BitMap *work_with_message(struct special_msg *message,
					int draw_modus, int base_dpi)
{
  struct parse_result res;
  struct BitMap *bitmap = NULL;
  char full_name[DOSNAMESIZE];
  long width, height, ssum, ssum2;
  char *str;
  

  pline("** receive special string:   (hres: %d, vres: %d)",
			message->hresolution,message->vresolution);
  pline("   \"%s\"",message->special_string);

  res.iffile[0] = '\0';
  res.hsize   = 0.0;
  res.vsize   = 0.0;
  res.hoffset = 0.0;
  res.voffset = 0.0;
  res.scale   = 0.0;
  res.hscale  = 0.0;
  res.vscale  = 0.0;
  res.hres    = message->hresolution;
  res.vres    = message->vresolution;

  ssum = ssum2 = 0;
  for (str = message->special_string; str != NULL && *str != '\0'; ssum += (long)*str, str++);
  
  ParseSpecial(message->special_string, &res);

  map.hoffset = (long)(((float)message->hresolution * res.hoffset)+0.5);
  map.voffset = (long)(((float)message->vresolution * res.voffset)+0.5);

  message->ret  = 0;
  message->bmap = &map;
  map.loc.map  = NULL;
  map.where_is = LOC_NONE;

  if (res.iffile[0] != '\0') {
    if (draw_modus == DRAW_BORDER || draw_modus == DRAW_RECT) {
      if (get_iff_size(res.iffile,&width,&height)) {
        map.width = width;			/* in pixel */
        map.height = height;
        if (draw_modus == DRAW_BORDER) {
          map.where_is = LOC_BORDER;
        }
        else {
          map.where_is = LOC_RECTANGLE;
        }
        scale_bmap(&map, &bitmap, &res, base_dpi);
      }
      else {
        pline("iff file ERROR! (transfer no picture)");
        message->ret = 5;		/* not used */
      }
    }
    else {
      sprintf(full_name,"%s.%ldx%ld", res.iffile, res.hres, res.vres);
      if ((draw_modus == DRAW_FILE || draw_modus == DRAW_FILE_B)
	  && test_date(res.iffile,full_name,&ssum2) && ssum == ssum2) {		
		/* full_name juenger als iffile und special unveraendert */
	if (draw_modus == DRAW_FILE) {
          map.where_is = LOC_FILE;
        }
        else {
          map.where_is = LOC_FILE_BORDER;
        }
        strcpy(bitfilename,full_name);
        map.loc.filename = bitfilename;
      }
      else {				/* iffile juenger als full_name */
        bitmap = load_iff_picture(res.iffile,&width,&height);
        if (bitmap != NULL) {
	  if (draw_modus == DRAW_IN_MEM_B || draw_modus == DRAW_FILE_B) {
	    map.where_is = LOC_BITMAP_BORDER;
	  }
	  else {
	    map.where_is = LOC_BITMAP;
	  }

          map.width = width;			/* in pixel */
          map.height = height;			/* in pixel */

          scale_bmap(&map, &bitmap, &res, base_dpi);
          map.loc.map = (unsigned long *)bitmap->Planes[0];

          if (draw_modus != DRAW_IN_MEM && draw_modus != DRAW_IN_MEM_B) {
	    pline("create file \"%s\"",full_name);
	    write_bmap(full_name, bitmap, ssum, map.width, map.height);
          }
        }
        else {
          pline("bitmap ERROR! (transfer no picture)");
          message->ret = 5;		/* not used */
        }
      }
    }
  }

  return bitmap;
}


static void scale_bmap (struct special_map *map, struct BitMap **old_bmap,
			struct parse_result *res, int base_dpi)
{
  long new_width, new_height;
  struct BitMap *new_bmap;
  int i;

  new_width  = (map->width * res->hres * 10 + 5) / (base_dpi * 10);
  new_height = (map->height * res->vres * 10 + 5) / (base_dpi * 10);

  if (res->hsize != 0.0) {
    new_width = (long)((res->hsize * (float)res->hres * 10.0 + 5.0) / 10.0);
  }
  if (res->vsize != 0.0) {
    new_height = (long)((res->vsize * (float)res->vres * 10.0 + 5.0) / 10.0);
  }

  if (new_width != map->width || new_height != map->height) {

    if (map->where_is == LOC_BORDER || map->where_is == LOC_RECTANGLE) {
      map->width  = new_width;
      map->height = new_height;
    }
    else {
      if (map->where_is == LOC_BITMAP || map->where_is == LOC_BITMAP_BORDER) {

        if ((new_bmap = malloc(sizeof(struct BitMap))) == NULL) {
		pline("NO MEMORY!! (no resizing of picture");
		return;
        }
        for (i=0; i<8; i++) {		/* importand */
		new_bmap->Planes[i] = NULL;
        }

        new_bmap->BytesPerRow = ((new_width + 15) / 16) * 2;
        new_bmap->Rows        = new_height;

        new_bmap->Depth       = (*old_bmap)->Depth;
        new_bmap->Flags       = (*old_bmap)->Flags;
        for (i=0; i<new_bmap->Depth; i++) {
	  new_bmap->Planes[i] = AllocMem(new_bmap->BytesPerRow*new_bmap->Rows,MEMF_PUBLIC|MEMF_CLEAR);
	  if (new_bmap->Planes[i] == NULL) { 
	    free_bmap(new_bmap);
	    pline("NO MEMORY!!");
	  }
        }

	/* ignore the return value (sorry) */
	(void)extract_one((unsigned char *)(*old_bmap)->Planes[0],
			  (unsigned char *)new_bmap->Planes[0],
			  (number)map->width,
			  (number)map->height,
			  (number)(*old_bmap)->BytesPerRow,
			  (number)new_bmap->BytesPerRow,
			  (number)0,
			  (number)0,
			  (number)map->width,
			  (number)map->height,
			  (number)new_width,
			  (number)new_height);

			  
        map->width  = new_width;
        map->height = new_height;

        free_bmap(*old_bmap);
        *old_bmap = new_bmap;

      }
    }
  }
}



void install_special_port(struct MsgPort **special_port)
{
  Forbid();
  *special_port = FindPort(SPECIAL_PORT);
  if (*special_port != NULL) {
    Permit();
    Fatal(NOT_FIRST);
  }
  *special_port = CreatePort(SPECIAL_PORT,0L);
  Permit();

  if (*special_port == NULL) {
    Fatal(NO_PORT);
  }
}



long work_with_special(int draw_modus, int base_dpi)
{
  struct special_msg *message;
  long ret = 0;

  while ((message = (struct special_msg *)GetMsg(special_port)) != NULL) {
    if (message != NULL) {
      if (message->action != AC_SEND_SPECIAL) {
        Fatal(EXP_SEND_SP);
      }

      bmap = work_with_message(message, draw_modus, base_dpi);

      message->action = AC_REPLY_SPECIAL;
      ReplyMsg(&(message->msg));
      WaitPort(special_port);
      message = (struct special_msg *)GetMsg(special_port);
      if (message == NULL) {
        Fatal(NO_MESSAGE);
      }
      if (message->action != AC_OK_BITMAP) {
        Fatal(NO_OK_BIT);
      }
      message->action = AC_REPLY_BITMAP;
      message->ret = 0;
      ReplyMsg(&(message->msg));
      if (ifffile != NULL) {
        CloseIFF(ifffile);
        ifffile = NULL;
      }
      free_bmap(bmap);
      bmap = NULL;
    }
  }

  return ret;
}

