#include "intuition/intuition.h"
#include "libraries/dos.h"

#define FREEPEN (-1)

struct Screen *OpenScreen();
struct Window *OpenWindow();

/*
 * sc.c - fractalish terrain generator.
 *
 *   Date:     March 5, 1987 (original version sometime in 1985)
 *   Author:   Chris Gray
 *   Language: C
 *   System:   Amiga
 */

/*
 * The nature of the terrain can be changed by playing with the numbers
 * in 'Range'. If you change SIZE, you must also change the number of
 * values given for 'Range'. The created terrain with SIZE = 8 is 256 pixels
 * by 256 pixels, which doesn't fit on a non-interlaced screen, so only the
 * top 200 pixels are displayed. The terrain is a torus, i.e. wraps around
 * both vertically and horizontally.
 */

/*
 * Feel free to use this algorithm in any games you write, so long as you
 * give me credit for it. (I THINK I invented it, since I've never heard of
 * anything similar, and other programs I've seen use much slower methods.)
 */

#define SIZE 8
#define COUNT (1 << SIZE)
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200
#define SCREEN_DEPTH 5
#define COLOURS (1 << SCREEN_DEPTH)
#define WINDOW_WIDTH (COUNT < SCREEN_WIDTH ? COUNT : SCREEN_WIDTH)
#define WINDOW_HEIGHT (COUNT < SCREEN_HEIGHT ? COUNT : SCREEN_HEIGHT)

unsigned short Range[SIZE] = {32, 32, 32, 22, 14, 8, 4, 2};

unsigned short ColourMap[COLOURS] = {
    0x00f, 0x050, 0x070, 0x0a0, 0x0c0, 0x0e0, 0x4f4, 0x8f8,
    0xdf0, 0xff0, 0xfd0, 0xfb0, 0xf90, 0xf70, 0xe50, 0xe33,
    0xe11, 0xf01, 0xf03, 0xf05, 0xf07, 0xf09, 0xf0b, 0xf0d,
    0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf, 0xfff
};

struct Screen *Screen;
struct Window *Window;

unsigned short Seed;

short Cell[COUNT][COUNT];

/*
 * random - return a random number 0 - passed range.
 */

unsigned short random(rang)
unsigned short rang;
{
    if (rang == 0)
        return 0;
    Seed = Seed * 17137 + 4287;
    Seed = (Seed >> 8) ^ (Seed << 8);
    return Seed % rang;
}

/*
 * set - set a given spot in Cell.
 */

void set(l, c, size, height)
unsigned short l, c, size;
short height;
{
    unsigned short rang;

    rang = Range[size];
    height = height + random(rang) - (rang + 1) / 2;
    Cell[l][c] = height;
}

/*
 * grow - grow the basic scenery heights.
 */

void grow()
{
    unsigned short l, c, i, step, nextStep, l1, l2, c1, c2;

    Cell[0][0] = 0;
    step = COUNT;
    for (i=0; i<SIZE; i++)
        {
        nextStep = step / 2;
        for (l=0; l<COUNT; l+=step)
            {
            l1 = l + nextStep;
            l2 = l + step;
            if (l2 == COUNT)
                l2 = 0;
            for (c=0; c<COUNT; c+=step)
                {
                c1 = c + nextStep;
                c2 = c + step;
                if (c2 == COUNT)
                    c2 = 0;
                set(l, c1, i, (Cell[l][c] + Cell[l][c2] + 1) / 2);
                set(l1, c, i, (Cell[l][c] + Cell[l2][c] + 1) / 2);
                set(l1, c1, i, (Cell[l][c] + Cell[l][c2] +
                                 Cell[l2][c] + Cell[l2][c2] + 2) / 4);
                }
            }
        step = nextStep;
        }
}

/*
 * display - display the resulting scenery.
 */

void display()
{
    unsigned short l, c;
    short height;

    for (l=0; l<WINDOW_HEIGHT; l++)
        {
        for (c=0; c<WINDOW_WIDTH; c++)
            {
            height = Cell[l][c];
            SetAPen(Window->RPort,
                    height<0 ?
                        0
                    : (height>=COLOURS ?
                        COLOURS-1
                    : height));
            WritePixel(Window->RPort, c, l);
            }
        }
}

struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
/*
 * main program.
 */

void main()
{
    static struct NewWindow newWindow =
            {
            0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,
            FREEPEN, FREEPEN,
            0x0, BORDERLESS | ACTIVATE | NOCAREREFRESH,
            NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
            CUSTOMSCREEN
            };

    static struct NewScreen newScreen =
          {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, 0, 1,
          0x0, CUSTOMSCREEN, NULL, NULL, NULL, NULL};
    struct DateStamp ds;
    static struct IntuiText bodyText =
        {COLOURS - 1, 0, 0, 51, 5, NULL, NULL, NULL};
    static struct IntuiText positiveText =
        {COLOURS - 1, 0, 0, 7, 3, NULL, NULL, NULL};
    static struct IntuiText negativeText =
        {COLOURS - 1, 0, 0, 7, 3, NULL, NULL, NULL};

    bodyText.IText = "Done";
    positiveText.IText = "Next";
    negativeText.IText = "Quit";
    IntuitionBase = (struct IntuitionBase *)
                    OpenLibrary("intuition.library", 0);
    if (IntuitionBase != NULL)
        {
        GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0);
        if (GfxBase != NULL)
            {
                Screen = OpenScreen(&newScreen);
                if (Screen != NULL)
                    {
                    LoadRGB4(&Screen->ViewPort, ColourMap, COLOURS);
                    newWindow.Screen = Screen;
                    Window = OpenWindow(&newWindow);
                    if (Window != NULL)
                        {
                        DateStamp(&ds);
                        Seed = (ds.ds_Minute ^ ds.ds_Tick) | 1;
                        do
                            {
                            grow();
                            display();
                            }
                        while (AutoRequest(Window, &bodyText, &positiveText,
                               &negativeText, 0x0, 0x0, 150, 50));
                        CloseWindow(Window);
                        }
                    CloseScreen(Screen);
                    }
            CloseLibrary(IntuitionBase);
            }
        CloseLibrary(GfxBase);
        }
}
