implementation module EdShift;

import StdEnv;
import EdConstants,EdText;
/* RWS ... */
from EdProgramState import EmptyUndo,::StartupInfo;
from EdMenuItems import Edit_UpdateMenuItems;
/* ... RWS */

// apply replace_lines_f to lines begin_line..end_line in text

replace_lines :: (TLine -> TLine) !Int !Int !Text -> Text;
replace_lines replace_line_f begin_line n_lines text
	= shift_text_in_blocks_at_line begin_line text;
	{
		shift_text_in_blocks_at_line line_n Nil
			= Nil;
		shift_text_in_blocks_at_line line_n (block:!blocks)
			| line_n>=LinesPerBlock
				= block :! shift_text_in_blocks_at_line (line_n-LinesPerBlock) blocks;
				= shift_text_in_blocks_right line_n n_lines block blocks;
		
		shift_text_in_blocks_right line_n n_lines block blocks
			# (n_lines,block) = shift_text_in_block line_n n_lines block;
			| n_lines==0
				= block :! blocks;
				# (next_block:!blocks) = blocks;
				= block :! shift_text_in_blocks_right 0 n_lines next_block blocks;
		
		shift_text_in_block line_n n_lines Nil
			= (n_lines,Nil);
		shift_text_in_block line_n n_lines lines0=:(line:!lines)
			| n_lines==0
				= (n_lines,lines0);
			| line_n==0
				# (n_lines,lines) = shift_text_in_block line_n (n_lines-1) lines;
				= (n_lines,replace_line_f line :! lines);
				# (n_lines,lines) = shift_text_in_block (line_n-1) n_lines lines;
				= (n_lines,line :! lines);
	}

from EdProgramState import :: ProgState{..},:: IO,:: ProgIO,:: Editor{..},:: Project,:: CallBackFun, :: CompilingInfo (..), :: CompilerProcess (..);

from EdProgramState import GetFrontWindow,SetFrontWindow,IsEmptySelection,Between;
from EdDraw import Erase_and_DrawLines,DrawReHilite;
from EdTextWindow import GetText;

from deltaWindow import DrawInWindow,WindowGetFrame;

n_lines_in_selection {l1,l2,c2}
	| c2<=0
		= l2-l1;
		= inc (l2-l1);

replace_lines_and_redraw :: (Int TLine->TLine) !ProgState !IO -> ProgIO;
replace_lines_and_redraw shift_selection_f pstate=:{editor={editwindows}} io
	# (window_id,front_window) = GetFrontWindow editwindows;
	| window_id==NoWdID
		= (pstate,io)
		# selection = front_window.wtext.WinText.selection;
		  n_lines = n_lines_in_selection selection.tsel;
		| IsEmptySelection selection || n_lines==0
			= ({pstate & editor.editwindows=SetFrontWindow front_window editwindows},io);
			# front_window_text = replace_lines (shift_selection_f front_window.wformat.tabw.ttabw) selection.tsel.l1 n_lines front_window.wtext.text;
			  front_window = {front_window & wtext.text=front_window_text,wstate.saved=False};
			  selection = extend_selection_to_begin_and_end_of_line front_window selection;
			  front_window = {front_window & wtext.WinText.selection=selection};
			  (front_window,io) = redraw_lines selection front_window window_id io;
/* RWS ...
			= ({pstate & editor.editwindows=SetFrontWindow front_window editwindows},io);
... */
			  front_window = {front_window & wstate.undoinfo = {EmptyUndo & menu.action = ""}};
			= Edit_UpdateMenuItems {pstate & editor.editwindows=SetFrontWindow front_window editwindows} io;
/* ... RWS */

from EdSupport import CalcPixelSelection;
from EdText import Line_NrChars,Text_GetLine;

extend_selection_to_begin_and_end_of_line {wtext={text,nrlines,curline},wformat={winfont,metrics,tabw}} selection=:{tsel}
	# tsel = extend_text_selection_to_begin_and_end_of_line tsel nrlines text;
	  psel = CalcPixelSelection tsel tabw.ptabw metrics.height curline text winfont.font;
	= {psel=psel,tsel=tsel};

extend_text_selection_to_begin_and_end_of_line {l1,l2,c2} n_lines_in_text text
	| c2==0
		= {l1=l1,l2=l2,c1=0,c2=c2};
	| l2<n_lines_in_text
		= {l1=l1,l2=l2+1,c1=0,c2=0};
		= {l1=l1,l2=l2,c1=0,c2=Line_NrChars (Text_GetLine (l2-1) text)};

redraw_lines selection=:{tsel=tsel=:{l1,l2}} wd=:{wformat={metrics={height,ascent,lead},tabw}} wdid io
	# ((_,(right,_)),io) = WindowGetFrame wdid io;
	  ofs = ascent + lead;
	  base_y = PictureTop + l1 * height + ofs;
	  (wd,_,text) = GetText wd;
	  n_redraw_lines = n_lines_in_selection tsel;
	  lines = Text_GetLines l1 n_redraw_lines text;	  
	  io = DrawInWindow wdid [ redraw_lines_and_selection] io;
		  with {
	  		redraw_lines_and_selection pic
	  			# pic = Erase_and_DrawLines False base_y height ofs tabw.ptabw right lines pic;
				= DrawReHilite selection height pic;
		  }
	= (wd,io);

shift_selection_right :: !ProgState !IO -> ProgIO;
shift_selection_right pstate io
	= replace_lines_and_redraw shift_line_right pstate io;

shift_line_right text_tab_width line
	= TabStr :! line;

shift_selection_left :: !ProgState !IO -> ProgIO;
shift_selection_left pstate io
	= replace_lines_and_redraw shift_line_left pstate io;

shift_line_left text_tab_width (string:!rest)
	| string==TabStr
		= rest;
		# n_leading_spaces = count_spaces_at_begin 0 string;
		# n_leading_spaces = if (n_leading_spaces<=text_tab_width) n_leading_spaces text_tab_width;
		| n_leading_spaces==size string
			= rest;
		| n_leading_spaces==0
			= string:!rest;
			= string % (n_leading_spaces,(size string)-1) :! rest;
shift_line_left text_tab_width line
	= line;

count_spaces_at_begin n_spaces string
	| n_spaces<size string && string.[n_spaces]==' '
		= count_spaces_at_begin (n_spaces+1) string;
		= n_spaces;
