#ifdef AZTEC_C
#include <functions.h>
#else
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#endif

#include <stdlib.h>
#include <exec/ports.h>
#include <dos/dos.h>
#include <math.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>

/* Example of using simpleblanker. This program simply draws lines using
 * equations based on Lissajous. It is not the greatest screen blanker since
 * the lines are on screen for awhile. It is painfully slow on a stock
 * Amiga with no 68881 or 68882. After the lines are drawn, the pattern is
 * repeated using only the background color to erase.
 */

/*
 * MachIV will execute a macro named "Blank" when the timeout says to blank
 * the screen. It also executes a macro named "Unblank" when screen blanking
 * should terminate and a macro named "MachIVQuit" when MachIV is quiting.
 * These macros could anything, but to communicate with MachIV, they would
 * be something like this:
 *
 * MSC_PUTMSG"MyScreenBlanker,1"     - Name this macro Blank.
 * MSC_PUTMSG"MyScreenBlanker,2"     - Name this macro Unblank.
 * MSC_PUTMSG"MyScreenBlanker,3"     - Name this macro MachIVQuit.
 *
 * SimpleBlanker waits on its port named "MyScreenBlanker" for one of three
 * signals. When it receives a BLANK (1) signal, it loops doing whatever
 * graphics on the screen that you program in. In this loop it also checks
 * for an UNBLANK (2), QUIT (3) or ctrl-c signal. Messages sent are actually
 * IntuiMessages structs but only Class, Code and Qualifier are filled in by
 * MachIV. Only Class is used in this program.
 *
 * Usage:  Run <nil: >nil: lissajous
 *
 * SimpleBlanker also quits when you execute it a second time or send it
 * a Ctrl-C via the "break" command or some similar program.
 *
 * Compiles under Aztec 5.2a and SAS/C 5.10a.
 *
 * Aztec:
 *
 *     cc SimpleBlanker -f8
 *     ln SimpleBlanker.o -lm8 -lc
 *
 *     or without a floating point chip (68881 or 68882)
 *
 *     cc SimpleBlanker
 *     ln SimpleBlanker.o -lm -lc
 *
 *     or with or without a 68881/68882
 *
 *     cc SimpleBlanker -fa
 *     ln SimpleBlanker.o -lma -lc
 *
 *
 * Lattice:
 *
 *     lc  -f8 -Lm SimpleBlanker
 *
 *     or without a 68881/68882
 *
 *     lc  -fi -Lm SimpleBlanker
 *
 *     or with or without a 68881/68882
 *
 *     lc  -fl -Lm SimpleBlanker
 *
 * Again, the program is terribly slow without at least a 14Mhz 68020.
 * A 68881 or 68882 is even better.
 *
 * The supplied program was compiled with Aztec using -fa and -lma so it
 * will run with or without a coprocessor. It does require
 * mathieeedoubbas.library in the libs: directory.
 *
 * Both compilers allow the // comment and I've used it here. If you are
 * using an older compiler, you will need to change them to the standard
 * comment form which I can't even show you here or it would be a nested
 * comment!
 */

// Signals sent from MachIV to your port.

#define BLANK   1
#define UNBLANK 2
#define QUIT    3

// Screen blanking constants.

#define SCREEN_DEPTH    4
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 400
#define XCENTER       SCREEN_WIDTH / 2
#define YCENTER       SCREEN_HEIGHT / 2
#define XRADIUS       270
#define YRADIUS       170
#ifndef PI
#define PI            3.14159
#endif
#define DEGREE        PI / 180

// Proto declaration.

void main(void);
static void drawlissa(void);
static void die(void);

// Global Variables

int Enable_Abort=0;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

char *portname = "MyScreenBlanker";

struct NewScreen newScreen = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_DEPTH,
                              -1,-1,LACE | HIRES,CUSTOMSCREEN,NULL,NULL,NULL,NULL};
struct Screen *s;

UWORD CMap[16] = {	/* Colors */
 0x000,0xF00,0xF02,0xE04,0xE06,0xD08,0xD09,0xD0B,
 0xC0C,0xA0C,0x80C,0x60B,0x40B,0x30B,0x10A,0x00A
};

struct MsgPort      *portptr,*rp;
struct IntuiMessage *imsg, quitmsg;

ULONG mode,action,sigmask,signals;
BOOL  wipe;

double a,b,t,at,bt,sat,x,y,z;
ULONG  z2;

void main(void)
{
  // First see if we are already running. If so, Put a message telling it
  // to quit. Wait for SimpleBlanker to Reply.

  if ((portptr = FindPort((STRPTR)portname)) != NULL) {
    if (rp = CreateMsgPort()) {   // Create a reply port for us.
      quitmsg.ExecMessage.mn_Node.ln_Type = NT_MESSAGE;
      quitmsg.ExecMessage.mn_ReplyPort = rp;
      quitmsg.ExecMessage.mn_Length = 32;
      quitmsg.Class = QUIT;
      PutMsg(portptr,(struct Message*)&quitmsg);
      WaitPort(rp); // Wait for SimpleBlanker to do a ReplyMsg();
      GetMsg(rp);
      DeleteMsgPort(rp);
      exit(0);
    }
  }
  // Port not found so create and add our port.

  if ((portptr = CreateMsgPort()) == NULL)
    exit(0);

  portptr->mp_Node.ln_Pri = 1;         // Will be searched for.
  portptr->mp_Node.ln_Name = portname;

  AddPort(portptr);
  sigmask = (1 << portptr->mp_SigBit) | SIGBREAKF_CTRL_C;

  IntuitionBase = (struct IntuitionBase *) OpenLibrary((UBYTE*)"intuition.library",0L);
  GfxBase = (struct GfxBase *) OpenLibrary((UBYTE*)"graphics.library",0L);

  for (;;) {
    signals = Wait(sigmask); // Waiting for BLANK or QUIT or SIGBREAKF_CTRL_C

    if (signals & SIGBREAKF_CTRL_C)
      die();

    while (imsg = (struct IntuiMessage *)GetMsg(portptr)) {
      action = imsg->Class;
      ReplyMsg((struct Message *)imsg);

      switch (action) {
        case BLANK:
          if ((s = OpenScreen(&newScreen)) == NULL) {
            break;
          }
          LoadRGB4(&s->ViewPort,CMap,16L);
          srand(IntuitionBase->Micros);

          wipe = 1;
          while ((imsg = (struct IntuiMessage *)GetMsg(portptr)) == NULL) {
            Move(&s->RastPort,XCENTER,YCENTER);
            a = b = t = 1;
            if (wipe) {
              wipe = 0;
              SetRast(&s->RastPort,0L);
              mode = (rand() % 5) + 1;
            }
            else {
              wipe = 1;
              SetAPen(&s->RastPort,0L);
            }
            drawlissa();
          }
          // Got a message.
          action = imsg->Class;
          ReplyMsg((struct Message *)imsg);
          if (action == QUIT)
            die();      // Will never return.
          else if (action != UNBLANK)
            break;                   // UNBLANK falls through!!
        case UNBLANK:
          if (s)
            CloseScreen(s);
          s = NULL;
          break;
        case QUIT:
          die();
          break;
      }
    }
  }
}

static void drawlissa(void)
{
  WORD i;

  for (i=0;i<1500;i++) {
    at = a * t;
    bt = b * t;
    sat = sin(at);
    x=XRADIUS*sat*cos(bt);
    y=YRADIUS*sat*sin(bt);

    z=YRADIUS*cos(at);
    z2=z*.1;

    switch (mode) {
      case 1:
        t = t + DEGREE;
        a=a+.003;
        b=b- .003;
        break;
      case 2:
        t=t + DEGREE + .01;
        a=a+.2;
        b=b-.2;
        break;
      case 3:
        t = t + DEGREE + .02;
        a=a+.01;
        b=b-.01;
        break;
      case 4:
        t = t + 4;
        a=a+.0005;
        b=b-.0005;
        break;
      case 5:
        t = t + .09;
        a=a+.0001;
        b=b-.0001;
        break;
    }
    if (!wipe && ((i % 50) == 0))     // change colors every 50 times
      SetAPen(&s->RastPort,(z2 % 15)+1);

    Draw(&s->RastPort,(long)(z+x+XCENTER),(long)(y+YCENTER));

    if (SetSignal(0L,0L) & sigmask) // check for any signals
      break;
  }
}

// This deletes and closes up everything.

static void die(void)
{
  RemPort(portptr);  // portptr must be valid or we wouldn't be here.

  DeleteMsgPort(portptr);

  if (s)
    CloseScreen(s);

  if (IntuitionBase)
    CloseLibrary((struct Library*)IntuitionBase);
  if (GfxBase)
    CloseLibrary((struct Library*)GfxBase);
  exit(0);
}
