/*
 * Copyright (c) 1988-91 by Patrick J. Naughton.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 */

/*
 * Copyright (c) 1991, 2015, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*-
 * flame.c - recursive fractal cosmic dust.
 *
 * Copyright (c) 1991 by Patrick J. Naughton.
 *
 * See xlock.c for copying information.
 *
 * Revision History:
 * 24-Jun-91: fixed portability problem with integer mod (%).
 * 06-Jun-91: Written. (received from Spot, spot@cs.cmu.edu).
 */

#include "xlock.h"
#include <math.h>

#define MAXTOTAL	10000
#define MAXBATCH	10

typedef struct {
    double      f[2][3][20];	/* three non-homogeneous transforms */
    int         max_levels;
    int         cur_level;
    int         SNUM;
    int         ANUM;
    int         width, height;
    int         num_points;
    int         total_points;
    int         pixcol;
    Window      win;
    XPoint      pts[MAXBATCH];
}           flamestruct;

static flamestruct flames[MAXSCREENS];

static short
halfrandom(int mv)
{
    static short lasthalf = 0;
    unsigned long r;

    if (lasthalf) {
	r = (unsigned long) lasthalf;
	lasthalf = 0;
    } else {
	r = (unsigned long) random();
	lasthalf = (short) (r >> 16);
    }
    return (short) (r % mv);
}

void
initflame(Window      win)
{
    flamestruct *fs = &flames[screen];
    XWindowAttributes xwa;

    srandom((uint_t) time((long *) 0));

    if ((batchcount < 1) || (batchcount > 64))
	batchcount = 20;

    XGetWindowAttributes(dsp, win, &xwa);
    fs->width = xwa.width;
    fs->height = xwa.height;

    fs->max_levels = batchcount;
    fs->win = win;

    XSetForeground(dsp, Scr[screen].gc, ssblack[screen].pixel);
    XFillRectangle(dsp, win, Scr[screen].gc, 0, 0, fs->width, fs->height);

    if (Scr[screen].npixels > 2) {
	fs->pixcol = halfrandom(Scr[screen].npixels);
	XSetForeground(dsp, Scr[screen].gc, Scr[screen].pixels[fs->pixcol]);
    } else {
	XSetForeground(dsp, Scr[screen].gc, sswhite[screen].pixel);
    }
}

static      Bool
recurse(
    flamestruct *fs,
    double x,
    double y,
    int l
    )
{
    if (l == fs->max_levels) {
	fs->total_points++;
	if (fs->total_points > MAXTOTAL)	/* how long each fractal runs */
	    return False;

	if (x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0) {
	    fs->pts[fs->num_points].x = (short) ((fs->width / 2) * (x + 1.0));
	    fs->pts[fs->num_points].y = (short) ((fs->height / 2) * (y + 1.0));
	    fs->num_points++;
	    if (fs->num_points > MAXBATCH) {	/* point buffer size */
		XDrawPoints(dsp, fs->win, Scr[screen].gc, fs->pts,
			    fs->num_points, CoordModeOrigin);
		fs->num_points = 0;
	    }
	}
    } else {
	int i;
	for (i = 0; i < fs->SNUM; i++) {
	    double      nx, ny;
	    nx = fs->f[0][0][i] * x + fs->f[0][1][i] * y + fs->f[0][2][i];
	    ny = fs->f[1][0][i] * x + fs->f[1][1][i] * y + fs->f[1][2][i];
	    if (i < fs->ANUM) {
		nx = sin(nx);
		ny = sin(ny);
	    }
	    if (!recurse(fs, nx, ny, l + 1))
		return False;
	}
    }
    return True;
}


void
drawflame(Window win)
{
    flamestruct *fs = &flames[screen];

    int         i, j, k;
    static int  alt = 0;

    if (!(fs->cur_level++ % fs->max_levels)) {
	XClearWindow(dsp, fs->win);
	alt = !alt;
    } else {
 	if (Scr[screen].npixels > 2) {
  	    XSetForeground(dsp, Scr[screen].gc,
 			   Scr[screen].pixels[fs->pixcol]);
	    if (--fs->pixcol < 0)
 		fs->pixcol = Scr[screen].npixels - 1;
 	}
    }

    /* number of functions */
    fs->SNUM = 2 + (fs->cur_level % 3);

    /* how many of them are of alternate form */
    if (alt)
	fs->ANUM = 0;
    else
	fs->ANUM = halfrandom(fs->SNUM) + 2;

    /* 6 coefs per function */
    for (k = 0; k < fs->SNUM; k++) {
	for (i = 0; i < 2; i++)
	    for (j = 0; j < 3; j++)
		fs->f[i][j][k] = ((double) (random() & 1023) / 512.0 - 1.0);
    }
    fs->num_points = 0;
    fs->total_points = 0;
    (void) recurse(fs, 0.0, 0.0, 0);
    XDrawPoints(dsp, win, Scr[screen].gc,
		fs->pts, fs->num_points, CoordModeOrigin);
}