// This file is part of Golly.
// See docs/License.html for the copyright notice.

#include "wx/wxprec.h"  // for compilers that support precompilation
#ifndef WX_PRECOMP
#include "wx/wx.h"      // for all others include the necessary headers
#endif

#include "wx/filename.h"   // for wxFileName

#include "bigint.h"
#include "lifealgo.h"
#include "qlifealgo.h"
#include "hlifealgo.h"
#include "generationsalgo.h"
#include "ltlalgo.h"
#include "jvnalgo.h"
#include "superalgo.h"
#include "ruleloaderalgo.h"

#include "wxgolly.h"       // for wxGetApp
#include "wxmain.h"        // for ID_ALGO0
#include "wxutils.h"       // for Fatal, Warning
#include "wxprefs.h"       // for gollydir
#include "wxlayer.h"       // for currlayer
#include "wxalgos.h"

// -----------------------------------------------------------------------------

// exported data:

wxMenu* algomenu;                   // menu of algorithm names
wxMenu* algomenupop;                // copy of algomenu for PopupMenu calls
algo_type initalgo = QLIFE_ALGO;    // initial layer's algorithm
AlgoData* algoinfo[MAX_ALGOS];      // static info for each algorithm

wxBitmap** circles7x7;              // circular icons for scale 1:8
wxBitmap** circles15x15;            // circular icons for scale 1:16
wxBitmap** circles31x31;            // circular icons for scale 1:32

wxBitmap** diamonds7x7;             // diamond-shaped icons for scale 1:8
wxBitmap** diamonds15x15;           // diamond-shaped icons for scale 1:16
wxBitmap** diamonds31x31;           // diamond-shaped icons for scale 1:32

wxBitmap** hexagons7x7;             // hexagonal icons for scale 1:8
wxBitmap** hexagons15x15;           // hexagonal icons for scale 1:16
wxBitmap** hexagons31x31;           // hexagonal icons for scale 1:32

wxBitmap** triangles7x7;            // triangular icons for scale 1:8
wxBitmap** triangles15x15;          // triangular icons for scale 1:16
wxBitmap** triangles31x31;          // triangular icons for scale 1:32

// -----------------------------------------------------------------------------

// These default cell colors were generated by continuously finding the
// color furthest in rgb space from the closest of the already selected
// colors, black, and white.

static unsigned char default_colors[] = {
    48,48,48, // better if state 0 is dark gray (was 255,127,0)
    0,255,127,127,0,255,148,148,148,128,255,0,255,0,128,
    0,128,255,1,159,0,159,0,1,255,254,96,0,1,159,96,255,254,
    254,96,255,126,125,21,21,126,125,125,21,126,255,116,116,116,255,116,
    116,116,255,228,227,0,28,255,27,255,27,28,0,228,227,227,0,228,
    27,28,255,59,59,59,234,195,176,175,196,255,171,194,68,194,68,171,
    68,171,194,72,184,71,184,71,72,71,72,184,169,255,188,252,179,63,
    63,252,179,179,63,252,80,9,0,0,80,9,9,0,80,255,175,250,
    199,134,213,115,100,95,188,163,0,0,188,163,163,0,188,203,73,0,
    0,203,73,73,0,203,94,189,0,189,0,94,0,94,189,187,243,119,
    55,125,32,125,32,55,32,55,125,255,102,185,102,185,255,120,209,168,
    208,166,119,135,96,192,182,255,41,83,153,130,247,88,55,89,247,55,
    88,55,247,87,75,0,0,87,75,75,0,87,200,135,59,51,213,127,
    255,255,162,255,37,182,37,182,255,228,57,117,142,163,210,57,117,228,
    193,255,246,188,107,123,123,194,107,145,59,5,5,145,59,59,5,145,
    119,39,198,40,197,23,197,23,40,23,40,197,178,199,158,255,201,121,
    134,223,223,39,253,84,149,203,15,203,15,149,15,149,203,152,144,90,
    143,75,139,71,97,132,224,65,219,65,219,224,255,255,40,218,223,69,
    74,241,0,241,0,74,0,74,241,122,171,51,220,211,227,61,127,87,
    90,124,176,36,39,13,165,142,255,255,38,255,38,255,255,83,50,107,
    224,142,165,255,181,9,9,255,181,181,9,255,140,238,70,255,74,5,
    74,5,255,138,84,51,31,172,101,177,115,17,221,0,0,0,221,0,
    0,0,221,220,255,200,0,41,50,255,150,205,178,45,116,113,255,189,
    47,0,44,40,119,171,205,107,255,177,115,172,133,73,236,109,0,168,
    168,46,207,188,181,203,212,188,35,90,97,52,39,209,184,41,164,152,
    227,46,70,46,70,227,211,156,255,98,146,222,136,56,95,102,54,152,
    86,142,0,142,0,86,0,86,142,86,223,96,246,135,46,4,208,120,
    212,233,158,177,92,214,104,147,88,149,240,147,227,93,148,72,255,133,
    209,27,194,147,255,255,44,93,0,160,36,158,182,233,0,96,94,217,
    218,103,88,163,154,38,118,114,139,94,0,43,113,164,174,168,188,114,
    0,23,119,42,86,93,255,226,202,80,191,155,255,158,136,0,247,62,
    234,146,88,0,183,229,110,212,36,0,143,161,105,191,210,133,164,0,
    41,30,89,164,0,132,30,89,42,178,222,217,121,22,11,221,107,22,
    69,151,255,45,158,3,158,3,45,3,45,158,86,42,29,9,122,22,
    213,209,110,53,221,57,159,101,91,93,140,45,247,213,37,185,34,0,
    0,185,34,34,0,185,236,0,172,210,180,78,231,107,221,162,49,43,
    43,162,49,49,43,162,36,248,213,114,0,214,213,36,248,149,34,243,
    185,158,167,144,122,224,34,245,149,255,31,98,31,98,255,152,200,193,
    255,80,95,128,123,63,102,62,72,255,62,148,151,226,108,159,99,255,
    226,255,126,98,223,136,80,95,255,225,153,15,73,41,211,212,71,41,
    83,217,187,180,235,79,0,166,127,251,135,243,229,41,0,41,0,229,
    82,255,216,141,174,249,249,215,255,167,31,79,31,79,167,213,102,185,
    255,215,83,4,2,40,224,171,220,41,0,4,6,50,90,221,15,113,
    15,113,221,33,0,115,108,23,90,182,215,36
};

// -----------------------------------------------------------------------------

// Note that all the default icons are grayscale bitmaps.
// These icons are used for lots of different rules with different numbers
// of states, and at rendering time we will replace the white pixels in each
// icon with the cell's state color to avoid "color shock" when switching
// between icon and non-icon view.  Gray pixels are used to do anti-aliasing.

// XPM data for default 7x7 icon
static const char* default7x7[] = {
// width height ncolors chars_per_pixel
"7 7 4 1",
// colors
". c #000000",    // black will be transparent
"D c #404040",
"E c #E0E0E0",
"W c #FFFFFF",    // white
// pixels
".DEWED.",
"DWWWWWD",
"EWWWWWE",
"WWWWWWW",
"EWWWWWE",
"DWWWWWD",
".DEWED."
};

// XPM data for default 15x15 icon
static const char* default15x15[] = {
// width height ncolors chars_per_pixel
"15 15 5 1",
// colors
". c #000000",    // black will be transparent
"D c #404040",
"C c #808080",
"B c #C0C0C0",
"W c #FFFFFF",    // white
// pixels
"...............",
"....DBWWWBD....",
"...BWWWWWWWB...",
"..BWWWWWWWWWB..",
".DWWWWWWWWWWWD.",
".BWWWWWWWWWWWB.",
".WWWWWWWWWWWWW.",
".WWWWWWWWWWWWW.",
".WWWWWWWWWWWWW.",
".BWWWWWWWWWWWB.",
".DWWWWWWWWWWWD.",
"..BWWWWWWWWWB..",
"...BWWWWWWWB...",
"....DBWWWBD....",
"..............."
};

// XPM data for default 31x31 icon
static const char* default31x31[] = {
// width height ncolors chars_per_pixel
"31 31 5 1",
// colors
". c #000000",    // black will be transparent
"D c #404040",
"C c #808080",
"B c #C0C0C0",
"W c #FFFFFF",    // white
// pixels
"...............................",
"...............................",
"..........DCBWWWWWBCD..........",
".........CWWWWWWWWWWWC.........",
".......DWWWWWWWWWWWWWWWD.......",
"......BWWWWWWWWWWWWWWWWWB......",
".....BWWWWWWWWWWWWWWWWWWWB.....",
"....DWWWWWWWWWWWWWWWWWWWWWD....",
"....WWWWWWWWWWWWWWWWWWWWWWW....",
"...CWWWWWWWWWWWWWWWWWWWWWWWC...",
"..DWWWWWWWWWWWWWWWWWWWWWWWWWD..",
"..CWWWWWWWWWWWWWWWWWWWWWWWWWC..",
"..BWWWWWWWWWWWWWWWWWWWWWWWWWB..",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"..BWWWWWWWWWWWWWWWWWWWWWWWWWB..",
"..CWWWWWWWWWWWWWWWWWWWWWWWWWC..",
"..DWWWWWWWWWWWWWWWWWWWWWWWWWD..",
"...CWWWWWWWWWWWWWWWWWWWWWWWC...",
"....WWWWWWWWWWWWWWWWWWWWWWW....",
"....DWWWWWWWWWWWWWWWWWWWWWD....",
".....BWWWWWWWWWWWWWWWWWWWB.....",
"......BWWWWWWWWWWWWWWWWWB......",
".......DWWWWWWWWWWWWWWWD.......",
".........CWWWWWWWWWWWC.........",
"..........DCBWWWWWBCD..........",
"...............................",
"..............................."
};

// XPM data for the 7x7 icon used for hexagonal CA
static const char* hex7x7[] = {
// width height ncolors chars_per_pixel
"7 7 3 1",
// colors
". c #000000",    // black will be transparent
"C c #808080",
"W c #FFFFFF",    // white
// pixels
".WWC...",
"WWWWW..",
"WWWWWW.",
"CWWWWWC",
".WWWWWW",
"..WWWWW",
"...CWW."};

// XPM data for the 15x15 icon used for hexagonal CA
static const char* hex15x15[] = {
// width height ncolors chars_per_pixel
"15 15 3 1",
// colors
". c #000000",    // black will be transparent
"C c #808080",
"W c #FFFFFF",    // white
// pixels
"...WWC.........",
"..WWWWWC.......",
".WWWWWWWWC.....",
"WWWWWWWWWWW....",
"WWWWWWWWWWWW...",
"CWWWWWWWWWWWC..",
".WWWWWWWWWWWW..",
".CWWWWWWWWWWWC.",
"..WWWWWWWWWWWW.",
"..CWWWWWWWWWWWC",
"...WWWWWWWWWWWW",
"....WWWWWWWWWWW",
".....CWWWWWWWW.",
".......CWWWWW..",
".........CWW..."};

// XPM data for 31x31 icon used for hexagonal CA
static const char* hex31x31[] = {
// width height ncolors chars_per_pixel
"31 31 3 1",
// colors
". c #000000",    // black will be transparent
"C c #808080",
"W c #FFFFFF",    // white
// pixels
".....WWC.......................",
"....WWWWWC.....................",
"...WWWWWWWWC...................",
"..WWWWWWWWWWWC.................",
".WWWWWWWWWWWWWWC...............",
"WWWWWWWWWWWWWWWWWC.............",
"WWWWWWWWWWWWWWWWWWWC...........",
"CWWWWWWWWWWWWWWWWWWWWC.........",
".WWWWWWWWWWWWWWWWWWWWWW........",
".CWWWWWWWWWWWWWWWWWWWWWC.......",
"..WWWWWWWWWWWWWWWWWWWWWW.......",
"..CWWWWWWWWWWWWWWWWWWWWWC......",
"...WWWWWWWWWWWWWWWWWWWWWW......",
"...CWWWWWWWWWWWWWWWWWWWWWC.....",
"....WWWWWWWWWWWWWWWWWWWWWW.....",
"....CWWWWWWWWWWWWWWWWWWWWWC....",
".....WWWWWWWWWWWWWWWWWWWWWW....",
".....CWWWWWWWWWWWWWWWWWWWWWC...",
"......WWWWWWWWWWWWWWWWWWWWWW...",
"......CWWWWWWWWWWWWWWWWWWWWWC..",
".......WWWWWWWWWWWWWWWWWWWWWW..",
".......CWWWWWWWWWWWWWWWWWWWWWC.",
"........WWWWWWWWWWWWWWWWWWWWWW.",
".........CWWWWWWWWWWWWWWWWWWWWC",
"...........CWWWWWWWWWWWWWWWWWWW",
".............CWWWWWWWWWWWWWWWWW",
"...............CWWWWWWWWWWWWWW.",
".................CWWWWWWWWWWW..",
"...................CWWWWWWWW...",
".....................CWWWWW....",
".......................CWW....."
};

// XPM data for the 7x7 icon used for von Neumann CA
static const char* vn7x7[] = {
// width height ncolors chars_per_pixel
"7 7 2 1",
// colors
". c #000000",    // black will be transparent
"W c #FFFFFF",    // white
// pixels
"...W...",
"..WWW..",
".WWWWW.",
"WWWWWWW",
".WWWWW.",
"..WWW..",
"...W..."
};

// XPM data for the 15x15 icon used for von Neumann CA
static const char* vn15x15[] = {
// width height ncolors chars_per_pixel
"15 15 2 1",
// colors
". c #000000",    // black will be transparent
"W c #FFFFFF",    // white
// pixels
"...............",
".......W.......",
"......WWW......",
".....WWWWW.....",
"....WWWWWWW....",
"...WWWWWWWWW...",
"..WWWWWWWWWWW..",
".WWWWWWWWWWWWW.",
"..WWWWWWWWWWW..",
"...WWWWWWWWW...",
"....WWWWWWW....",
".....WWWWW.....",
"......WWW......",
".......W.......",
"..............."
};

// XPM data for 31x31 icon used for von Neumann CA
static const char* vn31x31[] = {
// width height ncolors chars_per_pixel
"31 31 2 1",
// colors
". c #000000",    // black will be transparent
"W c #FFFFFF",    // white
// pixels
"...............................",
"...............................",
"...............W...............",
"..............WWW..............",
".............WWWWW.............",
"............WWWWWWW............",
"...........WWWWWWWWW...........",
"..........WWWWWWWWWWW..........",
".........WWWWWWWWWWWWW.........",
"........WWWWWWWWWWWWWWW........",
".......WWWWWWWWWWWWWWWWW.......",
"......WWWWWWWWWWWWWWWWWWW......",
".....WWWWWWWWWWWWWWWWWWWWW.....",
"....WWWWWWWWWWWWWWWWWWWWWWW....",
"...WWWWWWWWWWWWWWWWWWWWWWWWW...",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"...WWWWWWWWWWWWWWWWWWWWWWWWW...",
"....WWWWWWWWWWWWWWWWWWWWWWW....",
".....WWWWWWWWWWWWWWWWWWWWW.....",
"......WWWWWWWWWWWWWWWWWWW......",
".......WWWWWWWWWWWWWWWWW.......",
"........WWWWWWWWWWWWWWW........",
".........WWWWWWWWWWWWW.........",
"..........WWWWWWWWWWW..........",
"...........WWWWWWWWW...........",
"............WWWWWWW............",
".............WWWWW.............",
"..............WWW..............",
"...............W...............",
"...............................",
"..............................."
};

// XPM data for the 7x7 icons used by 4-state rules emulating a triangular neighborhood
static const char* tri7x7[] = {
// width height ncolors chars_per_pixel
"7 21 2 1",
// colors
". c #000000",    // black will be transparent
"W c #FFFFFF",    // white
// pixels for state 1
".......",
"W......",
"WW.....",
"WWW....",
"WWWW...",
"WWWWW..",
"WWWWWW.",
// pixels for state 2
".WWWWWW",
"..WWWWW",
"...WWWW",
"....WWW",
".....WW",
"......W",
".......",
// pixels for state 3
".WWWWWW",
"W.WWWWW",
"WW.WWWW",
"WWW.WWW",
"WWWW.WW",
"WWWWW.W",
"WWWWWW."
};

// XPM data for the 15x15 icons used by 4-state rules emulating a triangular neighborhood
static const char* tri15x15[] = {
// width height ncolors chars_per_pixel
"15 45 2 1",
// colors
". c #000000",
"W c #FFFFFF",
// pixels for state 1
"...............",
"W..............",
"WW.............",
"WWW............",
"WWWW...........",
"WWWWW..........",
"WWWWWW.........",
"WWWWWWW........",
"WWWWWWWW.......",
"WWWWWWWWW......",
"WWWWWWWWWW.....",
"WWWWWWWWWWW....",
"WWWWWWWWWWWW...",
"WWWWWWWWWWWWW..",
"WWWWWWWWWWWWWW.",
// pixels for state 2
".WWWWWWWWWWWWWW",
"..WWWWWWWWWWWWW",
"...WWWWWWWWWWWW",
"....WWWWWWWWWWW",
".....WWWWWWWWWW",
"......WWWWWWWWW",
".......WWWWWWWW",
"........WWWWWWW",
".........WWWWWW",
"..........WWWWW",
"...........WWWW",
"............WWW",
".............WW",
"..............W",
"...............",
// pixels for state 3
".WWWWWWWWWWWWWW",
"W.WWWWWWWWWWWWW",
"WW.WWWWWWWWWWWW",
"WWW.WWWWWWWWWWW",
"WWWW.WWWWWWWWWW",
"WWWWW.WWWWWWWWW",
"WWWWWW.WWWWWWWW",
"WWWWWWW.WWWWWWW",
"WWWWWWWW.WWWWWW",
"WWWWWWWWW.WWWWW",
"WWWWWWWWWW.WWWW",
"WWWWWWWWWWW.WWW",
"WWWWWWWWWWWW.WW",
"WWWWWWWWWWWWW.W",
"WWWWWWWWWWWWWW."
};

// XPM data for the 31x31 icons used by 4-state rules emulating a triangular neighborhood
static const char* tri31x31[] = {
// width height ncolors chars_per_pixel
"31 93 2 1",
// colors
". c #000000",
"W c #FFFFFF",
// pixels for state 1
"...............................",
"W..............................",
"WW.............................",
"WWW............................",
"WWWW...........................",
"WWWWW..........................",
"WWWWWW.........................",
"WWWWWWW........................",
"WWWWWWWW.......................",
"WWWWWWWWW......................",
"WWWWWWWWWW.....................",
"WWWWWWWWWWW....................",
"WWWWWWWWWWWW...................",
"WWWWWWWWWWWWW..................",
"WWWWWWWWWWWWWW.................",
"WWWWWWWWWWWWWWW................",
"WWWWWWWWWWWWWWWW...............",
"WWWWWWWWWWWWWWWWW..............",
"WWWWWWWWWWWWWWWWWW.............",
"WWWWWWWWWWWWWWWWWWW............",
"WWWWWWWWWWWWWWWWWWWW...........",
"WWWWWWWWWWWWWWWWWWWWW..........",
"WWWWWWWWWWWWWWWWWWWWWW.........",
"WWWWWWWWWWWWWWWWWWWWWWW........",
"WWWWWWWWWWWWWWWWWWWWWWWW.......",
"WWWWWWWWWWWWWWWWWWWWWWWWW......",
"WWWWWWWWWWWWWWWWWWWWWWWWWW.....",
"WWWWWWWWWWWWWWWWWWWWWWWWWWW....",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWW...",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWW..",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWWW.",
// pixels for state 2
".WWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"..WWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"...WWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"....WWWWWWWWWWWWWWWWWWWWWWWWWWW",
".....WWWWWWWWWWWWWWWWWWWWWWWWWW",
"......WWWWWWWWWWWWWWWWWWWWWWWWW",
".......WWWWWWWWWWWWWWWWWWWWWWWW",
"........WWWWWWWWWWWWWWWWWWWWWWW",
".........WWWWWWWWWWWWWWWWWWWWWW",
"..........WWWWWWWWWWWWWWWWWWWWW",
"...........WWWWWWWWWWWWWWWWWWWW",
"............WWWWWWWWWWWWWWWWWWW",
".............WWWWWWWWWWWWWWWWWW",
"..............WWWWWWWWWWWWWWWWW",
"...............WWWWWWWWWWWWWWWW",
"................WWWWWWWWWWWWWWW",
".................WWWWWWWWWWWWWW",
"..................WWWWWWWWWWWWW",
"...................WWWWWWWWWWWW",
"....................WWWWWWWWWWW",
".....................WWWWWWWWWW",
"......................WWWWWWWWW",
".......................WWWWWWWW",
"........................WWWWWWW",
".........................WWWWWW",
"..........................WWWWW",
"...........................WWWW",
"............................WWW",
".............................WW",
"..............................W",
"...............................",
// pixels for state 3
".WWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"W.WWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"WW.WWWWWWWWWWWWWWWWWWWWWWWWWWWW",
"WWW.WWWWWWWWWWWWWWWWWWWWWWWWWWW",
"WWWW.WWWWWWWWWWWWWWWWWWWWWWWWWW",
"WWWWW.WWWWWWWWWWWWWWWWWWWWWWWWW",
"WWWWWW.WWWWWWWWWWWWWWWWWWWWWWWW",
"WWWWWWW.WWWWWWWWWWWWWWWWWWWWWWW",
"WWWWWWWW.WWWWWWWWWWWWWWWWWWWWWW",
"WWWWWWWWW.WWWWWWWWWWWWWWWWWWWWW",
"WWWWWWWWWW.WWWWWWWWWWWWWWWWWWWW",
"WWWWWWWWWWW.WWWWWWWWWWWWWWWWWWW",
"WWWWWWWWWWWW.WWWWWWWWWWWWWWWWWW",
"WWWWWWWWWWWWW.WWWWWWWWWWWWWWWWW",
"WWWWWWWWWWWWWW.WWWWWWWWWWWWWWWW",
"WWWWWWWWWWWWWWW.WWWWWWWWWWWWWWW",
"WWWWWWWWWWWWWWWW.WWWWWWWWWWWWWW",
"WWWWWWWWWWWWWWWWW.WWWWWWWWWWWWW",
"WWWWWWWWWWWWWWWWWW.WWWWWWWWWWWW",
"WWWWWWWWWWWWWWWWWWW.WWWWWWWWWWW",
"WWWWWWWWWWWWWWWWWWWW.WWWWWWWWWW",
"WWWWWWWWWWWWWWWWWWWWW.WWWWWWWWW",
"WWWWWWWWWWWWWWWWWWWWWW.WWWWWWWW",
"WWWWWWWWWWWWWWWWWWWWWWW.WWWWWWW",
"WWWWWWWWWWWWWWWWWWWWWWWW.WWWWWW",
"WWWWWWWWWWWWWWWWWWWWWWWWW.WWWWW",
"WWWWWWWWWWWWWWWWWWWWWWWWWW.WWWW",
"WWWWWWWWWWWWWWWWWWWWWWWWWWW.WWW",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWW.WW",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWW.W",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWWW."
};

// -----------------------------------------------------------------------------

wxBitmap** CreateIconBitmaps(const char** xpmdata, int maxstates)
{
    if (xpmdata == NULL) return NULL;
    
    wxImage image(xpmdata);
    
#if defined(__WXMSW__) || defined(__WXGTK__)
    if (!image.HasAlpha()) {
        // add alpha channel and set to opaque
        image.InitAlpha();
    }
#endif
    
    wxBitmap allicons(image, -1);    // RGBA
    
    int wd = allicons.GetWidth();
    int numicons = allicons.GetHeight() / wd;
    if (numicons > 255) numicons = 255;          // play safe
    
    wxBitmap** iconptr = (wxBitmap**) malloc(256 * sizeof(wxBitmap*));
    wxBitmap* lastIcon = NULL;

    if (iconptr) {
        // initialize all pointers (not just those < maxstates)
        for (int i = 0; i < 256; i++) iconptr[i] = NULL;
        
        for (int i = 0; i < numicons; i++) {
            wxRect rect(0, i*wd, wd, wd);
            // add 1 to skip iconptr[0] (ie. dead state)
            lastIcon = new wxBitmap(allicons.GetSubBitmap(rect));
            iconptr[i+1] = lastIcon;
        }
        
        if (numicons < maxstates-1 && iconptr[numicons]) {
            // duplicate last icon using the last created bitmap
            for (int i = numicons; i < maxstates-1; i++) {
                iconptr[i+1] = lastIcon;
            }
        }
    }
    return iconptr;
}

// -----------------------------------------------------------------------------

void FreeIconBitmaps(wxBitmap** icons)
{
    if (icons) {
        wxBitmap* lastIcon = NULL;
        wxBitmap* icon = NULL;

        // if there are less icons than states then the last run of icons
        // all use the same bitmap so account for this when deleting
        for (int i = 0; i < 256; i++) {
            icon = icons[i];
            if (icon && (icon != lastIcon)) {
                delete icon;
            }
            lastIcon = icon;
        }
        free(icons);
    }
}

// -----------------------------------------------------------------------------

wxBitmap** ScaleIconBitmaps(wxBitmap** srcicons, int size)
{
    if (srcicons == NULL) return NULL;
    
    wxBitmap** iconptr = (wxBitmap**) malloc(256 * sizeof(wxBitmap*));
    if (iconptr) {
        for (int i = 0; i < 256; i++) {
            if (srcicons[i] == NULL) {
                iconptr[i] = NULL;
            } else {
                wxImage image = srcicons[i]->ConvertToImage();                
                // do NOT scale using wxIMAGE_QUALITY_HIGH (thin lines can disappear)
                image.Rescale(size, size, wxIMAGE_QUALITY_NORMAL);
#if defined(__WXMSW__) || defined(__WXGTK__)
                if (!image.HasAlpha()) {
                    // add alpha channel and set to opaque
                    image.InitAlpha();
                }
#endif
                iconptr[i] = new wxBitmap(image, -1);
            }
        }
    }
    return iconptr;
}

// -----------------------------------------------------------------------------

static void CreateDefaultIcons(AlgoData* ad)
{
    if (ad->defxpm7x7 || ad->defxpm15x15 || ad->defxpm31x31) {
        // create icons using given algo's default XPM data
        ad->icons7x7 = CreateIconBitmaps(ad->defxpm7x7, ad->maxstates);
        ad->icons15x15 = CreateIconBitmaps(ad->defxpm15x15, ad->maxstates);
        ad->icons31x31 = CreateIconBitmaps(ad->defxpm31x31, ad->maxstates);
        
        // create scaled bitmaps if size(s) not supplied
        if (!ad->icons7x7) {
            if (ad->icons15x15)
                // scale down 15x15 bitmaps
                ad->icons7x7 = ScaleIconBitmaps(ad->icons15x15, 7);
            else
                // scale down 31x31 bitmaps
                ad->icons7x7 = ScaleIconBitmaps(ad->icons31x31, 7);
        }
        if (!ad->icons15x15) {
            if (ad->icons31x31)
                // scale down 31x31 bitmaps
                ad->icons15x15 = ScaleIconBitmaps(ad->icons31x31, 15);
            else
                // scale up 7x7 bitmaps
                ad->icons15x15 = ScaleIconBitmaps(ad->icons7x7, 15);
        }
        if (!ad->icons31x31) {
            if (ad->icons15x15)
                // scale up 15x15 bitmaps
                ad->icons31x31 = ScaleIconBitmaps(ad->icons15x15, 31);
            else
                // scale up 7x7 bitmaps
                ad->icons31x31 = ScaleIconBitmaps(ad->icons7x7, 31);
        }
    } else {
        // algo didn't supply any icons so use static XPM data defined above
        ad->icons7x7 = CreateIconBitmaps(default7x7, ad->maxstates);
        ad->icons15x15 = CreateIconBitmaps(default15x15, ad->maxstates);
        ad->icons31x31 = CreateIconBitmaps(default31x31, ad->maxstates);
    }
}

// -----------------------------------------------------------------------------

AlgoData::AlgoData() {
    algomem = defbase = 0;
    statusbrush = NULL;
    icons7x7 = NULL;
    icons15x15 = NULL;
    icons31x31 = NULL;
}

// -----------------------------------------------------------------------------

AlgoData::~AlgoData() 
{
    FreeIconBitmaps(icons7x7);
    FreeIconBitmaps(icons15x15);
    FreeIconBitmaps(icons31x31);
    delete statusbrush;
}

// -----------------------------------------------------------------------------

AlgoData& AlgoData::tick() {
    AlgoData* r = new AlgoData();
    algoinfo[r->id] = r;
    return *r;
}

// -----------------------------------------------------------------------------

void InitAlgorithms()
{
    // QuickLife must be 1st and HashLife must be 2nd
    qlifealgo::doInitializeAlgoInfo(AlgoData::tick());
    hlifealgo::doInitializeAlgoInfo(AlgoData::tick());
    
    // these algos can be in any order
    generationsalgo::doInitializeAlgoInfo(AlgoData::tick());
    ltlalgo::doInitializeAlgoInfo(AlgoData::tick());
    jvnalgo::doInitializeAlgoInfo(AlgoData::tick());
    superalgo::doInitializeAlgoInfo(AlgoData::tick());
    
    // RuleLoader must be last so we can display detailed error messages
    // (see LoadRule in wxhelp.cpp)
    ruleloaderalgo::doInitializeAlgoInfo(AlgoData::tick());
    
    // algomenu is used for the Control > Set Algorithm submenu;
    // algomenupop is used when the tool bar's algo button is pressed
    // (we can't share a single menu for both purposes because we get
    // assert messages with wxOSX and wxGTK 2.9+)
    algomenu = new wxMenu();
    algomenupop = new wxMenu();
    
    // init algoinfo array
    for (int i = 0; i < NumAlgos(); i++) {
        AlgoData* ad = algoinfo[i];
        if (ad->algoName == 0 || ad->creator == 0)
            Fatal(_("Algorithm did not set name and/or creator"));
        
        wxString name = wxString(ad->algoName, wxConvLocal);
        algomenu->AppendCheckItem(ID_ALGO0 + i, name);
        algomenupop->AppendCheckItem(ID_ALGO0 + i, name);
        
        // does algo use hashing?
        ad->canhash = ad->defbase == 8;    // safer method needed???
        
        // set status bar background by cycling thru a few pale colors
        switch (i % 9) {
            case 0: ad->statusrgb.Set(255, 255, 206); break;  // pale yellow
            case 1: ad->statusrgb.Set(226, 250, 248); break;  // pale blue
            case 2: ad->statusrgb.Set(255, 233, 233); break;  // pale pink
            case 3: ad->statusrgb.Set(255, 227, 178); break;  // pale orange
            case 4: ad->statusrgb.Set(225, 255, 225); break;  // pale green
            case 5: ad->statusrgb.Set(243, 225, 255); break;  // pale purple
            case 6: ad->statusrgb.Set(200, 255, 255); break;  // pale aqua
            case 7: ad->statusrgb.Set(200, 200, 200); break;  // pale gray
            case 8: ad->statusrgb.Set(255, 255, 255); break;  // white
        }
        ad->statusbrush = new wxBrush(ad->statusrgb);
        
        // initialize default color scheme
        if (ad->defr[0] == ad->defr[1] &&
            ad->defg[0] == ad->defg[1] &&
            ad->defb[0] == ad->defb[1]) {
            // colors are nonsensical, probably unset, so use above defaults
            unsigned char* rgbptr = default_colors;
            for (int c = 0; c < ad->maxstates; c++) {
                ad->defr[c] = *rgbptr++;
                ad->defg[c] = *rgbptr++;
                ad->defb[c] = *rgbptr++;
            }
        }
        ad->gradient = ad->defgradient;
        ad->fromrgb.Set(ad->defr1, ad->defg1, ad->defb1);
        ad->torgb.Set(ad->defr2, ad->defg2, ad->defb2);
        for (int c = 0; c < ad->maxstates; c++) {
            ad->algor[c] = ad->defr[c];
            ad->algog[c] = ad->defg[c];
            ad->algob[c] = ad->defb[c];
        }
        
        CreateDefaultIcons(ad);
    }
    
    circles7x7 = CreateIconBitmaps(default7x7,256);
    circles15x15 = CreateIconBitmaps(default15x15,256);
    circles31x31 = CreateIconBitmaps(default31x31,256);
    
    diamonds7x7 = CreateIconBitmaps(vn7x7,256);
    diamonds15x15 = CreateIconBitmaps(vn15x15,256);
    diamonds31x31 = CreateIconBitmaps(vn31x31,256);
    
    hexagons7x7 = CreateIconBitmaps(hex7x7,256);
    hexagons15x15 = CreateIconBitmaps(hex15x15,256);
    hexagons31x31 = CreateIconBitmaps(hex31x31,256);
    
    // these icons can only be used with 4-state rules
    triangles7x7 = CreateIconBitmaps(tri7x7,4);
    triangles15x15 = CreateIconBitmaps(tri15x15,4);
    triangles31x31 = CreateIconBitmaps(tri31x31,4);
}

// -----------------------------------------------------------------------------

void DeleteAlgorithms()
{
    for (int i = 0; i < NumAlgos(); i++)
        delete algoinfo[i];
    
    FreeIconBitmaps(circles7x7);
    FreeIconBitmaps(circles15x15);
    FreeIconBitmaps(circles31x31);
    
    FreeIconBitmaps(diamonds7x7);
    FreeIconBitmaps(diamonds15x15);
    FreeIconBitmaps(diamonds31x31);
    
    FreeIconBitmaps(hexagons7x7);
    FreeIconBitmaps(hexagons15x15);
    FreeIconBitmaps(hexagons31x31);
    
    FreeIconBitmaps(triangles7x7);
    FreeIconBitmaps(triangles15x15);
    FreeIconBitmaps(triangles31x31);
    
    delete algomenupop;
}

// -----------------------------------------------------------------------------

lifealgo* CreateNewUniverse(algo_type algotype, bool allowcheck)
{
    lifealgo* newalgo = algoinfo[algotype]->creator();
    
    if (newalgo == NULL) Fatal(_("Failed to create new universe!"));
    
    if (algoinfo[algotype]->algomem >= 0)
        newalgo->setMaxMemory(algoinfo[algotype]->algomem);
    
    if (allowcheck) newalgo->setpoll(wxGetApp().Poller());
    
    return newalgo;
}

// -----------------------------------------------------------------------------

const char* GetAlgoName(algo_type algotype)
{
    return algoinfo[algotype]->algoName;
}

// -----------------------------------------------------------------------------

int NumAlgos()
{
    return staticAlgoInfo::getNumAlgos();
}
