/*

    Minimum Profit 5.x
    A Programmer's Text Editor

    Useless things.

    Copyright (C) 1991-2012 Angel Ortega <angel@triptico.com>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    http://triptico.com

*/

/** editor actions **/

mp.actions['pipes'] =   sub (d) {
    mp.add(new(mp_pipes))->init();
};

/** action descriptions **/

mp.actdesc['pipes'] = LL("Play \"Pipes\"");

/** code **/

sub mp.maze(doc, keycode)
{
	local tx = mp.window.tx;
	local ty = mp.window.ty;

	if (doc == NULL)
		doc = mp.open('<maze>');

	local maze = doc.maze;

	if (maze == NULL || maze.tx != tx || maze.ty != ty) {
		/* new or changed: rebuild everything */
		maze = {};
		maze.tx = tx;
		maze.ty = ty;
		doc.maze = maze;

		/* ensure odd size */
		if (!(tx & 1))
			tx--;
		if (!(ty & 1))
			ty--;

		/* init */
		maze.map = map(
            [1 .. ty],
            sub {
                map(
                    [1 .. tx],
                    sub { '#'; }
                );
            }
        );

		/* build */
		local x = 1;
		local y = 1;
		local stack = [];

		while (1) {
			local d = [];

			/* clear */
			maze.map[y][x] = ' ';

			foreach (t, [[0, -1], [1, 0], [0, 1], [-1, 0]]) {
				/* can space be opened? */
				local ny = y + t[0] * 2;
				local nx = x + t[1] * 2;

				if (nx > 0 && ny > 0 && maze.map[ny][nx] eq '#')
					push(d, t);
			}

			if (size(d)) {
				/* more than one way? stack this position */
				if (size(d) > 1)
					push(stack, [y, x]);

				/* pick one direction at random and move there */
				local m = d[random(size(d))];

				y += m[0];
				x += m[1];

				maze.map[y][x] = ' ';

				y += m[0];
				x += m[1];
			}
			else {
				/* no way from here: pop previous position */
				if ((d = pop(stack)) == NULL)
					break;

				y = d[0];
				x = d[1];
			}
		}

		maze.x = 1 + random(tx / 2) * 2;
		maze.y = 1 + random(ty / 2) * 2;

		maze.map[maze.y][maze.x] = '@';

		x = 1 + random(tx / 2) * 2;
		y = 1 + random(ty / 2) * 2;

		maze.map[y][x] = 'X';

		doc.keypress = sub(d, k) { mp.maze(d, k); };

		doc.render = sub(d) { map(d.maze.map, sub(e) { [8, join(e)];}); };
	}

	local x = maze.x;
	local y = maze.y;

	maze.map[y][x] = ' ';

	if (keycode eq 'cursor-up')
		y--;
	if (keycode eq 'cursor-down')
		y++;
	if (keycode eq 'cursor-left')
		x--;
	if (keycode eq 'cursor-right')
		x++;

	if (maze.map[y][x] eq 'X') {
		mp.alert(L("You won!"));
		doc.maze = NULL;
		return mp.maze(doc);
	}

	if (maze.map[y][x] eq ' ') {
		maze.x = x;
		maze.y = y;
	}

	maze.map[maze.y][maze.x] = '@';

    doc->undo();

	return doc;
}


/** pipes **/

global mp_pipes = new(mp_base, {
        name:       '<pipes>',
        x:          0,
        y:          1,
        tx:         -1,
        ty:         -1,
        wx:         -1,
        wy:         -1,
        sx:         -1,
        state:      'RST',
        game_map:   NULL,
        attr:       NULL,

        get_status: sub (pipes) {
            L("Pipes - carry water from top to bottom by turning the pipes");
        },
        init:       sub (pipes) {
            if (state eq 'RST' || tx != mp.window.tx || ty != mp.window.ty) {
                tx = mp.window.tx;
                ty = mp.window.ty;

                /* convert to integers */
                wx = sprintf('%d', ((tx - 2) / 3));
                wy = sprintf('%d', (((ty - 1) / 2) - 1));

                sx = random(wx);

                game_map = map([1 .. wy - 1],
                    sub {map([1 .. wx], sub { random(size(pieces) - 3); });}
                );

                /* add first line */
                ins(game_map, map([1 .. wx], sub { 6; }), 0);
                game_map[0][sx] = 7;

                /* add last line */
                push(game_map, map([1 .. wx], sub { 6; }));
                game_map[wy][5] = 8;
                game_map[wy][wx - 5] = 8;

                state = 'PLAY';

                pipes->verify();
            }

            return pipes;
        },
        keypress:   sub (pipes, keycode) {
            local toggle = 0;

            pipes->init();

            if (keycode eq 'cursor-up' && y > 1)
                y--;
            if (keycode eq 'cursor-down'&& y < wy - 1)
                y++;
            if (keycode eq 'cursor-left' && x > 0)
                x--;
            if (keycode eq 'cursor-right' && x < wx - 1)
                x++;
            if (keycode eq 'enter')
                toggle = 1;
            if (keycode eq 'mouse-left-button') {
                local ty = sprintf("%d", mp.mouse_y / 2);

                if (ty >= 1 && ty < wy) {
                    local tx = sprintf("%d", mp.mouse_x / 3);

                    if (tx >= 0 && tx < wx) {
                        x = tx; y = ty;
                        toggle = 1;
                    }
                }
            }

            if (toggle) {
                local n = game_map[y][x];
                game_map[y][x] = pieces[n].next;
                pipes->verify();
            }

            return pipes;
        },
        verify:    sub (pipes) {
            /* reset attributes */
            attr = map([0 .. wy], sub { [];});

            /* the water input is blue */
            attr[0][sx] = mp.colors.quotes.attr;

            /* calculate the path */
            local px = sx;
            local py = 0;
            local c = 'D'; /* downwards */
            local p;

            /* while there is a valid path */
            while ((p = pieces[game_map[py][px]]) && (c = p.out[c]) && dir[c]) {
                /* mark as valid path */
                attr[py][px] = mp.colors.quotes.attr;

                /* move in new direction */
                px += dir[c][0];
                py += dir[c][1];
            }

            if (c eq 'WIN') {
                attr[py][px] = mp.colors.quotes.attr;
                state = 'WON';
            }

            return pipes;
        },
        render:      sub (pipes) {
            local out = [];

            pipes->init();

            foreach (n, [0 .. wy]) {
                local l0 = [];
                local l1 = [];

                foreach (m, [0 .. (wx - 1)]) {
                    local p = game_map[n][m];
                    local s = pieces[p].shape;
                    local c;

                    if (n == y && m == x)
                        c = mp.colors.cursor.attr;
                    else
                        c = attr[n][m] || mp.colors.normal.attr;

                    push(l0, c, s[0]);
                    push(l1, c, s[1]);
                }

                push(out, l0, l1);
            }

            if (state eq 'WON') {
                out[0] = [
                    mp.colors.cursor.attr,
                    L("You won! Press any key.")
                ];

                state = 'RST';
            }

            return out;
        },

        dir: {
            U:  [ 0, -1],
            R:  [ 1,  0],
            D:  [ 0,  1],
            L:  [-1,  0]
        },
        pieces: [
            {
                shape:  ["\x{2502} \x{2514}", "\x{2514}\x{2500}\x{2500}"],
                out:    { D: 'R', L: 'U' },
                next:   1
            },
            {
                shape:  ["\x{250c}\x{2500}\x{2500}", "\x{2502} \x{250c}"],
                out:    { U: 'R', L: 'D' },
                next:   2
            },
            {
                shape:  ["\x{2500}\x{2500}\x{2510}", "\x{2510} \x{2502}"],
                out:    { U: 'L', R: 'D' },
                next:   3
            },
            {
                shape:  ["\x{2518} \x{2502}", "\x{2500}\x{2500}\x{2518}"],
                out:    { R: 'U', D: 'L' },
                next:   0
            },
            {
                shape:  ["\x{2502} \x{2502}", "\x{2502} \x{2502}"],
                out:    { U: 'U', D: 'D' },
                next:   5
            },
            {
                shape:  ["\x{2500}\x{2500}\x{2500}", "\x{2500}\x{2500}\x{2500}"],
                out:    { R: 'R', L: 'L' },
                next:   4
            },
            { /* blank */
                shape:  ["   ", "   "],
                out:    {},
                next:   6
            },
            { /* entry pipe */
                shape:  ["\x{2502} \x{2502}", "\\ /"],
                out:    { D: 'D' },
                next:   7
            },
            { /* out pipe */
                shape:  ["\x{2502} \x{2502}", "/ \\"],
                out:    { D: 'WIN' },
                next:   8
            }
        ]
    }
);
