/* vi:set ts=8 sts=4 sw=4 noet:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * tabpanel.c:
 */

#include "vim.h"

#if defined(FEAT_TABPANEL)

static void do_by_tplmode(int tplmode, int col_start, int col_end,
	int *pcurtab_row, int *ptabpagenr);

// set pcurtab_row. don't redraw tabpanel.
#define TPLMODE_GET_CURTAB_ROW	0
// set ptabpagenr. don't redraw tabpanel.
#define TPLMODE_GET_TABPAGENR	1
// redraw tabpanel.
#define TPLMODE_REDRAW		2

#define TPL_FILLCHAR		' '

#define VERT_LEN		1

// tpl_align's values
#define ALIGN_LEFT		0
#define ALIGN_RIGHT		1

static char_u *opt_name = (char_u *)"tabpanel";
static int opt_scope = OPT_LOCAL;
static int tpl_align = ALIGN_LEFT;
static int tpl_columns = 20;
static int tpl_is_vert = FALSE;

typedef struct {
    win_T   *wp;
    win_T   *cwp;
    char_u  *user_defined;
    int	maxrow;
    int	offsetrow;
    int	*prow;
    int	*pcol;
    int	attr;
    int	col_start;
    int	col_end;
} tabpanel_T;

    int
tabpanelopt_changed(void)
{
    char_u	*p;
    int		new_align = ALIGN_LEFT;
    int		new_columns = 20;
    int		new_is_vert = FALSE;

    p = p_tplo;
    while (*p != NUL)
    {
	if (STRNCMP(p, "align:", 6) == 0)
	{
	    p += 6;
	    if (STRNCMP(p, "left", 4) == 0)
	    {
		p += 4;
		new_align = ALIGN_LEFT;
	    }
	    else if (STRNCMP(p, "right", 5) == 0)
	    {
		p += 5;
		new_align = ALIGN_RIGHT;
	    }
	    else
		return FAIL;
	}
	else if (STRNCMP(p, "columns:", 8) == 0 && VIM_ISDIGIT(p[8]))
	{
	    p += 8;
	    new_columns = getdigits(&p);
	}
	else if (STRNCMP(p, "vert", 4) == 0)
	{
	    p += 4;
	    new_is_vert = TRUE;
	}

	if (*p != ',' && *p != NUL)
	    return FAIL;
	if (*p == ',')
	    ++p;
    }

    tpl_align = new_align;
    tpl_columns = new_columns;
    tpl_is_vert = new_is_vert;

    shell_new_columns();
    return OK;
}

/*
 * Return the width of tabpanel.
 */
    int
tabpanel_width(void)
{
    switch (p_stpl)
    {
	case 0:
	    return 0;
	case 1:
	    if (first_tabpage->tp_next == NULL)
		return 0;
    }
    if (Columns < tpl_columns)
	return 0;
    else
	return tpl_columns;
}

/*
 * Return the offset of a window considering the width of tabpanel.
 */
    int
tabpanel_leftcol(void)
{
    return tpl_align == ALIGN_RIGHT ? 0 : tabpanel_width();
}

/*
 * draw the tabpanel.
 */
    void
draw_tabpanel(void)
{
    int saved_KeyTyped = KeyTyped;
    int saved_got_int = got_int;
    int maxwidth = tabpanel_width();
    int vs_attr = HL_ATTR(HLF_C);
    int curtab_row = 0;
    int vsrow = 0;
    int is_right = tpl_align == ALIGN_RIGHT;

    if (maxwidth == 0)
	return;

    // Reset got_int to avoid build_stl_str_hl() isn't evaluated.
    got_int = FALSE;

    if (tpl_is_vert)
    {
	if (is_right)
	{
	    // draw main contents in tabpanel
	    do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN,
		    maxwidth - VERT_LEN, &curtab_row, NULL);
	    do_by_tplmode(TPLMODE_REDRAW, VERT_LEN, maxwidth, &curtab_row,
		    NULL);
	    // draw vert separator in tabpanel
	    for (vsrow = 0; vsrow < Rows; vsrow++)
		screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow,
			topframe->fr_width, vs_attr);
	}
	else
	{
	    // draw main contents in tabpanel
	    do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - VERT_LEN,
		    &curtab_row, NULL);
	    do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - VERT_LEN,
		    &curtab_row, NULL);
	    // draw vert separator in tabpanel
	    for (vsrow = 0; vsrow < Rows; vsrow++)
		screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow,
			maxwidth - VERT_LEN, vs_attr);
	}
    }
    else
    {
	do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth, &curtab_row, NULL);
	do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth, &curtab_row, NULL);
    }

    got_int |= saved_got_int;

    // A user function may reset KeyTyped, restore it.
    KeyTyped = saved_KeyTyped;

    redraw_tabpanel = FALSE;
}

/*
 * Return tabpagenr when clicking and dragging in tabpanel.
 */
    int
get_tabpagenr_on_tabpanel(void)
{
    int		maxwidth = tabpanel_width();
    int		curtab_row = 0;
    int		tabpagenr = 0;

    if (maxwidth == 0)
	return -1;

    do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth, &curtab_row, NULL);
    do_by_tplmode(TPLMODE_GET_TABPAGENR, 0, maxwidth, &curtab_row,
	    &tabpagenr);

    return tabpagenr;
}

/*
 * Fill tailing area between {start_row} and {end_row - 1}.
 */
    static void
screen_fill_tailing_area(
	int	tplmode,
	int	row_start,
	int	row_end,
	int	col_start,
	int	col_end,
	int	attr)
{
    int is_right = tpl_align == ALIGN_RIGHT;
    if (tplmode == TPLMODE_REDRAW)
	screen_fill(row_start, row_end,
		(is_right ? topframe->fr_width : 0) + col_start,
		(is_right ? topframe->fr_width : 0) + col_end,
		TPL_FILLCHAR, TPL_FILLCHAR, attr);
}

/*
 * screen_puts_len() for tabpanel.
 */
    static void
screen_puts_len_for_tabpanel(
	int	    tplmode,
	char_u	    *p,
	int	    len,
	int	    attr,
	tabpanel_T  *pargs)
{
    int		j;
    int		chlen;
    int		chcells;
    char_u	buf[IOSIZE];
    char_u	*temp;

    for (j = 0; j < len;)
    {
	if (tplmode != TPLMODE_GET_CURTAB_ROW
		&& pargs->maxrow <= *pargs->prow - pargs->offsetrow)
	    break;

	if (has_mbyte)
	    chlen = (*mb_ptr2len)(p + j);
	else
	    chlen = 1;

	for (int k = 0; k < chlen; k++)
	    buf[k] = p[j + k];
	buf[chlen] = NUL;
	j += chlen;

	// Make all characters printable.
	temp = transstr(buf);
	if (temp != NULL)
	{
	    vim_strncpy(buf, temp, sizeof(buf) - 1);
	    vim_free(temp);
	}

	if (has_mbyte)
	    chcells = (*mb_ptr2cells)(buf);
	else
	    chcells = 1;

	if (pargs->col_end < (*pargs->pcol) + chcells)
	{
	    // fill the tailing area of current row.
	    if (*pargs->prow - pargs->offsetrow >= 0
		    && *pargs->prow - pargs->offsetrow < pargs->maxrow)
		screen_fill_tailing_area(tplmode,
			*pargs->prow - pargs->offsetrow,
			*pargs->prow - pargs->offsetrow + 1,
			*pargs->pcol, pargs->col_end, attr);
	    *pargs->pcol = pargs->col_end;

	    if (pargs->col_end < chcells)
		break;
	}

	if (*pargs->pcol + chcells <= pargs->col_end)
	{
	    int off = (tpl_align == ALIGN_RIGHT)
		    ? topframe->fr_width
		    : 0;
	    if (tplmode == TPLMODE_REDRAW
		    && (*pargs->prow - pargs->offsetrow >= 0
		    && *pargs->prow - pargs->offsetrow < pargs->maxrow))
		screen_puts(buf, *pargs->prow - pargs->offsetrow,
			*pargs->pcol + off, attr);
	    *pargs->pcol += chcells;
	}
    }
}

/*
 * default tabpanel drawing behavior if 'tabpanel' option is empty.
 */
    static void
draw_tabpanel_default(int tplmode, tabpanel_T *pargs)
{
    int		modified;
    int		wincount;
    int		len = 0;
    char_u	buf[2] = { NUL, NUL };

    modified = FALSE;
    for (wincount = 0; pargs->wp != NULL;
	    pargs->wp = pargs->wp->w_next, ++wincount)
	if (bufIsChanged(pargs->wp->w_buffer))
	    modified = TRUE;

    if (modified || wincount > 1)
    {
	if (wincount > 1)
	{
	    vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
	    len = (int)STRLEN(NameBuff);
	    screen_puts_len_for_tabpanel(tplmode, NameBuff, len,
#if defined(FEAT_SYN_HL)
		    hl_combine_attr(pargs->attr, HL_ATTR(HLF_T)),
#else
		    pargs->attr,
#endif
		    pargs);
	}
	if (modified)
	{
	    buf[0] = '+';
	    screen_puts_len_for_tabpanel(tplmode, buf, 1, pargs->attr, pargs);
	}

	buf[0] = TPL_FILLCHAR;
	screen_puts_len_for_tabpanel(tplmode, buf, 1, pargs->attr, pargs);
    }

    get_trans_bufname(pargs->cwp->w_buffer);
    shorten_dir(NameBuff);
    len = (int)STRLEN(NameBuff);
    screen_puts_len_for_tabpanel(tplmode, NameBuff, len, pargs->attr, pargs);

    // fill the tailing area of current row.
    if (*pargs->prow - pargs->offsetrow >= 0
	    && *pargs->prow - pargs->offsetrow < pargs->maxrow)
	screen_fill_tailing_area(tplmode, *pargs->prow - pargs->offsetrow,
		*pargs->prow - pargs->offsetrow + 1,
		*pargs->pcol, pargs->col_end, pargs->attr);
    *pargs->pcol = pargs->col_end;
}

/*
 * Draw tabpanel content with highlight handling.
 * Processes hltab entries and fills tailing area.
 */
    static void
draw_tabpanel_with_highlight(
	int	    tplmode,
	char_u	    *buf,
	stl_hlrec_T *hltab,
	tabpanel_T  *pargs)
{
    char_u	*p;
    int		curattr;
    int		n;

    curattr = pargs->attr;
    p = buf;
    for (n = 0; hltab[n].start != NULL; n++)
    {
	screen_puts_len_for_tabpanel(tplmode, p, (int)(hltab[n].start - p),
		curattr, pargs);
	p = hltab[n].start;
	if (hltab[n].userhl == 0)
	    curattr = pargs->attr;
	else if (hltab[n].userhl < 0)
	    curattr = syn_id2attr(-hltab[n].userhl);
#ifdef FEAT_TERMINAL
	else if (pargs->wp != NULL && pargs->wp != curwin
		&& bt_terminal(pargs->wp->w_buffer)
		&& pargs->wp->w_status_height != 0)
	    curattr = highlight_stltermnc[hltab[n].userhl - 1];
	else if (pargs->wp != NULL && bt_terminal(pargs->wp->w_buffer)
		&& pargs->wp->w_status_height != 0)
	    curattr = highlight_stlterm[hltab[n].userhl - 1];
#endif
	else if (pargs->wp != NULL && pargs->wp != curwin
		&& pargs->wp->w_status_height != 0)
	    curattr = highlight_stlnc[hltab[n].userhl - 1];
	else
	    curattr = highlight_user[hltab[n].userhl - 1];
    }
    screen_puts_len_for_tabpanel(tplmode, p, (int)STRLEN(p), curattr, pargs);

    // fill the tailing area of current row.
    if (*pargs->prow - pargs->offsetrow >= 0
	    && *pargs->prow - pargs->offsetrow < pargs->maxrow)
	screen_fill_tailing_area(tplmode, *pargs->prow - pargs->offsetrow,
		*pargs->prow - pargs->offsetrow + 1, *pargs->pcol,
		pargs->col_end, curattr);
    *pargs->pcol = pargs->col_end;
}

/*
 * do something by tplmode for drawing tabpanel.
 */
    static void
do_by_tplmode(
	int	tplmode,
	int	col_start,
	int	col_end,
	int	*pcurtab_row,
	int	*ptabpagenr)
{
    int		attr_tplf = HL_ATTR(HLF_TPLF);
    int		attr_tpls = HL_ATTR(HLF_TPLS);
    int		attr_tpl = HL_ATTR(HLF_TPL);
    int		col = col_start;
    int		row = 0;
    tabpage_T	*tp = NULL;
    typval_T	v;
    tabpanel_T	args;

    args.maxrow = Rows;
    args.offsetrow = 0;
    args.col_start = col_start;
    args.col_end = col_end;

    if (tplmode != TPLMODE_GET_CURTAB_ROW && args.maxrow > 0)
	while (args.offsetrow + args.maxrow <= *pcurtab_row)
	    args.offsetrow += args.maxrow;

    tp = first_tabpage;

    for (row = 0; tp != NULL; row++)
    {
	if (tplmode != TPLMODE_GET_CURTAB_ROW
		&& args.maxrow <= row - args.offsetrow)
	    break;

	col = col_start;

	v.v_type = VAR_NUMBER;
	v.vval.v_number = tabpage_index(tp);
	set_var((char_u *)"g:actual_curtabpage", &v, TRUE);

	if (tp->tp_topframe == topframe)
	{
	    args.attr = attr_tpls;
	    if (tplmode == TPLMODE_GET_CURTAB_ROW)
	    {
		*pcurtab_row = row;
		do_unlet((char_u *)"g:actual_curtabpage", TRUE);
		break;
	    }
	}
	else
	    args.attr = attr_tpl;

	if (tp == curtab)
	{
	    args.cwp = curwin;
	    args.wp = firstwin;
	}
	else
	{
	    args.cwp = tp->tp_curwin;
	    args.wp = tp->tp_firstwin;
	}

	char_u	*usefmt = vim_strsave(p_tpl);

	if (usefmt != NULL && *usefmt != NUL)
	{
	    while (*usefmt != NUL)
	    {
		char_u	buf[IOSIZE];
		stl_hlrec_T	*hltab;
		stl_hlrec_T	*tabtab;

		if (args.maxrow <= row - args.offsetrow)
		    break;

		buf[0] = NUL;
#ifdef ENABLE_STL_MODE_MULTI_NL
		(void)build_stl_str_hl_mline_nl
#else
		(void)build_stl_str_hl_mline
#endif
			(args.cwp, buf, sizeof(buf),
			&usefmt, opt_name, opt_scope, TPL_FILLCHAR,
			args.col_end - args.col_start, &hltab, &tabtab);

		args.prow = &row;
		args.pcol = &col;

		draw_tabpanel_with_highlight(tplmode, buf, hltab, &args);

		// Move to next line for %@
		if (*usefmt != NUL)
		{
		    row++;
		    col = col_start;
		}
	    }
	}
	else
	{
	    args.user_defined = NULL;
	    args.prow = &row;
	    args.pcol = &col;
	    draw_tabpanel_default(tplmode, &args);
	}

	vim_free(usefmt);
	do_unlet((char_u *)"g:actual_curtabpage", TRUE);

	tp = tp->tp_next;

	if ((tplmode == TPLMODE_GET_TABPAGENR)
		&& (mouse_row <= (row - args.offsetrow)))
	{
	    *ptabpagenr = v.vval.v_number;
	    break;
	}
    }

    // fill the area of TabPanelFill.
    screen_fill_tailing_area(tplmode, row - args.offsetrow, args.maxrow,
	    args.col_start, args.col_end, attr_tplf);
}

#endif // FEAT_TABPANEL
