module patch.keyboardselect;

import st;
import x;
import config;
import patches;
import core.stdc.stdio;
import core.stdc.stdlib;
import core.stdc.string;
import deimos.X11.keysym;
import deimos.X11.X : KeySym, CurrentTime;

static if (isPatchEnabled!"KEYBOARDSELECT_PATCH") {
    __gshared {
        static int selectsearch_mode = 0;
        static TCursor cursor_state;
        static Glyph* saved_line = null;
        static int saved_col = 0;
        static int saved_bot = 0;
    }

    void set_notifmode(int type, KeySym ksym) {
        static immutable char*[2] lib = [" MOVE ".ptr, " SEL  ".ptr];
        static Glyph* g = null;
        static int col, bot;

        if (ksym == cast(KeySym)(-1)) {
            if (g) free(g);
            col = term.col;
            bot = term.bot;
            g = cast(Glyph*)xmalloc(col * Glyph.sizeof);
            memcpy(g, term.line[bot], col * Glyph.sizeof);
        } else if (ksym == cast(KeySym)(-2)) {
            if (g) memcpy(term.line[bot], g, col * Glyph.sizeof);
        }

        if (type < 2) {
            const(char)* z = lib[type];
            for (int i = col - 6; i < col && *z; i++, z++) {
                term.line[bot][i].mode = GlyphAttribute.REVERSE;
                term.line[bot][i].u = *z;
                term.line[bot][i].fg = config.defaultfg;
                term.line[bot][i].bg = config.defaultbg;
            }
        } else if (type < 5) {
            if (g) memcpy(term.line[bot], g, col * Glyph.sizeof);
        } else {
            for (int i = 0; i < col; i++) {
                term.line[bot][i].mode = GlyphAttribute.REVERSE;
                term.line[bot][i].u = ' ';
                term.line[bot][i].fg = config.defaultfg;
                term.line[bot][i].bg = config.defaultbg;
            }
            term.line[bot][0].u = cast(Rune)ksym;
        }

        term.dirty[bot] = 1;
        drawregion(0, bot, col, bot + 1);
    }

    Glyph getglyph(int y, int x) {
        static if (isPatchEnabled!"SCROLLBACK_PATCH") {
            int realy = y - term.scr;
            if (realy >= 0) {
                return term.line[realy][x];
            } else {
                realy = term.histi - term.scr + y + 1;
                return term.hist[realy][x];
            }
        } else {
            return term.line[y][x];
        }
    }

    void select_or_drawcursor(int selectsearch_mode, int type) {
        int done = 0;

        if (selectsearch_mode & 1) {
            selextend(term.c.x, term.c.y, type, done);
            setsel(getsel(), CurrentTime);
        } else {
            xdrawcursor(term.c.x, term.c.y, getglyph(term.c.y, term.c.x),
                       term.ocx, term.ocy, getglyph(term.ocy, term.ocx));
        }
    }

    extern(C) void keyboard_select(const(Arg)* dummy) {
        selectsearch_mode ^= 1;
        set_notifmode(selectsearch_mode, cast(KeySym)(-1));
        if (selectsearch_mode == 0) {
            set_notifmode(4, cast(KeySym)(-2));
        }
        select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
    }

    int trt_kbdselect(KeySym ksym, char* buf, int len) {
        static TCursor cu;
        int i, bound;
        
        if (selectsearch_mode == 0) return 0;

        switch (ksym) {
        case XK_Escape:
            selectsearch_mode = 0;
            set_notifmode(4, cast(KeySym)(-2));
            return 0;

        case XK_Return:
            selectsearch_mode ^= 1;
            set_notifmode(selectsearch_mode, cast(KeySym)(-1));
            select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            return 0;

        case XK_h:
        case XK_Left:
            if (term.c.x > 0) {
                term.c.x--;
                select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            }
            return 0;

        case XK_l:
        case XK_Right:
            if (term.c.x < term.col - 1) {
                term.c.x++;
                select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            }
            return 0;

        case XK_j:
        case XK_Down:
            if (term.c.y < term.bot) {
                term.c.y++;
                select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            }
            return 0;

        case XK_k:
        case XK_Up:
            if (term.c.y > term.top) {
                term.c.y--;
                select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            }
            return 0;

        case XK_w:
            // Word forward
            while (term.c.x < term.col - 1 && 
                   getglyph(term.c.y, term.c.x).u == ' ') {
                term.c.x++;
            }
            while (term.c.x < term.col - 1 && 
                   getglyph(term.c.y, term.c.x).u != ' ') {
                term.c.x++;
            }
            select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            return 0;

        case XK_b:
            // Word backward
            while (term.c.x > 0 && 
                   getglyph(term.c.y, term.c.x).u == ' ') {
                term.c.x--;
            }
            while (term.c.x > 0 && 
                   getglyph(term.c.y, term.c.x).u != ' ') {
                term.c.x--;
            }
            select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            return 0;

        case XK_0:
        case XK_Home:
            term.c.x = 0;
            select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            return 0;

        case XK_dollar:
        case XK_End:
            term.c.x = term.col - 1;
            select_or_drawcursor(selectsearch_mode, SelType.REGULAR);
            return 0;

        default:
            return 0;
        }
    }

    void toggle_winmode(int flag) {
        term.mode ^= flag;
    }
}