Gui.App();

Window* win = Compositor.CreateWindow(18, 62, 684, 474, WIN_FLAGS_DEFAULT);
Gui.Window.EnableAlphaChannel(win);
Gui.Window.SetOpacity(win, 224);
Gui.Window.SetIcon(win, Image.FileToContext2D("Icon.png"));
Gui.Window.SetTitle(win, "Terminal");

TerminalWidget* active_term = NULL;
@shell* sh = NULL;

U0 @terminal_keypress_callback(Window* win, I64 key)
{
    if (!active_term || Compositor.active_win != win)
        return;
    I64 i;
    U8 send_key[4];
    MemSetU32(&send_key, 0, 1);
    switch (key) {
    case SC_CURSOR_UP:
        send_key[0] = '\x1b';
        send_key[1] = '[';
        send_key[2] = 'A';
        break;
    case SC_CURSOR_DOWN:
        send_key[0] = '\x1b';
        send_key[1] = '[';
        send_key[2] = 'B';
        break;
    case SC_CURSOR_LEFT:
        send_key[0] = '\x1b';
        send_key[1] = '[';
        send_key[2] = 'D';
        break;
    case SC_CURSOR_RIGHT:
        send_key[0] = '\x1b';
        send_key[1] = '[';
        send_key[2] = 'C';
        break;
    case SC_DELETE:
        send_key[0] = 21;
        break;
    case SC_BACKSPACE:
        send_key[0] = 8;
        break;
    case SC_TAB:
        send_key[0] = 9;
        break;
    case SC_ENTER:
        send_key[0] = 10;
        break;
    case SC_HOME:
        send_key[0] = 22;
        break;
    case SC_END:
        send_key[0] = 23;
        break;
    case SC_PAGE_UP:
        send_key[0] = 24;
        break;
    case SC_PAGE_DOWN:
        send_key[0] = 25;
        break;
    case SC_ESC:
        send_key[0] = 27;
        break;
    case 0x02 ... 0x0D:
    case 0x10 ... 0x1B:
    case 0x1E ... 0x29:
    case 0x2B ... 0x35:
    case 0x39:
        if (!KeyDown(SC_SHIFT)) {
            if (KeyDown(SC_CTRL)) {
                switch (key) {
                case Char2ScanCode('c'):
                    send_key[0] = 0x03;
                    sh->break = TRUE;
                    break;
                default:
                    break;
                }
            } else {
                send_key[0] = NORMAL_KEY_SCAN_DECODE_TABLE(U8*)[key];
            }
        } else {
            if (key == 0x39) // Handle TempleOS SHIFT-SPACE character.
                send_key[0] = ' ';
            else {
                if (KeyDown(SC_CTRL)) {
                    // terminal copy paste handling
                } else {
                    send_key[0] = SHIFT_KEY_SCAN_DECODE_TABLE(U8*)[key];
                }
            }
        };
        break;
    default:
        return;
        break;
    }
    for (i = 0; i < 4; i++)
        if (send_key[i] && active_term->output)
            FifoU8Ins(active_term->output, send_key[i]);
}

TerminalWidget* t = Gui.CreateWidget(win, WIDGET_TYPE_TERMINAL, 0, 0, 120, 120);
t->pointer = Compositor.theme.pointer.text;
VerticalScrollBarWidget* vscroll = Gui.CreateWidget(win, WIDGET_TYPE_VERT_SCROLLBAR, 0, 0, 16, 128);
vscroll->height = 128;
Context2DWidget* status = Gui.CreateWidget(win, WIDGET_TYPE_CONTEXT2D, 0, 0, Display.Width(), 44);
status->ctx = NewContext2D(Display.Width(), 44);
status->ctx->fill(Color(204, 204, 204, win->opacity));

U0 @terminal_create_new_instance()
{
    U32 init_bg_color = t->color.background;
    init_bg_color.u8[3] = win->opacity;
    t->backing_store->fill(init_bg_color);
    active_term = t;
    sh = @shell_new;
    sh->session = &Compositor.session;
    t->output = sh->input;
    sh->output = t->input;
}

U0 window_close(Window* win)
{
    if (sh)
        if (sh->task && sh->exit)
            Free(sh);
    if (win == Compositor.active_win)
        Gui.Window.SetFocus(Compositor.GetWindowByTitle("Wallpaper"));
    Compositor.UnregisterForGlobalInputEvents(win);
    Compositor.DestroyWindow(win);
}

U0 @terminal_vscroll_change(Widget*)
{
    I64 i = 0;
    I64 max_scroll = vscroll->height - 32;
    F64 f1 = (ToF64(max_scroll) / ToF64(t->cursor.y + t->max.y - 2));
    while (vscroll->scroll > ToI64(i * f1))
        i++;
    t->scroll.y = i;
    t->refresh = TRUE;
    Gui.Window.Refresh(win);
}

U0 Main()
{
    Compositor.RegisterForGlobalInputEvents(win);
    Gui.Window.SetCallback(win, "keypress", &@terminal_keypress_callback);
    Gui.Widget.SetCallback(vscroll, "change", &@terminal_vscroll_change);
    I64 prev_width = -1;
    I64 prev_height = -1;
    I64 prev_max_x = -1;
    I64 prev_max_y = -1;
    F64 f1;
    Gui.Window.SetFocus(win);
    Gui.Window.SetCallback(win, "close", &window_close);

    @terminal_create_new_instance;

    while (win) {
        // FIXME: This should be event-driven...
        if (win->width != prev_width || win->height != prev_height) {
            win->width = 3 + RoundI64(win->width, 8);
            win->height = RoundI64(win->height, 16);
            prev_width = win->width;
            prev_height = win->height;
            if (active_term) {
                active_term->width = win->width;
                active_term->height = win->height;
            }
            status->y = win->height - 44;
            goto terminal_update_vscroll;
        }
        if (prev_max_x != t->max.x || prev_max_y != t->max.y) {
        terminal_update_vscroll:
            if (!t->max.y) {
                vscroll->x = Display.Width(); // Hide
            } else {
                vscroll->x = win->width - vscroll->width - 9;
                vscroll->height = win->height - 44;
                f1 = (ToF64(t->size.rows) / ToF64(1 + t->cursor.y + t->max.y));
                vscroll->length = ToI64((vscroll->height - 32) * f1);
                vscroll->scroll = vscroll->height;
            }
            prev_max_x = t->max.x;
            prev_max_y = t->max.y;
            // System.Log(Fs, "vscroll->scroll: %d", vscroll->scroll);

            Gui.Window.Refresh(win);
        }
        if (FifoU8Cnt(active_term->input))
            Gui.Window.Refresh(win);
        if (sh->exit) {
            win->callback.close(win);
            return;
        }
        Sleep(10);
    }
}

Main;