#pragma comment (lib, "fltk.lib")

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
#include <cmath>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

#include <FL/Fl.H>
#include <FL/x.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Button.H>
//#include <FL/Fl_Multiline_Input.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Widget.H>
#include <FL/fl_ask.H>

#define CLR(a) memset(a, 0, sizeof(a))

void __never(int a);
#define ass(s) {if (!(s)) {__never(__LINE__);cout.flush();cerr.flush();abort();}}

struct ZLevel
{
	int x, y;
	string lvl;
	string hint;
};

ZLevel levels[] = {
	{	3, 5,
		"####."
		"@  $ "
		"###  ",
		"Have you ever played Sokoban?\n"
		"Use R, L, U and D to write the program.\n"
		"When you are ready, click 'Compile' and then 'Run'." },
	{	1, 6,
		".@$   ",
		"Seems impossible? Jump back in time!\n"
		"Use 1-9 to jump back for corresponding number of seconds.\n"
		"You can also use N to wait for a second." },
	{	3, 4,
		"###."
		"@ $ "
		"### ",
		"R, L, U, D, N and 1-9 are all available commands.\n"
		"They are highlighted by blue, all other symbols will be ignored.\n"
		"Also, all symbols after '//' on the line will be ignored too." },
	{	4, 6,
		"#.   @"
		"#### #"
		"   $ #"
		"#### #",
		"Let's try a bit harder. You should move the box\n"
		"in one direction before you move it in another one.\n"
		"If you understand what I mean." },
	{	4, 6,
		"### ##"
		"###.$ "
		".@ $$ "
		"###. #",
		"More boxes make the game more enjoyable.\n"
		"So, enjoy it!\n"
		"If you can." },
	{	5, 6,
		"## ###"
		" $ ##."
		"## @ ."
		"# $ # "
		"### ##",
		"Just.\n"
		"Do.\n"
		"It." },
	/*{	4, 7,
		"###.###"
		"###7###"
		"###$###"
		".7$@$7.",
		"Oh no, several tiles will be eliminated in 7 seconds!\n"
		"Jump carefully to deliver all boxes in time.\n"
		"Anything on such tiles in eliminating moment leads to crash." }*/
};

class Fl_LevelWidget : public Fl_Widget
{
	int _x, _y;
	string lvl;
	string app;
	int t;
	void draw()
	{
		fl_push_clip(x(),y(),w(),h());
		fl_draw_box( FL_BORDER_BOX, x(), y(), w(), h(), fl_rgb_color( 64, 64, 64 ) );
		fl_pop_clip();

		fl_push_clip(x()+1,y()+1,w()-2,h()-2);

		int xx = x() + w()/2 - _y*18;
		int yy = y() + h()/2 - _x*18;

		for (int i=0; i<_x; i++)
			for (int j=0; j<_y; j++)
			{
				char c = lvl[i*_y + j];
				if (c!='#') fl_draw_box( FL_BORDER_BOX, xx+j*36+1, yy+i*36+1, 34, 34, FL_WHITE );
				if (c=='.')
				{
					fl_color( fl_rgb_color( 128, 128, 128 ) );
					fl_pie( xx+j*36+10, yy+i*36+10, 16, 16, 0., 360. );
				}
			}

		for (int i=0; i<_x; i++)
			for (int j=0; j<_y; j++)
			{
				char c = lvl[i*_y + j];
				if (c=='$' || c=='*')
				{
					fl_draw_box( FL_BORDER_BOX, xx+j*36+4, yy+i*36+4, 28, 28, c=='$' ? fl_rgb_color( 64, 64, 192 ) : fl_rgb_color( 192, 192, 64 ) );
					fl_color( FL_BLACK );
					fl_line( xx+j*36+4, yy+i*36+4, xx+j*36+4+27, yy+i*36+4+27 );
					fl_line( xx+j*36+4, yy+i*36+4+27, xx+j*36+4+27, yy+i*36+4 );
				}
				if (c=='@' || c=='!' || ('a'<=c && c<='z') || ('A'<=c && c<='Z'))
				{
					fl_color( FL_RED );
					fl_pie( xx+j*36+4, yy+i*36+4, 28, 28, 0., 360. );
					fl_color( FL_BLACK );
					fl_arc( xx+j*36+4, yy+i*36+4, 28, 28, 0., 360. );
				}
			}

		fl_pop_clip();
	}
public:
	Fl_LevelWidget(int x, int y, int w, int h, const char *label=0):Fl_Widget(x, y, w, h, label)
	{
		_x = _y = 0;
		lvl = "";
		app = "";
		t = 0;
	}
	virtual ~Fl_LevelWidget()
	{
	}
	void set_level( int X, int Y, string L, string a, int T )
	{
		_x = X;
		_y = Y;
		lvl = L;
		t = T;
		app = a;
	}
};

struct POS
{
	int x, y, z;
	POS( int _x=0, int _y=0, int _z=0 )
	{
		x = _x;
		y = _y;
		z = _z;
	}
};

#define FONT_SIZE 16
Fl_Text_Buffer * programs[20];
Fl_Text_Buffer * buf = 0;
Fl_Text_Buffer * stylebuf = 0;
Fl_Text_Buffer * consbuf = 0;
Fl_Text_Display::Style_Table_Entry styletable[] = {	// Style table
	{ FL_BLACK,      FL_COURIER,           FONT_SIZE }, // A - Plain
	{ FL_DARK_GREEN, FL_COURIER,           FONT_SIZE }, // B - Line comments
	{ FL_BLUE,       FL_COURIER_BOLD,      FONT_SIZE }, // C - Code
	{ FL_RED,        FL_COURIER_BOLD,      FONT_SIZE }, // D - Running commands
};

int cur_level = 0;
Fl_Window * wnd;
Fl_Choice * levs;
Fl_LevelWidget * lvl;
Fl_Text_Editor * inp;
Fl_Box * hint;

Fl_Button * b_comp, * b_run, * b_begin, * b_prev, * b_next;
Fl_Box * step;
char step_ch[20];
Fl_Text_Display * cons;

Fl_Scroll * scr;

string prog;
vector< int > inds;
vector< POS > pos;
vector< string > states;
vector< string > appear;
bool comp_err = false;
bool path_found = false;
int err_level = -1;

int cur_step = 0;

bool run_mode = false;

/*Fl_Menu_Item level_menu[] = {
  {"Red",	FL_ALT+'r'},
  {"Green",	FL_ALT+'g'},
  {"Blue",	FL_ALT+'b'},
  {"Strange",	FL_ALT+'s', 0, 0, FL_MENU_INACTIVE},
  {"&Charm",	FL_ALT+'c'},
  {"Truth",	FL_ALT+'t'},
  {"Beauty",	FL_ALT+'b'},
  {0}
};*/

class Fl_DebugWidget : public Fl_Widget
{
	int dx1, dy1, dx2, dy2;
	int dddy;
	void draw()
	{
		fl_push_clip(x(),y(),w(),h());
		fl_draw_box( FL_BORDER_BOX, x(), y(), w(), h(), FL_WHITE );
		fl_pop_clip();

		fl_push_clip(x()+1,y()+1,w()-2,h()-2);

		int xx = levels[cur_level].x;
		int yy = levels[cur_level].y;
		for (int z=(int)states.size()-1; z>=0; z--)
		{
			char ch[10];
			sprintf( ch, "%d", z );
			fl_color( FL_BLACK );
			fl_draw( ch, x() + 10, y() + 15 + dddy*z );
			if (z==cur_step)
			{
				int cx = x() + 5;
				int cy = y() + 2 + dddy*z;
				fl_line( cx, cy, cx+w()-10, cy, cx+w()-10, cy+dddy-10 );
				fl_line( cx, cy, cx, cy+dddy-10, cx+w()-10, cy+dddy-10 );
			}
			if (err_level == z)
			{
				int cx = x() + 10;
				int cy = y() + 5 + dddy/2 + dddy*z;
				fl_draw_box( FL_BORDER_BOX, cx-5, cy-5, 10, 10, FL_RED );
			}
			for (int i=0; i<xx; i++)
				for (int j=0; j<yy; j++)
					if (states[z][i*yy+j]!='#')
					{
						int cx = x() + w()/2 - ((yy-1)*dx1 + (xx-1)*dx2)/2 + dx1*j + dx2*i;
						int cy = y() + 5 + dddy/2 + dddy*z - ((xx-1)*dy2 + (yy-1)*dy1)/2 + dy1*j + dy2*i;

						//fl_draw_box( FL_BORDER_BOX, cx-5, cy-5, 10, 10, FL_WHITE );
						fl_color( FL_BLACK );
						double k = 0.45;
						fl_line( cx + (int)((dx1+dx2)*k), cy + (int)((dy1+dy2)*k),
							cx + (int)((dx1-dx2)*k), cy + (int)((dy1-dy2)*k) );
						fl_line( cx + (int)((dx1-dx2)*k), cy + (int)((dy1-dy2)*k),
							cx + (int)((-dx1-dx2)*k), cy + (int)((-dy1-dy2)*k) );
						fl_line( cx + (int)((-dx1-dx2)*k), cy + (int)((-dy1-dy2)*k),
							cx + (int)((-dx1+dx2)*k), cy + (int)((-dy1+dy2)*k) );
						fl_line( cx + (int)((-dx1+dx2)*k), cy + (int)((-dy1+dy2)*k),
							cx + (int)((dx1+dx2)*k), cy + (int)((dy1+dy2)*k) );

						if (states[z][i*yy+j]=='.' || states[z][i*yy+j]=='!' ||
							('A'<=states[z][i*yy+j] && states[z][i*yy+j]<='Z'))
						{
							//cout << states[z] << "\n";
							fl_draw_box( FL_BORDER_BOX, cx-3, cy-3, 6, 6, FL_WHITE );
						}
					}

			for (int i=0; i<xx; i++)
				for (int j=0; j<yy; j++)
				{
					int cx = x() + w()/2 - ((yy-1)*dx1 + (xx-1)*dx2)/2 + dx1*j + dx2*i;
					int cy = y() + 5 + dddy/2 + dddy*z - ((xx-1)*dy2 + (yy-1)*dy1)/2 + dy1*j + dy2*i;

					if (states[z][i*yy+j]=='$' || states[z][i*yy+j]=='*')
					{
						fl_color( states[z][i*yy+j]=='$' ? fl_rgb_color( 64, 64, 192 ) : fl_rgb_color( 192, 192, 64 ) );
						double k = 0.3;
						fl_polygon(
							cx + (int)((dx1+dx2)*k), cy + (int)((dy1+dy2)*k),
							cx + (int)((dx1-dx2)*k), cy + (int)((dy1-dy2)*k),
							cx + (int)((-dx1-dx2)*k), cy + (int)((-dy1-dy2)*k),
							cx + (int)((-dx1+dx2)*k), cy + (int)((-dy1+dy2)*k) );
						fl_color( FL_BLACK );
						fl_line( cx + (int)((dx1+dx2)*k), cy + (int)((dy1+dy2)*k),
							cx + (int)((dx1-dx2)*k), cy + (int)((dy1-dy2)*k) );
						fl_line( cx + (int)((dx1-dx2)*k), cy + (int)((dy1-dy2)*k),
							cx + (int)((-dx1-dx2)*k), cy + (int)((-dy1-dy2)*k) );
						fl_line( cx + (int)((-dx1-dx2)*k), cy + (int)((-dy1-dy2)*k),
							cx + (int)((-dx1+dx2)*k), cy + (int)((-dy1+dy2)*k) );
						fl_line( cx + (int)((-dx1+dx2)*k), cy + (int)((-dy1+dy2)*k),
							cx + (int)((dx1+dx2)*k), cy + (int)((dy1+dy2)*k) );
					}
				}

			for (int i=0; i<(int)pos.size(); i++)
				if (pos[i].z == z)
				{
					int cx = x() + w()/2 - ((yy-1)*dx1 + (xx-1)*dx2)/2 + dx1*pos[i].y + dx2*pos[i].x;
					int cy = y() + 5 + dddy/2 + dddy*z - ((xx-1)*dy2 + (yy-1)*dy1)/2 + dy1*pos[i].y + dy2*pos[i].x;

					fl_color( FL_BLUE );
					if (comp_err && !path_found && i==(int)pos.size()-1) fl_color( FL_RED );
					if (comp_err && path_found && z==err_level) fl_color( FL_RED );
					if (i >= (int)inds.size()) fl_color( fl_rgb_color( 96, 96, 96 ) );
					fl_pie( cx-7, cy-7, 14, 14, 0., 360. );

					if (i>0 && pos[i-1].z == z-1)
					{
						int cx2 = x() + w()/2 - ((yy-1)*dx1 + (xx-1)*dx2)/2 + dx1*pos[i-1].y + dx2*pos[i-1].x;
						int cy2 = y() + 5 + dddy/2 + dddy*(z-1) - ((xx-1)*dy2 + (yy-1)*dy1)/2 + dy1*pos[i-1].y + dy2*pos[i-1].x;

						fl_color( FL_BLUE );
						fl_line( cx, cy, cx2, cy2 );
					}

					if (i<(int)pos.size()-1 && pos[i+1].z < z)
					{
						fl_color( FL_BLUE );
						fl_line( cx+2, cy, cx+2, cy-dddy*(z-pos[i+1].z) );
					}
				}
		}
		fl_color( FL_BLACK );
		fl_pop_clip();
	}
public:
	Fl_DebugWidget(int x, int y, int w, int h, const char *label=0):Fl_Widget(x, y, w, h, label)
	{
		dx1 = 50; dy1 = -10;
		dx2 = 4; dy2 = 16;
		dddy = 0;
	}

	void reset_all()
	{
		int xx = levels[cur_level].x;
		int yy = levels[cur_level].y;
		dddy = xx*dy2 - yy*dy1;
		resize( scr->x()+2, scr->y()+2, 380, (int)states.size()*dddy+10 );
		//redraw();
		wnd->redraw();
	}
};

Fl_DebugWidget * deb = 0;

void style_parse(const char * text, char * style, int length)
{
	bool flag = false; // comment
	for (int i=0; i<length; i++)
	{
		if (text[i] == '/' && text[i+1] == '/') flag = true;
		if (text[i] == '\n') flag = false;
		if (flag)
			style[i] = 'B';
		//else if (text[i] == 'R')
		//	style[i] = 'D';
		else if (text[i] == 'L' || text[i] == 'R' || text[i] == 'U' || text[i] == 'D' ||
					text[i] == 'N' || ( '1' <= text[i] && text[i] <= '9' ))
			style[i] = 'C';
		else style[i] = 'A';
	}
}

void style_init()
{
	char * style = new char[buf->length() + 1];
	char * text = buf->text();
	
	memset(style, 'A', buf->length());
	style[buf->length()] = '\0';

	if (!stylebuf) stylebuf = new Fl_Text_Buffer(buf->length());
	style_parse(text, style, buf->length());
	
	stylebuf->text(style);
	delete[] style;
	free(text);
}

void style_update(
	int pos,						// I - Position of update
	int nInserted,					// I - Number of inserted chars
	int nDeleted,					// I - Number of deleted chars
	int /*nRestyled*/,				// I - Number of restyled chars
	const char * /*deletedText*/,	// I - Text that was deleted
	void * cbArg)					// I - Callback data
{
	style_init();
}

void style_unfinished_cb(int, void*) {}

void select_level_cb(int, void * v)
{
	int val = (int)v;
	programs[cur_level]->text( buf->text() );
	cur_level = val;
	lvl->set_level( levels[val].x, levels[val].y, levels[val].lvl, levels[val].lvl, 0 );
	lvl->redraw();
	buf->text( programs[val]->text() );
	style_init();
	hint->label( levels[val].hint.c_str() );

	b_comp->activate();
	b_run->deactivate();
	b_begin->deactivate();
	b_prev->deactivate();
	b_next->deactivate();
	step->label( "0/0" );

	comp_err = false;
	path_found = false;
	err_level = -1;

	prog = "";
	inds.clear();
	pos.clear();
	states.clear();

	consbuf->text( "Level is loaded." );

	deb->reset_all();

	wnd->redraw();
}

void comp_error( int ind, char * mess )
{
	consbuf->append( " fail" );
	consbuf->append( mess );
	comp_err = true;

	if (!path_found)
	{
		if (ind!=-1 && ind < (int)inds.size())
		{
			char * style = stylebuf->text();
			style[ inds[ ind ] ] = 'D';
			stylebuf->text( style );
			free(style);
		}
	}
	else
	{
		err_level = ind;
	}
}

void compile_cb(Fl_Widget * w, void * v)
{
	b_comp->deactivate();
	b_run->deactivate();
	b_begin->deactivate();
	b_prev->deactivate();
	b_next->deactivate();
	inp->deactivate();
	comp_err = false;
	path_found = false;
	err_level = -1;

	consbuf->text( "" );
	consbuf->append( "SokoJump compiler v3.1415\n" );
	consbuf->append( "Ripatti Software (c) 2016\n" );
	consbuf->append( "Compiling..." );

	int x = levels[cur_level].x;
	int y = levels[cur_level].y;
	string L = levels[cur_level].lvl;

	prog = "";
	inds.clear();
	pos.clear();
	states.clear();

	//consbuf->append( "\nParsing..." );
	{
		char * pr = buf->text();
		int sz = buf->length();
		bool flag = true;
		int deep = 0, mx_deep = 0;
		for (int i=0; i<sz; i++)
		{
			if (pr[i]=='/' && pr[i+1]=='/') flag = false;
			if (pr[i]=='\n') flag = true;
			if (flag)
				if (pr[i] == 'L' || pr[i] == 'R' || pr[i] == 'U' || pr[i] == 'D' ||
						pr[i] == 'N' || ( '1' <= pr[i] && pr[i] <= '9' ))
				{
					prog.push_back( pr[i] );
					inds.push_back( i );
					if ('1' <= pr[i] && pr[i] <= '9') deep -= (pr[i]-'0');
					else deep++;
					mx_deep = max( deep, mx_deep );
				}
		}
		free(pr);
		while (deep <= mx_deep)
		{
			deep++;
			prog.push_back( 'N' );
		}
		//cout << prog << "\n";
	}
	//consbuf->append( " ok" );

	consbuf->append( "\nPath building..." );
	{
		string start = L;
		//cout << start << "\n";
		int cx = -1, cy = -1, cz = 0;
		for (int i=0; i<(int)start.size(); i++)
			if (start[i]!='#')
			{
				if (start[i]=='@' || start[i]=='!')
				{
					if (cx!=-1)
					{
						comp_error( -1, "\n Fatal error: more than one unit on the field." );
						goto end;
					}
					cx = i/y;
					cy = i%y;
				}
				if (start[i]=='!' || start[i]=='.') start[i]='.';
				else start[i]=' ';
			}
		//cout << start << "\n";
		if (cx==-1)
		{
			comp_error( -1, "\n Fatal error: no unit on the field." );
			goto end;
		}
		
		pos.push_back( POS( cx, cy, cz ) );
		states.push_back( start );
		appear.push_back( start );

		for (int z=0; z<(int)prog.size(); z++)
		{
			bool jump = false;
			if (prog[z]=='L')
			{
				if (states[cz][cx*y+cy]==' ')
					states[cz][cx*y+cy] = 'l';
				else if (states[cz][cx*y+cy]=='.')
					states[cz][cx*y+cy] = 'L';
				else
				{
					comp_error( z, "\n Error: unknown error." );
					goto end;
				}
				cy--;
				cz++;
			}
			else if (prog[z]=='R')
			{
				if (states[cz][cx*y+cy]==' ')
					states[cz][cx*y+cy] = 'r';
				else if (states[cz][cx*y+cy]=='.')
					states[cz][cx*y+cy] = 'R';
				else
				{
					comp_error( z, "\n Error: unknown error." );
					goto end;
				}
				cy++;
				cz++;
			}
			else if (prog[z]=='U')
			{
				if (states[cz][cx*y+cy]==' ')
					states[cz][cx*y+cy] = 'u';
				else if (states[cz][cx*y+cy]=='.')
					states[cz][cx*y+cy] = 'U';
				else
				{
					comp_error( z, "\n Error: unknown error." );
					goto end;
				}
				cx--;
				cz++;
			}
			else if (prog[z]=='D')
			{
				if (states[cz][cx*y+cy]==' ')
					states[cz][cx*y+cy] = 'd';
				else if (states[cz][cx*y+cy]=='.')
					states[cz][cx*y+cy] = 'D';
				else
				{
					comp_error( z, "\n Error: unknown error." );
					goto end;
				}
				cx++;
				cz++;
			}
			else if (prog[z]=='N')
			{
				if (states[cz][cx*y+cy]==' ')
					states[cz][cx*y+cy] = 'n';
				else if (states[cz][cx*y+cy]=='.')
					states[cz][cx*y+cy] = 'N';
				else
				{
					comp_error( z, "\n Error: unknown error." );
					goto end;
				}
				cz++;
			}
			else // 1-9
			{
				if (states[cz][cx*y+cy]==' ')
					states[cz][cx*y+cy] = 'v';
				else if (states[cz][cx*y+cy]=='.')
					states[cz][cx*y+cy] = 'V';
				else
				{
					comp_error( z, "\n Error: unknown error." );
					goto end;
				}
				cz-=(prog[z]-'0');
				if (cz<1)
				{
					comp_error( z, "\n Error: cannot jump into time <= 0." );
					goto end;
				}
				jump = true;
			}

			if (cx<0 || cx>=x || cy<0 || cy>=y || start[cx*y+cy]=='#')
			{
				if (jump) comp_error( z, "\n Error: unit jumped into the wall." );
				else comp_error( z, "\n Error: unit bumped into the wall." );
				goto end;
			}

			if (cz < (int)states.size())
				if (states[cz][cx*y+cy]=='l' || states[cz][cx*y+cy]=='L' ||
					states[cz][cx*y+cy]=='r' || states[cz][cx*y+cy]=='R' ||
					states[cz][cx*y+cy]=='u' || states[cz][cx*y+cy]=='U' ||
					states[cz][cx*y+cy]=='d' || states[cz][cx*y+cy]=='D' ||
					states[cz][cx*y+cy]=='v' || states[cz][cx*y+cy]=='V')
				{
					comp_error( z, "\n Error: self intersection of the path." );
					goto end;
				}

			if (!jump)
				if (	(prog[z]=='L' && (states[cz-1][cx*y+cy]=='r' || states[cz-1][cx*y+cy]=='R')) ||
						(prog[z]=='R' && (states[cz-1][cx*y+cy]=='l' || states[cz-1][cx*y+cy]=='L')) ||
						(prog[z]=='U' && (states[cz-1][cx*y+cy]=='d' || states[cz-1][cx*y+cy]=='D')) ||
						(prog[z]=='D' && (states[cz-1][cx*y+cy]=='u' || states[cz-1][cx*y+cy]=='U')) )
				{
					comp_error( z, "\n Error: self intersection of the path." );
					goto end;
				}

			if (jump)
			{
				appear[cz-1][cx*y+cy] = '^';
			}

			pos.push_back( POS( cx, cy, cz ) );
			if (cz == (int)states.size())
			{
				states.push_back( start );
				appear.push_back( start );
			}
		}
	}
	path_found = true;
	consbuf->append( " ok" );

	consbuf->append( "\nBoxes moving..." );
	{
		for (int i=0; i<x; i++)
			for (int j=0; j<y; j++)
				if (L[i*y+j]=='$' || L[i*y+j]=='*')
					states[0][i*y+j] = L[i*y+j];
		for (int z=0; z<(int)states.size()-1; z++)
		{
			string s = "udlr";
			string S = "UDLR";
			int dx[] = { -1, 1, 0, 0 };
			int dy[] = { 0, 0, -1, 1 };
			for (int i=0; i<x; i++)
				for (int j=0; j<y; j++)
					if (states[z][i*y+j] == '$' || states[z][i*y+j] == '*')
					{
						int cnt = 0, last = -1;
						for (int t=0; t<4; t++)
						{
							int xx = i-dx[t], yy = j-dy[t];
							if (0<=xx && xx<x && 0<=yy && yy<y)
								if (states[z][xx*y+yy]==s[t] || states[z][xx*y+yy]==S[t])
								{
									cnt++;
									last = t;
								}
						}
						if (cnt>1)
						{
							comp_error( z, "\n Error: more than one unit move a box." );
							goto end;
						}
						if (cnt<=1)
						{
							int xx = i, yy = j;
							if (cnt==1)
							{
								xx = i+dx[last];
								yy = j+dy[last];
							}
							//cout << "cnt=" << cnt << " last=" << last << " xx=" << xx << " yy=" << yy << "\n";
							if ( !(0<=xx && xx<x && 0<=yy && yy<y) || states[z+1][xx*y + yy]=='#' )
							{
								comp_error( z, "\n Error: moving a box into the wall." );
								goto end;
							}

							if ( states[z+1][xx*y + yy]=='$' || states[z+1][xx*y + yy]=='*' )
							{
								comp_error( z, "\n Error: collision of boxes." );
								goto end;
							}

							if ( !(states[z+1][xx*y + yy]=='.' || states[z+1][xx*y + yy]==' ') )
							{
								comp_error( z, "\n Error: collision of a box and a unit." );
								goto end;
							}

							if (states[z+1][xx*y + yy]=='.') states[z+1][xx*y + yy] = '*';
							else states[z+1][xx*y + yy] = '$';
						}
					}
		}
	}
	consbuf->append( " ok" );

	consbuf->append( "\nThe program is successfully compiled." );

	consbuf->append( "\nFinal check..." );
	{
		bool flag = true;
		for (int i=0; i<x; i++)
			for (int j=0; j<y; j++)
			{
				char c = states[(int)states.size()-1][i*y+j];
				if (c=='.' || ('A'<=c && c<='Z'))
					flag = false;
			}
		if (flag)
		{
			consbuf->append( " ok" );
			//fl_message( "Congratulations!\nLevel complete!\nThe next level is unlocked." );
		}
		else consbuf->append( " fail" );
	}

end:;
	sprintf( step_ch, "0/%d", (int)states.size()-1 );
	step->label( step_ch );
	cur_step = 0;
	lvl->set_level( levels[cur_level].x, levels[cur_level].y, states[cur_step], appear[cur_step], 0 );

	deb->reset_all();
	b_comp->activate();
	b_run->activate();
	b_begin->activate();
	b_prev->activate();
	b_next->activate();
	inp->activate();
}

void reset_step()
{
	sprintf( step_ch, "%d/%d", cur_step, (int)states.size()-1 );
	step->label( step_ch );

	lvl->set_level( levels[cur_level].x, levels[cur_level].y, states[cur_step], appear[cur_step], 0 );

	wnd->redraw();
}

void run_cb(Fl_Widget * w, void * v);

void timeout_cb( void * v )
{
	run_cb( 0, v );
}

void run_cb(Fl_Widget * w, void * v)
{
	int val = (int)v;
	if (run_mode)
	{
		if (val == 0) // click
		{
			b_run->label( "Run" );
			run_mode = false;

			levs->activate();
			b_comp->activate();
			b_begin->activate();
			b_prev->activate();
			b_next->activate();
			inp->activate();
		}
		else // by timer
		{
			if (cur_step < (int)states.size()-1)
			{
				cur_step++;
				reset_step();
				Fl::repeat_timeout(0.5, timeout_cb, (void *)1);
			}
			else run_cb( w, 0 );			
		}
	}
	else
	{
		if (val==1) return;

		b_run->label( "Stop" );
		run_mode = true;
		levs->deactivate();
		b_comp->deactivate();
		b_begin->deactivate();
		b_prev->deactivate();
		b_next->deactivate();
		inp->deactivate();

		Fl::repeat_timeout(0.5, timeout_cb, (void *)1);
	}
}

void begin_cb(Fl_Widget * w, void * v)
{
	cur_step = 0;
	reset_step();
}

void prev_cb(Fl_Widget * w, void * v)
{
	if (cur_step > 0)
	{
		cur_step--;
		reset_step();
	}
}

void next_cb(Fl_Widget * w, void * v)
{
	if (cur_step < (int)states.size()-1)
	{
		cur_step++;
		reset_step();
	}
}

int main(int argc, char **argv)
{
	//Fl::scheme("gtk+");

	int scr_x = GetSystemMetrics(SM_CXSCREEN);
	int scr_y = GetSystemMetrics(SM_CYSCREEN);
	int sx = 800, sy = 600;
	wnd = new Fl_Double_Window( (scr_x-sx)/2, (scr_y-sy)/2, sx, sy, "SokoJump v0.1 by ripatti [IGDC-134] [follow me on twitter @ripattisoftware]" );

	//Fl_Box * lab1 = new Fl_Box( 0, 0, sx/2, 20, "Level 1" );
	levs = new Fl_Choice( sx/4, 0, sx/8, 20, "Select level: " );
	for (int a=0; a<6; a++)
	{
		char * ch = new char[20];
		sprintf( ch, "Level %d", a+1 );
		levs->add( ch, 0, (Fl_Callback *)select_level_cb, (void *)a, 0 );
	}
	//levs->add( "Level 1", 0, (Fl_Callback *)select_level_cb, (void *)1, 0 );
	//levs->add( "Level 2", 0, (Fl_Callback *)select_level_cb, (void *)2, FL_MENU_INACTIVE );
	levs->value( 0 );
	lvl = new Fl_LevelWidget( 0, 20, sx/2, 200, "Level" );
	//lvl->set_level( levels[0].x, levels[0].y, levels[0].lvl, 0 );

	b_comp = new Fl_Button( 2, 222, sx/10-4, 30, "Compile" );
	b_run = new Fl_Button( sx/10+2, 222, sx/10-4, 30, "Run" );
	b_begin = new Fl_Button( (sx/10)*2+2, 222, 30, 30, "@<<" );
	b_prev = new Fl_Button( (sx/10)*2+2+34, 222, 30, 30, "@<" );
	b_next = new Fl_Button( sx/2-32, 222, 30, 30, "@>" );
	step = new Fl_Box( b_prev->x() + b_prev->w() + 2, 222, b_next->x() - (b_prev->x() + b_prev->w()) - 4, 30, "0/0" );
	step->box( FL_BORDER_BOX );
	b_comp->callback( compile_cb, 0 );
	b_run->callback( run_cb, 0 );
	b_begin->callback( begin_cb, 0 );
	b_prev->callback( prev_cb, 0 );
	b_next->callback( next_cb, 0 );

	for (int i=0; i<20; i++)
		programs[i] = new Fl_Text_Buffer();
	buf = new Fl_Text_Buffer();
	inp = new Fl_Text_Editor( 0, 270, sx/2, 100, "Program" );

	inp->textfont(FL_COURIER);
	inp->textsize(16);
	inp->buffer(buf);
	buf->add_modify_callback(style_update, inp);
	buf->call_modify_callbacks();

	buf->text( "" );
	style_init();

	inp->highlight_data(stylebuf, styletable, sizeof(styletable) / sizeof(styletable[0]), 'A', style_unfinished_cb, 0);
	inp->cursor_style( 3 );

	//inp->deactivate();

	Fl_Box * lab2 = new Fl_Box( 0, 370, sx/2, 20, "Hint" );
	hint = new Fl_Box( 0, 390, sx/2, 56, "" );
	hint->box( Fl_Boxtype::FL_BORDER_BOX );

	consbuf = new Fl_Text_Buffer();
	cons = new Fl_Text_Display( 0, 466, sx/2, 134, "Console" );
	cons->textfont(FL_COURIER_BOLD);
	cons->textsize(12);
	cons->buffer(consbuf);
	consbuf->text( "Level is loaded." );

	scr = new Fl_Scroll( sx/2, 20, sx/2, 580, "Debug" );
	scr->box( FL_DOWN_FRAME );
	deb = new Fl_DebugWidget( scr->x()+2, scr->y()+2, 10, 10, "Deb" );
	scr->end();

	wnd->show(argc, argv);

	select_level_cb( 0, 0 );

	Fl::run();

	return 0;
}