/*
 * Al-j.c Michael Saunby	M.Saunby@reading.ac.uk
 *
 * Release 1.1  December 1992 Jpeg viewer using Albert HAM8 public screen and
 * Independent JPEG groups jpeg decompressor.
 */
#define PROG_NAME "Al-J JPEG Viewer"
#define COPYRIGHT_MESSAGE "Version 1.1\nDecember 1992\n\nCopyright © 1992\n\
Michael Saunby\n\
\nThis program is based in part\n\
on the work of the Independent\n\
JPEG Group"

#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <graphics/gfxbase.h>
#include <graphics/displayinfo.h>
#include <intuition/screens.h>
#include <dos/dosextens.h>
#include <dos/dosasl.h>
#include <libraries/gadtools.h>

#include <clib/asl_protos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/layers_protos.h>
#include <clib/graphics_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/dos_protos.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "display24.h"

/* Values greater than 1 give bad pixels, why? */
#define ARRAY24_MAX_ROWS 1

/*
 * ReadArgs() template
 */
#define TEMPLATE "NAME"
#define OPT_NAME 0
#define OPT_COUNT 1

int JPEG_restart = 0;		/* set to 1 for New file */

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase *GfxBase = NULL;
struct Library *GadToolsBase = NULL;
struct Library *AslBase = NULL;
extern struct ExecBase *SysBase;

/* following for 24 bit put row */
SHORT ham8_line;		/* screen line number to draw at */
UBYTE *ham8_pens = NULL;
struct RastPort ham8_temprp;
struct BitMap *temp_bm = NULL;
struct Screen *screen = NULL;
struct FileRequester *filereq = NULL;
struct BitMap *bitmap = NULL;
struct Window *window = NULL;
struct Menu *menu;
struct Gadget *gadgets, *horprop = NULL, *vertprop = NULL;
void *vi;
ULONG allocsignal;

/* gadget ids */
#define HORPROP 	1
#define VERTPROP 	2
/* current x and y offsets */
LONG scroll_x = 0;
LONG scroll_y = 0;
LONG window_max_w;
LONG window_max_h;

struct EasyStruct failedES =
{sizeof (struct EasyStruct), 0, PROG_NAME,
 "%s", "OK",};

#define OPEN 1
#define ABOUT 2
#define QUIT 3
struct NewMenu prj_menu[] =
{
  {NM_TITLE, "Project", 0, 0, 0, 0,},
  {NM_ITEM, "Open...", "O", 0, 0, (void *) OPEN,},
  {NM_ITEM, NM_BARLABEL, 0, 0, 0, 0,},
  {NM_ITEM, "About...", "?", 0, 0, (void *) ABOUT,},
  {NM_ITEM, "Quit", "Q", 0, 0, (void *) QUIT,},
  {NM_END, NULL, 0, 0, 0, 0,},
};

void
Cleanup ()
{
  if (screen)
    UnlockPubScreen (NULL, screen);
  screen = NULL;

  if (window)
    {
      ReleasePens (&(window->WScreen->ViewPort));
      FreeVisualInfo (vi);
      CloseWindow (window);
      gadgets = NULL;
    }
  window = NULL;

  if (menu)
    ClearMenuStrip (window);
  menu = NULL;
  if (horprop)
    DisposeObject (horprop);
  horprop = NULL;
  if (vertprop)
    DisposeObject (vertprop);
  vertprop = NULL;
  if (ham8_pens)
    FreeMem (ham8_pens, ((temp_bm->BytesPerRow << 3) * temp_bm->Rows));
  ham8_pens = NULL;
  if (temp_bm)
    FreeBitMap (temp_bm);
  temp_bm = NULL;
  if (bitmap)
    FreeBitMap (bitmap);
  bitmap = NULL;
  if (!JPEG_restart)
    {
      if (filereq)
	FreeAslRequest (filereq);
      filereq = NULL;
      if (AslBase)
	CloseLibrary (AslBase);
      AslBase = NULL;
      if (GadToolsBase)
	CloseLibrary (GadToolsBase);
      GadToolsBase = NULL;
      if (IntuitionBase)
	CloseLibrary ((struct Library *) IntuitionBase);
      IntuitionBase = NULL;
      if (GfxBase)
	CloseLibrary ((struct Library *) GfxBase);
      GfxBase = NULL;
    }
}

void
Quit (char whytext[], UBYTE failcode)
{
  EasyRequest (NULL, &failedES, NULL, whytext);
  Cleanup ();
  exit (failcode);
}

void
UserWait ()
{
  struct IntuiMessage *msg;
  struct Gadget *gadget;
  BOOL abort = FALSE;
  UWORD class, code;
  LONG new_x, new_y;

  SetGadgetAttrs (horprop, window, NULL,
		  GA_Disabled, FALSE,
		  TAG_END);
  SetGadgetAttrs (vertprop, window, NULL,
		  GA_Disabled, FALSE,
		  TAG_END);

  do
    {
      WaitPort (window->UserPort);

      while (msg = GT_GetIMsg (window->UserPort))
	{
	  code = msg->Code;
	  class = msg->Class;
	  gadget = (struct Gadget *) msg->IAddress;
	  GT_ReplyIMsg (msg);
	  switch (class)
	    {
	    case IDCMP_MENUPICK:
	      switch ((UWORD) MENU_USERDATA (ItemAddress (menu, code)))
		{
		case QUIT:
		  abort = TRUE;
		  break;
		case OPEN:
		  JPEG_restart = TRUE;
		  abort = TRUE;
		  break;
		case ABOUT:
		  EasyRequest (window, &failedES,
			       NULL,
			       COPYRIGHT_MESSAGE);
		  break;
		}
	      break;
	    case IDCMP_CLOSEWINDOW:
	      abort = TRUE;
	      break;
	    case IDCMP_SIZEVERIFY:
	    case IDCMP_NEWSIZE:
	      SetGadgetAttrs (vertprop, window, NULL,
			      PGA_Visible, window->GZZHeight,
			      TAG_END);
	      SetGadgetAttrs (horprop, window, NULL,
			      PGA_Visible, window->GZZWidth,
			      TAG_END);
	      break;
	    case IDCMP_GADGETUP:
	      switch (gadget->GadgetID)
		{
		case HORPROP:
		  GetAttr (PGA_Top, horprop, &new_x);
		  ScrollLayer (0L, window->RPort->Layer, new_x - scroll_x, 0);
		  scroll_x = new_x;
		  WindowLimits (window, 0, 0, window_max_w - scroll_x, 0);
		  break;

		case VERTPROP:
		  GetAttr (PGA_Top, vertprop, &new_y);
		  ScrollLayer (0L, window->RPort->Layer, 0, new_y - scroll_y);
		  scroll_y = new_y;
		  WindowLimits (window, 0, 0, 0, window_max_h - scroll_y);
		  break;
		}

	      break;
	    default:
	      /*
	       * EasyRequest(window, &failedES, NULL, "Bogus message
	       * sorry!");
	       */
	      break;
	    }

	}
  } while (abort == FALSE);

  Cleanup ();

}

char *
Startup (char *namebuf)
{
  char *ret_code = NULL;
  LONG result[OPT_COUNT] =
  {0};
  struct RDArgs *rda;

  if (!JPEG_restart)
    {
      if ((GfxBase =
	   (struct GfxBase *) OpenLibrary ("graphics.library", 36)) == NULL)
	Quit ("graphics.library is too old <V36", 25);

      if ((IntuitionBase =
	   (struct IntuitionBase *) OpenLibrary ("intuition.library", 36)) == NULL)
	Quit ("intuition.library is too old <V36", 25);

      if ((AslBase = OpenLibrary ("asl.library", 36)) == NULL)
	Quit ("asl.library is too old <V36", 25);

      if ((GadToolsBase = OpenLibrary ("gadtools.library", 36)) == NULL)
	Quit ("gadtools.library is too old <V36", 25);

      if ((filereq =
	   (struct FileRequester *) AllocAslRequestTags (ASL_FileRequest,
				      ASLFR_PubScreenName, HAM8_ALBERT_NAME,
							 TAG_END))
	  == NULL)
	Quit ("could not build file requster", 25);

      rda = ReadArgs (TEMPLATE, result, NULL);

      if (result[OPT_NAME])
	{
	  strcpy (namebuf, (UBYTE *) result[OPT_NAME]);
	  ret_code = namebuf;
	}
      FreeArgs (rda);
    }
  JPEG_restart = FALSE;

  if (ret_code == NULL)
    {
      struct Screen *screen;
      /*
       * Opening the File Requester does not bring screen to front. Passing
       * the name ie. UnlockPubscreen(HAM8_ALBERT_NAME, NULL) is allowed
       * but not recommended. I haven't tried it.
       */
      if ((screen = LockPubScreen (HAM8_ALBERT_NAME)) != NULL)
	{
	  ScreenToFront (screen);
	  UnlockPubScreen (NULL, screen);
	}
      if (AslRequest (filereq, 0L))
	{
	  if(filereq->rf_Dir[strlen(filereq->rf_Dir)-1] == ':')
	  sprintf (namebuf, "%s%s", filereq->rf_Dir, filereq->rf_File);
	  else
	  sprintf (namebuf, "%s/%s", filereq->rf_Dir, filereq->rf_File);
	  ret_code = namebuf;
	}
    }
  ham8_line = 0;

  return (ret_code);
}


void
CreateWindow (UWORD width, UWORD height)
{
  int pen_errors;

  struct Gadget **tmpgad;

  if ((menu = CreateMenus (prj_menu, GTMN_FrontPen, 1, TAG_DONE)) == NULL)
    Quit ("could not build menu", 25);

  if ((screen = LockPubScreen (HAM8_ALBERT_NAME)) == NULL)
    Quit ("could not find screen", 25);

  if ((bitmap = AllocBitMap (width, height, 8, BMF_DISPLAYABLE | BMF_CLEAR,
			     NULL)) == NULL)
    Quit ("could not allocate bitmap", 25);
  gadgets = NULL;
  tmpgad = &gadgets;

  /* Create scroller gadgets */
  horprop = (struct Gadget *) NewObject (NULL, "propgclass",
					 GA_Immediate, TRUE,
					 GA_BottomBorder, TRUE,
					 GA_GZZGadget, TRUE,
					 GA_RelVerify, TRUE,
					 PGA_Freedom, FREEHORIZ,
					 PGA_Borderless, FALSE,
					 PGA_NewLook, TRUE,
					 GA_RelBottom, -10,
					 GA_RelWidth, -21,
					 GA_Left, 3,
					 GA_Height, 7,
					 PGA_Top, 0,
					 PGA_Visible, width,
					 PGA_Total, width,
					 GA_ID, HORPROP,
					 GA_Previous, tmpgad,
					 TAG_END);

  vertprop = (struct Gadget *) NewObject (NULL, "propgclass",
					  GA_Immediate, TRUE,
					  GA_RightBorder, TRUE,
					  GA_GZZGadget, TRUE,
					  GA_RelVerify, TRUE,
					  PGA_Freedom, FREEVERT,
					  PGA_Borderless, FALSE,
					  PGA_NewLook, TRUE,
					  GA_RelHeight, -21,
					  GA_RelRight, -13,
					  GA_Width, 10,
					  GA_Top, 10,
					  PGA_Top, 0,
					  PGA_Visible, height,
					  PGA_Total, height,
					  GA_ID, VERTPROP,
					  GA_Previous, tmpgad,
					  TAG_END);

  if ((window = OpenWindowTags (NULL,
				WA_Title, PROG_NAME,
				WA_Top, screen->BarHeight + 1,
				WA_InnerHeight, height,
				WA_InnerWidth, width,
				WA_NewLookMenus, TRUE,
				WA_PubScreen, screen,
				WA_SizeGadget, TRUE,
				WA_DepthGadget, TRUE,
				WA_DragBar, TRUE,
				WA_CloseGadget, TRUE,
				WA_Activate, FALSE,
				WA_SizeBBottom, TRUE,
				WA_SizeBRight, TRUE,
				WA_MinWidth, 64,
				WA_MinHeight, 32,
				WA_MaxWidth, -1,
				WA_MaxHeight, -1,
				WA_AutoAdjust, TRUE,
				WA_SuperBitMap, bitmap,
				WA_GimmeZeroZero, TRUE,
				WA_Gadgets, gadgets,
				WA_IDCMP, MENUPICK | IDCMP_NEWSIZE |
				IDCMP_SIZEVERIFY | CLOSEWINDOW | GADGETUP,
				TAG_END)) == NULL)
    {
      Quit ("could not open window\nProbably not enough chip ram", 25);
    }
  vi = GetVisualInfo (screen, TAG_END);
  /*
   * Have window now so no longer need lock on screen.
   */

  UnlockPubScreen (NULL, screen);
  screen = NULL;

  if (LayoutMenus (menu, vi, TAG_DONE) == NULL)
    Quit ("could not layout menu", 25);
  SetMenuStrip (window, menu);

  /*
   * The window may not have opened at the size we asked for so fix the
   * prop gads.
   */
  SetGadgetAttrs (horprop, window, NULL,
		  PGA_Visible, window->GZZWidth,
		  PGA_Top, 0,
		  GA_Disabled, TRUE,
		  TAG_END);
  SetGadgetAttrs (vertprop, window, NULL,
		  PGA_Visible, window->GZZHeight,
		  PGA_Top, 0,
		  GA_Disabled, TRUE,
		  TAG_END);

  window_max_w = window->Width;
  window_max_h = window->Height;
  scroll_x = 0;
  scroll_y = 0;
  WindowLimits (window, 0, 0, window_max_w, window_max_h);

  CopyMem (&(window->RPort), &ham8_temprp, sizeof (struct RastPort));
  ham8_temprp.Layer = NULL;
  if ((temp_bm = AllocBitMap (width, /* height */ 1, /* depth */ 8, 0L, bitmap)) == NULL)
    Quit ("cannot get line bitmap", 25);
  ham8_temprp.BitMap = temp_bm;
  ham8_pens = AllocMem (((temp_bm->BytesPerRow << 3) * ARRAY24_MAX_ROWS), 0L);
  /*
   * Get a copy of the special palette. The palette is set when the screen
   * is created.
   */
  pen_errors = StandardPalette (&(window->WScreen->ViewPort), TRUE);
  if (pen_errors)
    EasyRequest (window, &failedES,
		 NULL,
		 "Could not find full HAM-8 Albert palette");

}

#undef GLOBAL			/* Means something else to IJPEG code */
#include "jinclude.h"

#ifndef EIGHT_BIT_SAMPLES
Sorry, this code only copes with 8 - bit JSAMPLEs.	/* deliberate syntax err */
#endif


/*
 * Write the file header.
 */

METHODDEF void
output_init (decompress_info_ptr cinfo)
{
  CreateWindow ((SHORT) cinfo->image_width, (SHORT) cinfo->image_height);

}


/*
 * Write some pixel data.
 */

METHODDEF void
put_pixel_rows (decompress_info_ptr cinfo, int num_rows,
		JSAMPIMAGE pixel_data)
{
  JSAMPROW ptr0, ptr1, ptr2;
  long width = cinfo->image_width;
  int row, height;

      for (row = 0; row < num_rows; row += height)
	{
	  ptr0 = pixel_data[0][row];
	  ptr1 = pixel_data[1][row];
	  ptr2 = pixel_data[2][row];
	  height = num_rows - row;	/* ie rows left */
	  height = (ARRAY24_MAX_ROWS < height) ? ARRAY24_MAX_ROWS : height;

	  WritePixelArray24 (window->RPort, 0, ham8_line,
			     width, height,
			     ptr0, ptr1, ptr2, ham8_pens, &ham8_temprp);
	  ham8_line += height;
	}
}

METHODDEF void
put_gray_rows (decompress_info_ptr cinfo, int num_rows,
		JSAMPIMAGE pixel_data)
{
  JSAMPROW ptr0;
  long width = cinfo->image_width;
  int row, height;


      for (row = 0; row < num_rows; row += height)
	{
	  ptr0 = pixel_data[0][row];
	  height = num_rows - row;	/* ie rows left */
	  height = (ARRAY24_MAX_ROWS < height) ? ARRAY24_MAX_ROWS : height;

	/* Dont have a special grayscale function yet */
	  WritePixelArray24 (window->RPort, 0, ham8_line,
			     width, height,
			     ptr0, ptr0, ptr0, ham8_pens, &ham8_temprp);
	  ham8_line += height;
	}
}

/*
 * Might be nice to try quantized output sometime.
 */

METHODDEF void
put_demapped_rows (decompress_info_ptr cinfo, int num_rows,
		   JSAMPIMAGE pixel_data)
{
}
METHODDEF void
put_color_map (decompress_info_ptr cinfo, int num_colors, JSAMPARRAY colormap)
{

}


/*
 * Finish up at the end of the file.
 */

METHODDEF void
output_term (decompress_info_ptr cinfo)
{
  UserWait ();
}


/*
 * The method selection routine for HAM8 format output. This should be called
 * from d_ui_method_selection if HAM8 output is wanted.
 */

GLOBAL void
jselwham8 (decompress_info_ptr cinfo)
{


  cinfo->methods->output_init = output_init;
  cinfo->methods->put_color_map = put_color_map;
  if(cinfo->out_color_space == CS_RGB)
	  cinfo->methods->put_pixel_rows = put_pixel_rows;
  else
	  cinfo->methods->put_pixel_rows = put_gray_rows;
  cinfo->methods->output_term = output_term;
}
