/******************************************************************************

    MODUL
	block.c

    DESCRIPTION
	In this module are all functions that work with blocks.

    NOTES

    BUGS

    TODO

    EXAMPLES

    SEE ALSO

    INDEX

    HISTORY
	27. Aug 1992	ada created

******************************************************************************/

/* Includes */
#include <defs.h>

/* Globale Variable */
Prototype void RedrawBlock	      (void);
Prototype int  is_inblock	      (int, short);
Prototype void displayblock	      (int);
Prototype void text_redrawblock       (int);
Prototype void do_block 	      (void);
Prototype int  block_ok 	      (void);
Prototype void do_bdelete	      (void);
Prototype void do_bcopy 	      (void);
Prototype void do_bmove 	      (void);
Prototype void do_bsource	      (void);
Prototype void do_autounblock	      (void);

/* Interne Defines & Strukturen */


/* Interne Variable */
static int bs_line,  bs_column;
static int cur_line, cur_column;


/* Interne Prototypes */


#ifdef NOTDEF
/* Redraw NORMAL-Block according to cursor-coordinates */
void RedrawBlock (void)
{
    ED * ep = Ep;
    short lines, cols, len;

    if (!(ActualBlock.flags & BF_ACTIVE) || (ActualBlock.type != BT_NORMAL) || ActualBlock.ep != ep)
	return;

printf ("RedrawBlock\n");
    if (!(ActualBlock.flags & BF_START) ) {
	/* If nothing happened ... do nothing ! */
	if (ActualBlock.end_line == ep->Line && ActualBlock.end_column == ep->Column)
	    return;

	/* If we move behind our start-position change rules */
	if (ep->Line < ActualBlock.start_line ||
		(ep->Line == ActualBlock.start_line && ep->Column < ActualBlock.start_column)) {
	    ActualBlock.end_line = ActualBlock.start_line;
	    ActualBlock.end_column = ActualBlock.start_column;
	    ActualBlock.flags ^= BF_START;

	    goto block_end;
	}

block_start:
	/* Calculate relative offset. Right/Down is positive */
	lines = ep->Line - ActualBlock.end_line;
	cols = ep->Column - ActualBlock.end_column;

	if (lines > 0) {
	    /* Show all new lines */
	    text_displayseg (ActualBlock.end_line - ep->Topline, lines);
	} else if (lines) {
	    /* Erase block in some lines */
	    text_displayseg (ep->Line - ep->Topline, -lines);
	} else { /* lines == 0 */
	    /* we didn't change line but position in the line */
	    RP * rp = ep->Win->RPort;

	    /* Find the real length of our line */
	    len = strlen (ep->List[ActualBlock.end_line]);

	    /* ... to the right */
	    if (cols > 0) {
		/* we must advance the block */
		SetAPen (rp, BLOCK_FPEN);
		SetBPen (rp, BLOCK_BPEN);

		/* If we are already beyond EOL, we may stop */
		if (len < ActualBlock.end_column)
		    return;

		/* There is a bit of text to display */
		if (len > ActualBlock.end_column) {
		    len -= ActualBlock.end_column;

		    /* How much (at most) ? */
		    if (len < cols)
			cols = len;

		    /* Display it */
		    Move (rp, ROW(ActualBlock.end_column - ep->Topcolumn),
			    COLT(ActualBlock.end_line - ep->Topline));
		    Text (rp, Current + ActualBlock.end_column, cols);

		    /* Not how much we have already */
		    len -= cols;
		}

		/* If there is length left, user is beyond EOL and we
		   fill up to the right border */
		if (len) {
		    SetAPen (rp, BLOCK_BPEN);

		    RectFill (rp, COL(ep->Column - ep->Topcolumn + 1),
			    ROW(ep->Line), Xpixs, ROW(ep->Line+1)-1);
		}

		/* Reset pens (save) */
		SetAPen (rp, TEXT_FPEN);
		SetBPen (rp, TEXT_BPEN);
	    } else { /* We got shorter */
		SetAPen (rp, TEXT_FPEN);
		SetBPen (rp, TEXT_BPEN);

		len -= ep->Column;

		/* don't do anything if we are still beyong EOL */
		if (len < 0)
		    return;

		/* Make cols positive */
		cols = -cols;

		if (cols < len)
		    len = cols;

		Move (rp, ROW(ep->Column - ep->Topcolumn),
			COLT(ActualBlock.end_line - ep->Topline));
		Text (rp, Current + ep->Column, len);

		/* if there is something beyond EOL */
		if (cols > len) {
		    SetAPen (rp, BLOCK_BPEN);

		    RectFill (rp, COL(len + ep->Column + 1),
			    ROW(ep->Line), Xpixs, ROW(ep->Line+1)-1);
		}
	    } /* cols changed */
	} /* lines changed */

	ActualBlock.end_line = ep->Line;
	ActualBlock.end_column = ep->Column;
    } else { /* "BF_END" */
	/* If nothing happened ... do nothing ! */
	if (ActualBlock.start_line == ep->Line && ActualBlock.start_column == ep->Column)
	    return;

	/* If we move behind our start-position change rules */
	if (ep->Line > ActualBlock.end_line || (ep->Line == ActualBlock.end_line && ep->Column > ActualBlock.end_column)) {
	    ActualBlock.start_line = ActualBlock.end_line;
	    ActualBlock.start_column = ActualBlock.end_column;
	    ActualBlock.flags ^= BF_START;

	    goto block_start;
	}

block_end:
	/* Calculate relative offset. Left/Up is positive */
	lines = ActualBlock.start_line - ep->Line;
	cols  = ActualBlock.start_column - ep->Column;

	if (lines > 0) {
	    /* Show all new lines */
	    text_displayseg (ep->Line - ep->Topline, lines);
	} else if (lines) {
	    /* Erase block in some lines */
	    text_displayseg (ActualBlock.start_line - ep->Topline, -lines);
	} else { /* lines == 0 */
	    /* we didn't change line but position in the line */
	    RP * rp = ep->Win->RPort;

	    /* Find the real length of our line */
	    len = strlen (ep->List[ActualBlock.start_line]);

	    /* ... to the right */
	    if (cols < 0) {
		/* we must decrease the block */
		SetAPen (rp, BLOCK_FPEN);
		SetBPen (rp, BLOCK_BPEN);

		/* If we are already beyond EOL, we may stop */
		if (len < ActualBlock.start_column)
		    return;

		/* There is a bit of text to display */
		if (len > ActualBlock.end_column) {
		    len -= ActualBlock.end_column;

		    cols = -cols;

		    /* How much (at most) ? */
		    if (len < cols)
			cols = len;

		    /* Display it */
		    Move (rp, ROW(ActualBlock.end_column - ep->Topcolumn),
			    COLT(ActualBlock.end_line - ep->Topline));
		    Text (rp, Current + ActualBlock.end_column, cols);

		    /* Not how much we have already */
		    len -= cols;
		}

		/* If there is length left, user is beyond EOL and we
		   fill up to the right border */
		if (len) {
		    SetAPen (rp, BLOCK_BPEN);

		    RectFill (rp, COL(ep->Column - ep->Topcolumn + 1),
			    ROW(ep->Line), Xpixs, ROW(ep->Line+1)-1);
		}

		/* Reset pens (save) */
		SetAPen (rp, TEXT_FPEN);
		SetBPen (rp, TEXT_BPEN);
	    } else { /* We got longer */
		SetAPen (rp, TEXT_FPEN);
		SetBPen (rp, TEXT_BPEN);

		len -= ep->Column;

		/* don't do anything if we are still beyong EOL */
		if (len < 0)
		    return;

		/* Make cols positive */
		cols = -cols;

		if (cols < len)
		    len = cols;

		Move (rp, ROW(ep->Column - ep->Topcolumn),
			COLT(ActualBlock.end_line - ep->Topline));
		Text (rp, Current + ep->Column, len);

		/* if there is something beyond EOL */
		if (cols > len) {
		    SetAPen (rp, BLOCK_BPEN);

		    RectFill (rp, COL(len + ep->Column + 1),
			    ROW(ep->Line), Xpixs, ROW(ep->Line+1)-1);
		}
	    } /* cols changed */
	} /* lines changed */

	ActualBlock.start_line = ep->Line;
	ActualBlock.start_column = ep->Column;
    }
} /* RedrawBlock */
#endif


int is_inblock (int line, short col)
{
    short blockpos = BP_OUTSIDE;

    if (ActualBlock.type != BT_NONE && Ep == ActualBlock.ep && !Comlinemode && line >= ActualBlock.start_line &&
	    line <= ActualBlock.end_line) {

	if (line == ActualBlock.start_line)
	    blockpos |= BP_START;
	if (line == ActualBlock.end_line)
	    blockpos |= BP_END;

	switch (ActualBlock.type) {
	    case BT_LINE:
		blockpos |= BP_INSIDE;
		break;

	    case BT_NORMAL:
		if (col != -1 && (line == ActualBlock.start_line && col >= ActualBlock.start_column) ||
			(line == ActualBlock.end_line && col <= ActualBlock.end_column))
		    blockpos |= BP_INSIDE;
		break;

	    case BT_VERTICAL:
		if (col != -1 && col >= ActualBlock.start_column && col <= ActualBlock.end_column)
		    blockpos |= BP_INSIDE;
		break;
	} /* switch (ActualBlock.type) */
    } /* if validblock */

    return (blockpos);
} /* is_inblock */


void displayblock (int on)
{
    ED	* ep	= Ep;
    short start = ep->Topline;
    short lines = ActualBlock.end_line - ActualBlock.start_line + 1;
    short flags;

    if (start < ActualBlock.start_line)
	start = ActualBlock.start_line;

    start -= ep->Topline;

    if (ep == ActualBlock.ep) {
	flags = lineflags (ActualBlock.start_line) & lineflags (ActualBlock.end_line);

	if (!on && (!flags || flags == LINE_VISIBLE)) {
	    struct RastPort * rp = ep->Win->RPort;
	    short	      x;

	    x = lines + start;

	    if (x > Rows)
		x = Rows;

	    SetAPen (rp, TEXT_BPEN);

	    /* Block löschen */
	    if (start < x)
		RectFill (rp, Xbase, ROW(start), Xpixs, ROW(x)-1);

	    /* Block aus */
	    ActualBlock.type = BT_NONE;
	}

	/* Text neu darstellen */
	text_displayseg (start, lines);
    } else if (!on) {
	ActualBlock.type = BT_NONE;
    }
} /* displayblock */


void text_redrawblock (int ok)
{
    WIN * savewin = NULL;

    if (ActualBlock.ep) {
	if (ActualBlock.ep != Ep) {
	    savewin = Ep->Win;
	    text_switch (ActualBlock.ep->Win);
	}

	if (ActualBlock.type != BT_NONE) {
	    displayblock (ok);
	}

	if (savewin)
	    text_switch (savewin);
    } /* if (ActualBlock.ep) */

    if (!ok) {
	ActualBlock.type = BT_NONE;
    }
} /* text_redrawblock */


void do_block()          /* block, unblock, bstart, bend */
{
    ED * ep = Ep;

    text_sync ();

    switch(av[0][0]) {
	case 'b':
	    switch (av[0][1]) { /* b[lse] */
		case 'l':
		    if (ActualBlock.type == BT_NONE) {
bstart:
			ActualBlock.ep = ep;
			ActualBlock.start_line = ep->Line;
			ActualBlock.end_line = -1;
			ActualBlock.type = BT_LINE;    /* Note: DON'T DISPLAY THIS ! */
			title ("Block begin");
		    } else {
			if (block_ok ()) {
			    if (ep->AutoUnblock) {
				text_redrawblock(0);
				goto bstart;
			    } else {
				error ("block:\nBlock already marked");
				break;
			    }
			}

			if (ActualBlock.ep != ep)
			    goto bstart;

			title ("Block end");

			ActualBlock.end_line = ep->Line;

			if (ActualBlock.start_line > ActualBlock.end_line) {
			    ActualBlock.end_line = ActualBlock.start_line;
			    ActualBlock.start_line = ep->Line;
			}

			ActualBlock.type = BT_LINE;    /* Force old-style */
			text_redrawblock (1);   /* Now display */
		    }
		break;

		case 's': /* bstart */
		    if (ActualBlock.type == BT_NONE)
			ActualBlock.end_line = -1;
		    else if (ActualBlock.ep != ep) {
			error ("bstart:\nmust be in the\nsame window");
			break;
		    }

		    ActualBlock.ep    = ep;
		    ActualBlock.type  = BT_LINE;
		    ActualBlock.flags = 0;

		    if (ActualBlock.end_line != -1) {
			ULONG old_start,
			      line,
			      diff;

			if (ActualBlock.start_line == ep->Line)
			    break;

			title ("Block start");

			/* Block switched */
			if (ep->Line > ActualBlock.end_line) {
			    ActualBlock.start_line = ActualBlock.end_line;
			    ActualBlock.end_line = ep->Line;

			    text_redisplay ();

			    break;
			}

			old_start = ActualBlock.start_line;
			ActualBlock.start_line = ep->Line;

			if (old_start < ep->Line) { /* We have some block left */
			    RP * rp;

			    if (old_start > ep->Topline) {
				line = old_start - ep->Topline;
				diff = ep->Line - old_start;
			    } else {
				line = 0;
				diff = ep->Topline - ep->Line;
			    }

			    if (!diff)
				break;

			    rp = ep->Win->RPort;

			    SetWrMsk (rp, BLOCK_MASK);
			    SetAPen (rp, TEXT_BPEN);
			    RectFill (rp, Xbase, ROW(line), Xpixs,
							    ROW(line+diff)-1);

			    text_displayseg (line, diff);
			} else { /* block gets longer */
			    line = ep->Line - ep->Topline;
			    diff = old_start - ep->Line;

			    if (line+diff > Rows)
				diff = Rows - line;

			    if (!diff)
				break;

			    text_displayseg (line, diff);
			}
		    } else {
			ActualBlock.start_line = ep->Line;

			title ("Block begin");
		    }
		break;

		case 'e': /* bend */
		    if (ActualBlock.type == BT_NONE)
			error ("BEND needs BLOCK/BSTART");
		    else if (ActualBlock.ep != ep) {
			error ("bend:\nmust be in the\nsame window");
			break;
		    } else {
			ULONG old_start,
			      line,
			      diff;

			if (ActualBlock.end_line == ep->Line)
			    break;

			title ("Block end");

			if (ActualBlock.end_line == -1) {
			    ActualBlock.end_line = ep->Line;

			    text_redrawblock (1);   /* Now display */

			    break;
			}

			/* Block switched */
			if (ep->Line < ActualBlock.start_line) {
			    ActualBlock.end_line = ActualBlock.start_line;
			    ActualBlock.start_line = ep->Line;

			    text_redisplay ();

			    break;
			}

			old_start = ActualBlock.end_line;
			ActualBlock.end_line = ep->Line;

			if (old_start > ep->Line) { /* We have some block left */
			    RP	* rp;

			    line = ep->Line - ep->Topline +1;
			    diff = old_start - ep->Line;

			    if (line+diff > Rows)
				diff = Rows - line;

			    if (!diff)
				break;

			    rp = ep->Win->RPort;

			    SetWrMsk (rp, BLOCK_MASK);
			    SetAPen (rp, TEXT_BPEN);
			    RectFill (rp, Xbase, ROW(line), Xpixs,
							    ROW(line+diff)-1);

			    text_displayseg (line, diff);
			} else { /* block gets longer */
			    if (old_start < ep->Topline) {
				line = 0;
				diff = ep->Line - ep->Topline +1;
			    } else {
				line = old_start - ep->Topline +1;
				diff = ep->Line - old_start;
			    }

			    text_displayseg (line, diff);
			}
		    }
		break;
	    }
	break;

	case 'u':
	    text_redrawblock (0);
	    title ("Block unmarked");
	break;

	case 'l':
	    ActualBlock.ep = ep;
	    ActualBlock.start_line = ActualBlock.end_line = ep->Line;

	    title ("Line marked");
	    text_redrawblock (1);
	break;
    }
} /* do_block() */


int block_ok (void)
{
    if (ActualBlock.type != BT_NONE && ActualBlock.end_line >= 0)
	return (1);

    return (0);
} /* block_ok */


void do_bdelete (void)
{
    long n;
    ED *bep = ActualBlock.ep;
    WIN *savewin = Ep->Win;

    /* TODO */
    if (ActualBlock.type != BT_LINE)
	return;

    if (block_ok()) {
	text_switch(bep->Win);

	n = ActualBlock.end_line - ActualBlock.start_line + 1;

	if (bep->Line >= ActualBlock.start_line && bep->Line <= ActualBlock.end_line)
	    bep->Line = ActualBlock.start_line;

	if (bep->Line > ActualBlock.end_line) {
	    bep->Line -= n;
	    bep->Topline -= n;
	}

	freelist(bep->List + ActualBlock.start_line, ActualBlock.end_line - ActualBlock.start_line + 1);
	bmovl(bep->List+ActualBlock.end_line+1,bep->List+ActualBlock.start_line,(bep->Lines-ActualBlock.end_line-1));
	bep->Lines -= n;
	bep->Modified = 1;

	if (bep->Line >= bep->Lines)
	    bep->Line = bep->Lines - 1;

	if (bep->Topline >= bep->Lines)
	    bep->Topline = bep->Lines - 1;

	if (bep->Line < 0)
	    bep->Topline = bep->Line = 0;

	if (bep->Lines == 0) {
	    ++bep->Lines;
	    bep->List[0] = allocline(1);
	}

	text_load ();

	ActualBlock.type = BT_NONE;

	if (!text_sync ())
	    text_redisplay ();

	text_switch (savewin);
    }
} /* do_bdelete */


void do_bcopy()
{
    LINE  * list;
    long    lines,
	    i;
    ED	  * ep = Ep;
    ubyte * str;

    text_sync ();

    /* TODO */
    if (ActualBlock.type != BT_LINE)

    if (!block_ok ())
	return;

    if (ep == ActualBlock.ep && ep->Line > ActualBlock.start_line && ep->Line <= ActualBlock.end_line) {
	error ("bcopy:\nCannot copy a\nblock into itself");
	return;
    }

    lines = ActualBlock.end_line - ActualBlock.start_line + 1;

    if (extend (ep, lines)) {
	if (list = (LINE *)alloclptr(lines)) {
	    bmovl (ActualBlock.ep->List+ActualBlock.start_line,list,lines);
	    bmovl (ep->List+ep->Line, ep->List+ep->Line+lines,
		    ep->Lines-ep->Line);

	    for (i = 0; i < lines; ++i) {
		str = allocline (strlen((char *)list[i])+1);

		if (!str) {
		    nomemory ();
		    FreeMem (list, lines * sizeof(LINE));
		    freelist (ep->List + ep->Line, i);
		    bmovl (ep->List+ep->Line+lines, ep->List+ep->Line,
			    ep->Lines-ep->Line);
		    return;
		}

		strcpy ((char *)str, (char *)list[i]);
		ep->List[ep->Line+i] = str;
	    }

	    FreeMem (list, lines * sizeof(LINE));

	    if (ep == ActualBlock.ep && ep->Line <= ActualBlock.start_line) {
		ActualBlock.start_line += lines;
		ActualBlock.end_line += lines;
	    }

	    ep->Modified = 1;
	    ep->Lines += lines;

	    text_load ();

	    if (!text_sync ()) {
		scroll_display (0, -lines, 0, ep->Line, MAXLINELEN, ep->Topline+Rows);
	    }

	    /* Move cursor to the bottom */
	    ep->Line += lines;

	    text_load ();

	    text_sync (); /* display is adjusted, if needed */
	}
    }
} /* do_bcopy() */


void do_bmove()
{
    long   lines;
    LINE * list;
    ED	 * ep = Ep;

    text_sync ();

    if (!block_ok ())
	return;

    if (ActualBlock.ep == ep && ep->Line >= ActualBlock.start_line && ep->Line <= ActualBlock.end_line) {
	error ("bmove:\nCannot move a\nblock into itself");
	return;
    }

    lines = ActualBlock.end_line - ActualBlock.start_line + 1;

    if (!(list = (LINE *)alloclptr (lines))) {
	nomemory ();
	return;
    }

    ActualBlock.ep->Modified = ep->Modified = 1;
    bmovl (ActualBlock.ep->List + ActualBlock.start_line, list, lines);

    if (ep == ActualBlock.ep) {
	if (ep->Line > ActualBlock.start_line) {
	    bmovl (ep->List+ActualBlock.end_line+1, ep->List+ActualBlock.start_line, ep->Line-ActualBlock.end_line-1);
	    bmovl (list, ep->List + ep->Line - lines, lines);
	} else {
	    bmovl (ep->List+ep->Line, ep->List+ep->Line+lines, ActualBlock.start_line-ep->Line);
	    bmovl (list, ep->List + ep->Line, lines);
	}
    } else {
	WIN * savewin = ep->Win;

	if (extend (ep, lines)) {
	    bmovl (ActualBlock.ep->List+ActualBlock.end_line+1, ActualBlock.ep->List+ActualBlock.start_line, ActualBlock.ep->Lines-ActualBlock.end_line-1);
	    bmovl (ep->List+ep->Line, ep->List+ep->Line+lines, ep->Lines-ep->Line);
	    bmovl (list, ep->List+ep->Line, lines);

	    ep->Lines += lines;
	    ActualBlock.ep->Lines -= lines;

	    if (ActualBlock.ep->Line >= ActualBlock.start_line && ActualBlock.ep->Line <= ActualBlock.end_line) {
		ActualBlock.ep->Line = ActualBlock.start_line;

		if (ActualBlock.ep->Line == ActualBlock.ep->Lines)
		    ActualBlock.ep->Line --;
	    }

	    if (ActualBlock.ep->Line > ActualBlock.end_line)
		ActualBlock.ep->Line -= lines;

	    if (ActualBlock.ep->Line < 0)
		ActualBlock.ep->Line = 0;

	    ActualBlock.type = BT_NONE;

	    if (ActualBlock.ep->Lines == 0) {
		LINE ptr = allocline (1);

		ActualBlock.ep->List[0] = ptr;
		ActualBlock.ep->Lines ++;
	    }

	    Ep->Line += lines;	/* Move cursor down */

	    text_load ();       /* text_switch() calls text_sync() ! */
	    text_switch (ActualBlock.ep->Win);     /* Make DEST to active Ed */

	    ActualBlock.ep = NULL;	   /* No active block */

	    if (!Ep->iconmode) {        /* Not iconified */
		if (!text_sync ())          /* Redisplay */
		    text_redisplay ();          /* ALWAYS */
	    }

	    text_switch (savewin);      /* old Ed */

	    ep = Ep;			/* set ep */
	}
    }

    ActualBlock.type = BT_NONE;    /* We don't have any block now ! */

    FreeMem (list, lines * sizeof(LINE));
    ep->Modified = 1;

    text_load ();

    if (!text_sync ())
	text_redisplay ();
} /* do_bmove() */


/*
 *  BSOURCE
 *
 *  note that since the start and end lines are loaded immediately and the
 *  block unblock'd before execution starts, you can theoretically have
 *  another BSOURCE as part of this BSOURCE (but be carefull!).
 */

void do_bsource (void)
{
    char buf[MAXLINELEN];
    int i, sl, se;

    /* TODO */
    if (ActualBlock.type != BT_LINE)
	return;

    if (block_ok ()) {
	sl = ActualBlock.start_line;
	se = ActualBlock.end_line + 1;

	for (i = sl; ActualBlock.ep && i < se && i < ActualBlock.ep->Lines; ++i) {
	    text_sync ();        /* make sure we are using latest text */
	    strcpy (buf, (char *)ActualBlock.ep->List[i]);
	    if (*buf != '#' && do_command (buf) == 0)
		break;
	}
	text_redrawblock (0);
    }
} /* do_bsource */


void do_autounblock (void)
{
    Ep->AutoUnblock = test_arg (av[1], Ep->AutoUnblock);
} /* do_autounblock */


/******************************************************************************
*****  ENDE block.c
******************************************************************************/
