module patch.openurlonclick;

import core.stdc.string : strchr, strncmp;
import core.sys.posix.unistd : pid_t;
import std.algorithm : min, max;
import std.string : toStringz, fromStringz;

import patches : isPatchEnabled;
import st : term, Rune, Line, ATTR_WRAP;
import st : TLINE;

// Define ATTR_SET if not already defined
enum ATTR_SET = 1 << 15;
enum MODE_ALTSCREEN = 1 << 2;

// Define IS_SET helper
bool IS_SET(uint flag) {
    import st : win;
    return (win.mode & flag) != 0;
}

static if (isPatchEnabled!"OPENURLONCLICK_PATCH") {
    static if (!isPatchEnabled!"REFLOW_PATCH") {
        static if (isPatchEnabled!"SCROLLBACK_PATCH") {
            alias TLINEURL = TLINE;
        } else {
            auto TLINEURL(int y) { return term.line[y]; }
        }
    }

    extern(C) export __gshared int url_x1, url_y1, url_x2, url_y2 = -1;
    extern(C) export __gshared int url_draw, url_click, url_maxcol;

    private int isvalidurlchar(Rune u) {
        static immutable string urlchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ~
            "abcdefghijklmnopqrstuvwxyz" ~
            "0123456789-._~:/?#@!$&'*+,;=%";
        return u < 128 && strchr(urlchars.ptr, cast(int)u) != null;
    }

    static if (isPatchEnabled!"REFLOW_PATCH") {
        private int findeowl(Line line) {
            int i = term.col - 1;

            do {
                if (line[i].mode & ATTR_WRAP)
                    return i;
            } while (!(line[i].mode & ATTR_SET) && --i >= 0);

            return -1;
        }
    } else {
        private int findeowl(int row) {
            static if (isPatchEnabled!"COLUMNS_PATCH") {
                int col = term.maxcol - 1;
            } else {
                int col = term.col - 1;
            }

            do {
                if (TLINEURL(row)[col].mode & ATTR_WRAP)
                    return col;
            } while (TLINEURL(row)[col].u == ' ' && --col >= 0);
            return -1;
        }
    }

    void clearurl() {
        while (url_y1 <= url_y2 && url_y1 < term.row)
            term.dirty[url_y1++] = 1;
        url_y2 = -1;
    }

    static if (isPatchEnabled!"REFLOW_PATCH") {
        char* detecturl(int col, int row, int draw) {
            static char[2048] url;
            Line line;
            int x1, y1, x2, y2;
            int i = url.length/2+1, j = cast(int)(url.length/2);
            int row_start = row, col_start = col;
            int minrow = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr - term.histf;
            int maxrow = IS_SET(MODE_ALTSCREEN) ? term.row - 1 : term.scr + term.row - 1;

            if (draw)
                clearurl();

            url_maxcol = 0;
            line = TLINE(row);

            if (!isvalidurlchar(line[col].u))
                return null;

            do {
                x1 = col_start;
                y1 = row_start;
                url_maxcol = max(url_maxcol, x1);
                url[--i] = cast(char)line[col_start].u;
                if (--col_start < 0) {
                    if (--row_start < minrow || (col_start = findeowl(TLINE(row_start))) < 0)
                        break;
                    line = TLINE(row_start);
                }
            } while (isvalidurlchar(line[col_start].u) && i > 0);

            if (url[i] != 'h')
                return null;

            line = TLINE(row);
            do {
                x2 = col;
                y2 = row;
                url_maxcol = max(url_maxcol, x2);
                url[j++] = cast(char)line[col].u;
                if (line[col++].mode & ATTR_WRAP) {
                    if (++row > maxrow)
                        break;
                    col = 0;
                    line = TLINE(row);
                }
            } while (col < term.col && isvalidurlchar(line[col].u) && j < url.length-1);

            url[j] = 0;

            if (strncmp("https://".ptr, &url[i], 8) && strncmp("http://".ptr, &url[i], 7))
                return null;

            if (strchr(",.;:?!".ptr, cast(int)(url[j-1])) != null) {
                x2 = max(x2-1, 0);
                url[j-1] = 0;
            }

            if (draw) {
                url_x1 = (y1 >= 0) ? x1 : 0;
                url_x2 = (y2 < term.row) ? x2 : url_maxcol;
                url_y1 = max(y1, 0);
                url_y2 = min(y2, term.row-1);
                url_draw = 1;
                for (y1 = url_y1; y1 <= url_y2; y1++)
                    term.dirty[y1] = 1;
            }

            return &url[i];
        }
    } else {
        char* detecturl(int col, int row, int draw) {
            static char[2048] url;
            int x1, y1, x2, y2, wrapped;
            int row_start = row;
            int col_start = col;
            int i = url.length/2+1, j = cast(int)(url.length/2);

            static if (isPatchEnabled!"SCROLLBACK_PATCH") {
                int minrow = term.scr - term.histn, maxrow = term.scr + term.row - 1;
                if ((term.mode & (1 << 2)) != 0)
                    minrow = 0, maxrow = term.row - 1;
            } else {
                int minrow = 0, maxrow = term.row - 1;
            }
            url_maxcol = 0;

            if (draw)
                clearurl();

            if (!isvalidurlchar(TLINEURL(row)[col].u))
                return null;

            do {
                x1 = col_start;
                y1 = row_start;
                url_maxcol = max(url_maxcol, x1);
                url[--i] = cast(char)TLINEURL(row_start)[col_start].u;
                if (--col_start < 0) {
                    if (--row_start < minrow || (col_start = findeowl(row_start)) < 0)
                        break;
                }
            } while (i > 0 && isvalidurlchar(TLINEURL(row_start)[col_start].u));

            if (url[i] != 'h')
                return null;

            do {
                x2 = col;
                y2 = row;
                url_maxcol = max(url_maxcol, x2);
                url[j++] = cast(char)TLINEURL(row)[col].u;
                wrapped = TLINEURL(row)[col].mode & ATTR_WRAP;
                static if (isPatchEnabled!"COLUMNS_PATCH") {
                    if (++col >= term.maxcol || wrapped) {
                        col = 0;
                        if (++row > maxrow || !wrapped)
                            break;
                    }
                } else {
                    if (++col >= term.col || wrapped) {
                        col = 0;
                        if (++row > maxrow || !wrapped)
                            break;
                    }
                }
            } while (j < url.length-1 && isvalidurlchar(TLINEURL(row)[col].u));

            url[j] = 0;

            if (strncmp("https://".ptr, &url[i], 8) && strncmp("http://".ptr, &url[i], 7))
                return null;

            if (draw) {
                url_x1 = (y1 >= 0) ? x1 : 0;
                url_x2 = (y2 < term.row) ? x2 : url_maxcol;
                url_y1 = max(y1, 0);
                url_y2 = min(y2, term.row-1);
                url_draw = 1;
                for (y1 = url_y1; y1 <= url_y2; y1++)
                    term.dirty[y1] = 1;
            }

            return &url[i];
        }
    }

    void openUrlOnClick(int col, int row, char* url_opener) {
        char* url = detecturl(col, row, 1);
        if (url) {
            import core.stdc.stdlib : system;
            import std.format : format;
            import std.process : spawnProcess;
            
            // Use spawnProcess instead of posix_spawnp
            string[] args = [cast(string)fromStringz(url_opener), cast(string)fromStringz(url)];
            try {
                spawnProcess(args);
            } catch (Exception e) {
                // Ignore errors
            }
        }
    }
}