class CLine
{
    CLine   *next, *last;
    U8      *line;
};

U0 EdLiteUpdate(CLine *head, CLine *cur_line, I64 cur_col, I64 line_start_col)
{
    I64      ch, i, j, k, k2, cursor_col, cursor_row = -1;
    U8      *st;
    CLine   *tmpl = cur_line;
    Bool     done_eof = FALSE;

    text.raw_col = 0;
    for (i = 0; i < text.rows / 2; i++)
        if (tmpl->last != head)
            tmpl = tmpl->last;
    for (i = 0; i < text.rows; i++)
    {
        if (cursor_row < 0 && tmpl == cur_line)
        {
            k = 0;
            for (j = 0; j < cur_col; j++)
                if (tmpl->line[j] == '\t')
                    k = (k + 4) & ~3;
                else
                    k++;
            cursor_col = k;
            cursor_row = i;
        }
        if (tmpl != head)
        {
            st = tmpl->line;
            k = 0;
            j= 0 ;
            while (ch = *st++)
            {
                if (ch == '\t')
                    k2 = (k + 4) & ~3;
                else
                    k2 = k + 1;
                if (line_start_col <= k < line_start_col + text.cols)
                {
                    '' ch;
                    j = k2 - line_start_col;
                }
                k = k2;
            }
            if (j < text.cols)
                '\n';
            tmpl = tmpl->next;
        }
        else
        {
            if (!done_eof)
            {
                '<EOF>';
                done_eof = TRUE;
            }
            '\n';
        }
    }
    text.raw_col = text.cols * cursor_row + cursor_col - line_start_col;
    RawPutChar(0x7F);
}

Bool EdLite(U8 *filename, I64 num=1, I64 edf_dof_flags=0)
{//Light weight text editor for debugging.
    U8      *src, *src2, *src3, *dst, *buf, *bin_data = NULL;
    I64      i, count = 0, ch, sc, size, bin_size = 0, line_start_col = 0, cur_col = 0, old_raw_flags = text.raw_flags;
    CLine    head, *tmpl, *tmpl1, *cur_line;
    Bool     res        = FALSE,
             old_raw    = Raw(ON),
             old_debug  = DebugMode(ON),
             old_single = SingleUser(ON);

    if (!filename)
        filename = blkdev.tmp_filename;
    buf = FileRead(filename, &size);

    PUSHFD
    CLI
    text.raw_flags = text.raw_flags & ~RAWF_SCROLL | RAWF_SHOW_DOLLAR;
    kbd.scan_code = 0;
    QueueInit(&head);
    head.line = StrNew("");

    if (buf)
    {
        src = buf;
        while (*src)
        {
            src2 = src;
            while ((ch = *src++) && ch != '\r' && ch != '\n');
            src--;
            *src++ = 0;
            if (!ch)
                src--;
            while (ch == '\r' && *src == '\n' || *src == CH_CURSOR)
                src++;
            dst = src3 = src2;
            while (ch = *src3++)
                if (ch != '\n' && ch != CH_CURSOR)
                    *dst++ = ch;
            *dst = 0;

            tmpl = MAlloc(sizeof(CLine));
            tmpl->line = StrNew(src2);
            QueueInsert(tmpl, head.last);
            count++;
        }

        if (src + 1 - buf<size)
        {
            bin_data = MAlloc(bin_size = size - (src - buf));
            MemCopy(bin_data, src, bin_size);
        }
        Free(buf);
        res = TRUE;
    }

    cur_line = head.next;
    if (--num < 0)
        res = FALSE;
    else
    {
        if (num <= count)
            while (num--)
                cur_line = cur_line->next;
        else
        {
            cur_line = &head;
            res = FALSE;
        }
    }
    do
    {
        if (cur_line == &head)
            cur_col = 0;
        while (cur_col - line_start_col < 0)
            line_start_col -= 8;
        while (cur_col - line_start_col >= text.cols)
            line_start_col += 8;
        EdLiteUpdate(&head, cur_line, cur_col, line_start_col);
        switch (ch = KeyGet(&sc, FALSE, TRUE))
        {
            case 0:
                switch (sc.u8[0])
                {
                    case SC_CURSOR_UP:
                        if (cur_line->last != &head)
                            cur_line = cur_line->last;
                        if (cur_col > StrLen(cur_line->line))
                            cur_col = StrLen(cur_line->line);
                        break;

                    case SC_CURSOR_DOWN:
                        if (cur_line != &head)
                            cur_line = cur_line->next;
                        if (cur_col > StrLen(cur_line->line))
                            cur_col = StrLen(cur_line->line);
                        break;

                    case SC_CURSOR_RIGHT:
                        cur_col++;
                        if (cur_col > StrLen(cur_line->line))
                        {
                            tmpl = cur_line->next;
                            if (tmpl != &head)
                            {
                                cur_col = 0;
                                cur_line = tmpl;
                            }
                            else
                                cur_col = StrLen(cur_line->line);
                        }
                        break;

                    case SC_CURSOR_LEFT:
                        if (cur_col)
                            cur_col--;
                        else
                        {
                            tmpl = cur_line->last;
                            if (tmpl != &head)
                            {
                                cur_line = tmpl;
                                cur_col = StrLen(tmpl->line);
                            }
                        }
                        break;

                    case SC_PAGE_UP:
                        for (i = 1; i < text.rows; i++)
                        {
                            if (cur_line->last != &head)
                                cur_line = cur_line->last;
                            if (cur_col > StrLen(cur_line->line))
                                cur_col = StrLen(cur_line->line);
                        }
                        break;

                    case SC_PAGE_DOWN:
                        for (i = 1; i < text.rows; i++)
                        {
                            if (cur_line != &head)
                                cur_line = cur_line->next;
                            if (cur_col > StrLen(cur_line->line))
                                cur_col = StrLen(cur_line->line);
                        }
                        break;

                    case SC_DELETE:
                        if (cur_col == StrLen(cur_line->line))
                        {
                            tmpl = cur_line->next;
                            if (cur_line != &head && tmpl != &head) {
                                src = MStrPrint("%s%s", cur_line->line, tmpl->line);
                                Free(cur_line->line);
                                Free(tmpl->line);
                                cur_line->line = src;
                                QueueRemove(tmpl);
                                Free(tmpl);
                            }
                        }
                        else
                            StrCopy(cur_line->line + cur_col, cur_line->line + cur_col + 1);
                        break;
                }
                break;

            case '\n':
            case '\r':
                tmpl = MAlloc(sizeof(CLine));
                tmpl->line = StrNew(cur_line->line + cur_col);
                cur_line->line[cur_col] = 0;
                QueueInsert(tmpl, cur_line);
                cur_line = tmpl;
                cur_col = 0;
                break;

            case CH_BACKSPACE:
                if (cur_col) {
                    StrCopy(cur_line->line + cur_col - 1, cur_line->line + cur_col);
                    cur_col--;
                }
                else if (cur_line!=&head && cur_line->last!=&head)
                {
                    tmpl = cur_line->last;
                    src = MStrPrint("%s%s", tmpl->line, cur_line->line);
                    cur_col = StrLen(tmpl->line);
                    Free(cur_line->line);
                    Free(tmpl->line);
                    tmpl->line = src;
                    QueueRemove(cur_line);
                    Free(cur_line);
                    cur_line = tmpl;
                }
                break;

            case CH_CTRLY:
                if (cur_line != &head)
                {
                    tmpl = cur_line;
                    cur_line = cur_line->next;
                    QueueRemove(tmpl);
                    Free(tmpl->line);
                    Free(tmpl);
                    cur_col = 0;
                }
                break;

            default:
                if (Bt(char_bmp_printable, ch))
                {
                    if (cur_line == &head) {
                        cur_line = MAlloc(sizeof(CLine));
                        cur_line->line = StrNew("");
                        QueueInsert(cur_line, head.last);
                    }
                    src = MAlloc(StrLen(cur_line->line) + 2);
                    MemCopy(src, cur_line->line, cur_col);
                    src[cur_col] = ch;
                    if (cur_col < StrLen(cur_line->line))
                        StrCopy(src + cur_col + 1, cur_line->line + cur_col);
                    else
                        src[cur_col + 1] = 0;
                    Free(cur_line->line);
                    cur_line->line = src;
                    cur_col++;
                }
        }
    }
    while (ch != CH_SHIFT_ESC && ch != CH_ESC);

    if (ch != CH_ESC)
    {
        if (edf_dof_flags & EDF_WAS_WRITE)
            res = FALSE;
    }
    else
    {
        size = bin_size;

        tmpl = head.next;
        while (tmpl != &head)
        {
            size += StrLen(tmpl->line) + 1;
            tmpl = tmpl->next;
        }

        buf = dst = MAlloc(size);
        tmpl = head.next;
        while (tmpl != &head)
        {
            i = StrLen(tmpl->line);
            MemCopy(dst, tmpl->line, i);
            dst += i;
            *dst++ = '\n';
            tmpl = tmpl->next;
        }
        if (bin_data)
            MemCopy(dst, bin_data, bin_size);
        FileWrite(filename, buf, size);
        Free(buf);

        if (edf_dof_flags & EDF_WAS_WRITE)
            res = TRUE;
    }

    tmpl = head.next;
    while (tmpl != &head)
    {
        tmpl1 = tmpl->next;
        QueueRemove(tmpl);
        Free(tmpl->line);
        Free(tmpl);
        tmpl = tmpl1;
    }
    Free(head.line);
    Free(bin_data);
    Raw(old_raw);
    DebugMode(old_debug);
    SingleUser(old_single);
    text.raw_flags = text.raw_flags & ~RAWF_SHOW_DOLLAR | old_raw_flags & (RAWF_SHOW_DOLLAR | RAWF_SCROLL);
    POPFD

    return res;
}

U0 ToFileLine(U8 *_fl_file_line, U8 **_filename, I64 *_linenum)
{//"FI:D:/Dir/File.ZC,123" to "D:/Dir/File.ZC" and 123.
    U8 *st, *fl_file_line = StrNew(_fl_file_line);
    I64 linenum;

    StrFirstRemove(fl_file_line, ":");
    st = StrNew(fl_file_line);
    StrLastRemove(fl_file_line, ",", st);
    linenum = Str2I64(st);
    Free(st);
    *_filename = fl_file_line;
    *_linenum = linenum;
}

Bool EdLiteFileLine(U8 *fl_file_line, I64 edf_dof_flags=0)
{
    Bool res;
    U8  *filename;
    I64  linenum;

    ToFileLine(fl_file_line, &filename, &linenum);
    res = EdLite(filename, linenum, edf_dof_flags);
    Free(filename);

    return res;
}

U0 FixSet(U8 *filename, I64 line)
{//Compiler calls this to set file line for Fix
    U8 *st = MStrPrint("FL:%s,%d", filename, line);

    while (LBts(&sys_semas[SEMA_FIX], 0))
        Yield;
    Free(debug.fix_file_line);
    debug.fix_file_line = SysStrNew(st);
    LBtr(&sys_semas[SEMA_FIX], 0);
}

Bool Fix(I64 edf_dof_flags=0)
{//Jump to last error src code to fix it.
    U8  *st;
    Bool res = FALSE;

    while (LBts(&sys_semas[SEMA_FIX], 0))
        Yield;
    st = StrNew(debug.fix_file_line);
    LBtr(&sys_semas[SEMA_FIX], 0);

    if (st)
    {
        if (IsRaw)
            res = EdLiteFileLine(st, edf_dof_flags);
        else
            res = Ed(st, edf_dof_flags);
    }
    Free(st);

    return res;
}