/*
Char graphics are how games on the C64 were made.  You don't need to do it
this way, unless for fun.  You can just make device context bigger than
the screen and scroll around.

See  ::/Demo/Games/RawHide.CC or ::/Demo/Games/BigGuns.CC.

The nice thing about character graphics are the animations.
*/

#define MAP_WIDTH               (TEXT_COLS * 2)
#define MAP_HEIGHT              (TEXT_ROWS * 2)

#define CH_WATER                '^'
#define CH_LAND                 CH_SPACE
#define CH_TREE                 '*'

U16 map[MAP_HEIGHT][MAP_WIDTH];

I64 screen_pix_x, screen_pix_y;

U0 DrawIt(CTask *task, CDC *)
{
        U16 *ptr;
        I64  i, j, y = screen_pix_y >> 3;

        gr.hide_col = gr.hide_row = TRUE;
        gr.pan_text_x = 7 - screen_pix_x & 7;
        gr.pan_text_y = 7 - screen_pix_y & 7;
        for (i = 0; i < task->win_height; i++)
        {
                ptr = &map[y++][screen_pix_x >> 3];
                for (j = 0; j < task->win_width; j++)
                        //By the time you clip to window and handle Fs->scroll_x, Fs->scroll_y,
                        //it is too much trouble to do raw access to gr.text_base like we do
                        //in ::/Demo/Games/Maze.CC.
                        TextChar(task, FALSE, j, i, *ptr++);
        }
}

U8 *old_font = text.font;
U64 waves[4] = {0x0011AA440011AA44, 0x0022558800225588, 0x0044AA110044AA11, 0x0088552200885522};

U0 AnimateEndCB()
{
        text.font = old_font;
        Exit;
}

U0 AnimateTask(I64)
{
        I64 i;
        U64 *font = MAlloc(256 * 8);

        Fs->task_end_cb = &AnimateEndCB;
        MemCopy(font, text.font, 256 * 8);
        text.font = font;
        font[CH_TREE] = 0x18187E7E3C3C18;
        while (TRUE)
        {
                font[CH_WATER] = waves[i++ & 0x3];
                Sleep(200);
                Refresh;
        }
}

U0 ScrollTaskX(I64 sign)
{
        I64 i;

        for (i = 0; i < 32; i++)
        {
                screen_pix_x = ClampI64(screen_pix_x + sign, 0, (MAP_WIDTH - TEXT_COLS + 1) * FONT_WIDTH);
                Refresh;
        }
}

U0 ScrollTaskY(I64 sign)
{
        I64 i;

        for (i = 0; i < 32; i++)
        {
                screen_pix_y = ClampI64(screen_pix_y + sign, 0, (MAP_HEIGHT - TEXT_ROWS + 2) * FONT_HEIGHT);
                Refresh;
        }
}

U0 Init()
{
        I64 i, j, x, y;

        screen_pix_x = (MAP_WIDTH  - TEXT_COLS) >> 1 * FONT_WIDTH;
        screen_pix_y = (MAP_HEIGHT - TEXT_ROWS) >> 1 * FONT_HEIGHT;

        MemSetU16(map, CH_WATER + (BLUE << 4 + LTBLUE) << 8, MAP_WIDTH * MAP_HEIGHT);
        for (i = 1; i < MAP_WIDTH - 1; i++)
                map[0][i] = map[MAP_HEIGHT - 1][i] = '.' + (BLUE << 4 + RED) << 8;
        for (i = 1; i < MAP_HEIGHT - 1; i++)
                map[i][0] = map[i][MAP_WIDTH - 1]  = '.' + (BLUE << 4 + RED) << 8;

        map[0][0]                                                       = '.' + (BLUE << 4 + RED) << 8;
        map[0][MAP_WIDTH - 1]                           = '.' + (BLUE << 4 + RED) << 8;
        map[MAP_HEIGHT - 1][0]                          = '.' + (BLUE << 4 + RED) << 8;
        map[MAP_HEIGHT - 1][MAP_WIDTH - 1]      = '.' + (BLUE << 4 + RED) << 8;

        for (i = 0; i < 20; i++)
        {
                x = MAP_WIDTH  * Rand;
                y = MAP_HEIGHT * Rand;
                for (j = 0; j < 1000; j++)
                {
                        map[y][x] = CH_LAND + (YELLOW << 4 + BLACK) << 8;
                        x = ClampI64(x + RandU16 % 3 - 1, 0, MAP_WIDTH  - 1);
                        y = ClampI64(y + RandU16 % 3 - 1, 0, MAP_HEIGHT - 1);
                }
        }
        for (i = 0; i < 100; i++)
        {
                do
                {
                        x = MAP_WIDTH  * Rand;
                        y = MAP_HEIGHT * Rand;
                }
                while (map[y][x].u8[0] != CH_LAND);

                for (j = 0; j < 100; j++)
                {
                        map[y][x] = CH_TREE + (YELLOW << 4 + GREEN) << 8;
                        x = ClampI64(x + RandU16 % 3 - 1, 0, MAP_WIDTH  - 1);
                        y = ClampI64(y + RandU16 % 3 - 1, 0, MAP_HEIGHT - 1);
                }
        }
}

U0 CleanUp()
{
        gr.pan_text_x = gr.pan_text_y = 0;
        gr.hide_col = gr.hide_row = FALSE;
}

U0 CharDemo()
{
        I64 sc;

        SettingsPush; //See SettingsPush
        Fs->text_attr = YELLOW << 4 + BLUE;
        AutoComplete;
        WinBorder;
        WinMax;
        DocCursor;

        MenuPush(       "File {"
                                "  Abort(,CH_SHIFT_ESC);"
                                "  Exit(,CH_ESC);"
                                "}"
                                "Play {"
                                "  Restart(,'\n');"
                                "  Left(,,SC_CURSOR_LEFT);"
                                "  Right(,,SC_CURSOR_RIGHT);"
                                "  Up(,,SC_CURSOR_UP);"
                                "  Down(,,SC_CURSOR_DOWN);"
                                "}"
                                );
        Init;
        Fs->animate_task = Spawn(&AnimateTask, NULL, "Animate",, Fs);
        Fs->draw_it              = &DrawIt;
        try
        {
                while (TRUE)
                {
                        switch (KeyGet(&sc))
                        {
                                case 0:
                                        switch (sc.u8[0])
                                        {
                                                case SC_CURSOR_LEFT:
                                                        Spawn(&ScrollTaskX, -1, "Scroll",, Fs);
                                                        break;

                                                case SC_CURSOR_RIGHT:
                                                        Spawn(&ScrollTaskX, 1, "Scroll",, Fs);
                                                        break;

                                                case SC_CURSOR_UP:
                                                        Spawn(&ScrollTaskY, -1, "Scroll",, Fs);
                                                        break;

                                                case SC_CURSOR_DOWN:
                                                        Spawn(&ScrollTaskY, 1, "Scroll",, Fs);
                                                        break;
                                        }
                                        break;

                                case '\n':
                                        CleanUp;
                                        Init;
                                        break;

                                case CH_SHIFT_ESC:
                                case CH_ESC:
                                        goto sq_done;
                        }
                }
sq_done: //Don't goto out of try
        }
        catch
                PutExcept;
        SettingsPop;
        CleanUp;
        MenuPop;
}

CharDemo;