/*
 *  Colour.c - a program for manipulating the colours of screens.
 *
 *  Written because I like one set of colours in a CLI and another in
 *  my terminal window, and can't always afford the memory for another
 *  screen. Plus, I like a more direct way of changing colours than
 *  twiddling a bunch of slider gadgets (although that way has its uses
 *  too, so I include the capability). Lastly, Americans don't know
 *  how to spell "colour" :-).
 *
 *  Features include:
 *	- save all colours from a given screen
 *	- load a set of colours into a given screen
 *	- set a particular colour register in a given screen
 *	- adjust screen colours interactively (OPTIONAL)
 *
 *  Copyright 1987, John Russell
 *
 *  5 Alderdice Place
 *  St. John's, Newfoundland
 *  Canada   A1B 2P8
 *  (709) 726-7847
 *
 *  Freely redistributable.
 */

/* Manx users compile with "+l" just in case */

/* These includes might not be enough, I use precompiled includes. */
/* anything to do with screens & viewports may be required */

#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <stdio.h>
#include "ctype.h"

#define LOAD 1
#define SAVE 2
#define SET 3
#define ADJUST 4

/* if "fancy" is defined, you have the option to adjust on-the-fly */

#define FANCY 1

/* debug = "puts" to enable tracing, leave blank to disable */

#define debug(x)

struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase *GfxBase=NULL;

char *usage = "\n\
Bad syntax.\n\n\
Usage 1: colour load   <screen | \"-null\"> [from] <filename>.\n\
Usage 2: colour save   <screen | \"-null\"> [to] <filename>.\n\
Usage 3: colour set    <screen | \"-null\"> index R G B.\n\
Usage 4: colour adjust <screen | \"-null\">.\n\n\
Note  -  Specify screen by first word of title (case doesn't matter).\n\
Note  -  Load file is searched for as \"file\" and \"s:file.col\".\n";

main(argc,argv)
int argc;
char *argv[];
{
    struct Screen *screen;	/* which screen to consider */
    struct ViewPort *vp;	/* controlling viewport for screen */
    FILE *fp;			/* for loading & saving */
    short good_args = 0;	/* right # arguments for syntax? */
    short mode = 0;		/* what type of command is it? */
    int r,g,b;
    int i,n,fname,depth;	/* depth == number of colours in screen */
    UWORD colour;		/* I only remember these typedefs */
    struct ColorMap *map;	  /* when reading includes :-) */
    char name[80];

    /* check for valid arguments and commands */

    if (argc < 3)    /* always need at least 3 */
    {
	puts(usage);
	exit(0);
    }
    else if (!compare(argv[1],"load"))
    {
	debug("Asked to load a colour set.");
	mode = LOAD;
	if (argc == 4 || (!compare(argv[3],"from") && argc == 5))
	    good_args = 1;
    }
    else if (!compare(argv[1],"save"))
    {
	debug("Asked to save a colour set.");
	mode = SAVE;
	if (argc == 4 || (!compare(argv[3],"to") && argc == 5))
	    good_args = 1;
    }
    else if (!compare(argv[1],"set"))
    {
	debug("Asked to set a single colour.");
	mode = SET;
	if (argc == 7) good_args = 1;
    }

#ifdef FANCY
    else if (!compare(argv[1],"adjust"))
    {
	debug("Asked to adjust screen colours.");
	mode = ADJUST;
	if (argc == 3) good_args = 1;
    }
#endif

    fname = argc - 1;	/* argv[fname] = file for load & save */

    if (!good_args) {
	puts(usage);
	exit(0);
    }

    debug("Arguments are legal.");

    if (! (IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library",33L)))
    {
	puts("Please use Kickstart/Workbench V1.2 for latest Intuition.");
	exit(0);
    }

    if (! (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",33L)))
    {
	puts("Please use Kickstart/Workbench V1.2 for latest Graphics.");
	exit(0);
    }

    screen = IntuitionBase->FirstScreen;

/* the target screen is found when the first word of the title matches the
   2nd argument specified, OR if the 2nd argument is -null, the first
   screen with NO title is matched (eg the DPaint screen) */

    LockIBase(NULL);	/* prevent window list from changing while in loop */

    while (screen)	/* do all screens */
    {
	    if (!compare(argv[2],screen->Title) ||
	       ((screen->Title == NULL) && (!compare(argv[2],"-null"))))
	    {
		UnlockIBase(NULL);

		debug("Found the indicated screen.");

		vp = &screen->ViewPort;     /* don't like 'em much myself */
		map = vp->ColorMap;
		depth = 1 << screen->BitMap.Depth;   /* no easier way? */

		switch (mode)
		{

		    case SET:

			i = atoi(argv[3]);  /* colour index */
			r = atoi(argv[4]);  /* RGB values */
			g = atoi(argv[5]);
			b = atoi(argv[6]);

			if (i < depth)
			{
			    SetRGB4(vp,i,r,g,b);
			    debug("Set single colour.");
			}
			else
			    puts("Illegal colour index for this screen.");

			break;

		    case LOAD:	/* (LA Law is on in the background) */

			/* search current directory and S: for file */

			/* look for for file as "file" and "s:file.col" */


			if (!(fp = fopen(argv[fname],"r")))
			{
			    /* Boy do I hate C string functions! */

			    strcpy(name,"s:");
			    strcat(name,argv[fname]);
			    strcat(name,".col");

			    debug("Searching for file in S: directory.");
			    fp = fopen(name,"r");
			}

			if (!fp)
			{
			    puts("Unable to open load file!");
			    goto QUIT;
			}

			debug("Opened file successfully.");

			/* This is an *ugly* loop condition! */

			for (i=0;
			     (i < depth)
				&&
			     (fscanf(fp,"%d %d %d\n",&r,&g,&b)==3);
			     i++)
			{
			    SetRGB4(vp,i,r,g,b);
			    debug("Set a colour.");
			}

			fclose(fp);
			break;

		    case SAVE:

			fp = fopen(argv[fname],"w");

			if (!fp) {
			    puts("Unable to open save file!");
			    goto QUIT;
			}

			for (i=0; i < depth; i++)
			{
			    colour = GetRGB4(map,i);

			    /* each colour component encoded by 4 bits */

			    r = (colour & 0xf00) >> 8;
			    g = (colour & 0x0f0) >> 4;
			    b = (colour & 0x00f);

			    fprintf(fp,"%d %d %d\n",r,g,b);
			    debug("Saved a colour.");
			}

			fclose(fp);

			break;

#ifdef FANCY
		    case ADJUST:
			ScreenToFront(screen);
			DoColorWindow(screen);	/* I can't take credit for */
			break;			/* this section */
#endif

		    default:

			debug("Illegal instruction mode.");
			break;
		}

		goto QUIT;	/* only match 1 screen */

	    }

	    screen = screen->NextScreen;

	}

    UnlockIBase(NULL);	/* didn't find the screen at all */
    debug("Didn't find indicated screen.");

QUIT:
    CloseLibrary(IntuitionBase);
    CloseLibrary(GfxBase);
}

/* case-insensitive string comparison, with \0 and space as end conditions */

compare(string1,string2)
char *string1,*string2;
{
    while ((*string1 != '\0') && (*string1 != ' '))
    {
	if (toupper(*(string1++)) != toupper(*(string2++)))
	    return(1);
    }
    return(0);	/* return weird values like strcmp() */
}

