##############################################################################
#
# mh_hold_mode.pl v1.08 (20170424)
#
# Copyright (c) 2007, 2015-2017  Michael Hansen
#
# Permission to use, copy, modify, and distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice
# appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# THE AUTHOR BE LIABLE FOR  ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
##############################################################################
#
# Emulation of ircII per-window hold_mode
#
# upgrading from v0.xx to v1.xx:
#
# the statusbar item have changed name from mh_more to mh_sbmore, you
# can remove the old item with '/statusbar window remove mh_more' and
# follow the instructions below to add the new item
#
# should you not like the new item look, you can revert it back to the
# old style with the setting mh_hold_mode_more_oldstyle
#
# previously the default window hold_mode and scroll_always were hardcoded
# they are now setable in irssi (see settings below)
#
# instructions:
#
# add the mh_sbmore statusbar item with '/statusbar window add -after window_empty mh_sbmore'
# (For better control of the placement of mh_sbmore see '/HELP STATUSBAR'
#
# mh_sbmore *should* fully replace Irssi's standard more, so you could
# remove it with '/STATUSBAR WINDOW REMOVE more'
#
# when the script is loaded it will reset all windows and scroll them
# to the last line (without losing backlog) and from then on you will
# have to do '/HOLD_MODE ON' or '/HOLD_MODE OFF' in a given window to
# enable/disable hold_mode for it (off by default)
#
# should the buffer become too much for you to bother to read it, you
# can jump to the bottom with '/HOLD_MODE FLUSH' (although '/CLEAR'
# will do the same)
#
# when hold_mode is on, it will scroll until the last line you said or
# command you send, then hold untill you press enter (and depending on
# the 'scroll_always' value it will either scroll on any enter, or only on
# enter with empty commandline). enter will scroll by 1 page (minus 1 line)
# unless you have moved around in the scrollback buffer with pg-up/dn, in
# which case it will scroll to where you left (except when you pg-dn to
# end of buffer, this will reset the scroll stop point to that line)
#
# (also, you can reset the point to scroll to at any time by just
# pressing enter when in "live" view, the current line will then be set
# as the scroll stop point)
#
# it is not a true-to-ircII hold_mode emulation, as some things are slightly
# different, over time they might be made optional to be able to go back
# to as close to ircII as possible.
#
# (i hope i didn't forget anything :-)
#
# settings:
#
# mh_hold_mode_more_oldstyle (default OFF): switch between old and new
# style more statusbar item
#
# mh_hold_mode_default_hold_mode (default OFF): change the default hold_mode
# setting for new windows
#
# mh_hold_mode_default_scroll_always (default ON): change the default scroll_always
# setting for new windows
#
# history:
#
#	v1.08 (20170424)
#		added 'sbitems' to irssi header for better scriptassist.pl support (github issue #1)
#
#	v1.07 (20160510)
#		corrected typo in instructions
#		added space to old-style more so it looks better
#
#	v1.06 (20160503)
#		fixed call to missing sub
#
#	v1.05 (20160211)
#		moved default settings from hardcoded to irssi settings _default_hold_mode and _default_scroll_always
#
#	v1.04 (20160126)
#		added mh_hold_mode_more_oldstyle and supporting code
#		added namespace to MSGLEVEL
#		code cleanup
#
#	v1.03 (20151226)
#		now using 'key send_line' instead of 'gui key pressed'
#		added /help
#		fixed '/hold_mode toggle', it didnt do anything before
#		code cleanup
#		added changed field to irssi header
#		changed url
#
#	v1.02 (20151210)
#		now accepts both 10 and 13 as keycode for enter, required to work in upcoming irssi
#
#	v1.01 (20151204)
#		nicer (imho) mh_sbmore
#
#	v1.00 (20151201)
#		cleanup and re-release, this have been in active use since 2007 without any issues
#
#	v0.12 (unknown 2007)
#		added scroll_always option: /HOLD_MODE scroll_always [on|off|toggle]
#		Defaults to always scroll on, even if the line is not empty when you
#		press enter - the ircII way. If you like to only scroll on empty lines
#		set '/HOLD_MODE scroll_always off'
#		added '/HOLD_MODE toggle' for ircII emulation completenes
#

use v5.14.2;

use strict;

##############################################################################
#
# irssi head
#
##############################################################################

use Irssi 20100403;
use Irssi::TextUI;

our $VERSION = '1.08';
our %IRSSI   =
(
	'name'        => 'mh_hold_mode',
	'description' => 'Emulation of ircII per-window hold_mode',
	'license'     => 'BSD',
	'authors'     => 'Michael Hansen',
	'contact'     => 'mh on IRCnet #help',
	'url'         => 'http://scripts.irssi.org / https://github.com/mh-source/irssi-scripts',
	'changed'     => 'Mon Apr 24 10:32:25 CEST 2017',
	'sbitems'     => 'mh_sbmore',
);

##############################################################################
#
# global variables
#
##############################################################################

our %config;

our $lastcommand = '';
our $more        = 0;

##############################################################################
#
# common support functions
#
##############################################################################

sub trim_space
{
	my ($string) = @_;

	if (defined($string))
	{
		$string =~ s/^\s+//g;
		$string =~ s/\s+$//g;

	} else
	{
		$string = '';
	}

	return($string);
}

##############################################################################
#
# script functions
#
##############################################################################

sub config_window_get
{
	my ($windowrec) = @_;

	if ($windowrec)
	{
		my $windowid = $windowrec->{'_irssi'};

		if (!exists($config{'WINDOW'}{$windowid}))
		{
			if (Irssi::settings_get_bool('mh_hold_mode_default_hold_mode'))
			{
				$config{'WINDOW'}{$windowid}{'hold_mode'} = 1;

			} else
			{
				$config{'WINDOW'}{$windowid}{'hold_mode'} = 0;
			}

			if (Irssi::settings_get_bool('mh_hold_mode_default_scroll_always'))
			{
				$config{'WINDOW'}{$windowid}{'scroll_always'} = 1;

			} else
			{
				$config{'WINDOW'}{$windowid}{'scroll_always'} = 0;
			}

		}

		return(\%{$config{'WINDOW'}{$windowid}});
	}

	return(undef);
}

sub config_window_del
{
	my ($windowrec) = @_;

	if ($windowrec)
	{
		my $windowid = $windowrec->{'_irssi'};

		if (exists($config{'WINDOW'}{$windowid}))
		{
			delete($config{'WINDOW'}{$windowid});
			return(1);
		}
	}

	return(undef);
}

sub window_bookmark_compare
{
	my ($bookmark1, $bookmark2) = @_;

	if ($bookmark1 and $bookmark2)
	{
		if ($bookmark1->{'_irssi'} == $bookmark2->{'_irssi'})
		{
			return(1);
		}
	}

	return(0);
}

sub window_refresh
{
	my $windowrec = Irssi::active_win();

	if (not $windowrec->view()->{'empty_linecount'})
	{
		my $bookmark = $windowrec->view()->get_bookmark('mh_hold_mode');

		if (not $bookmark)
		{
			$windowrec->view()->set_bookmark('mh_hold_mode', $windowrec->view()->{'startline'});
			$bookmark = $windowrec->view()->get_bookmark('mh_hold_mode');

			if (not $bookmark)
			{
				return(0);
			}
		}

		$windowrec->view()->scroll_line($bookmark);
		statusbar_more_redraw();
	}
}

sub window_scroll
{
	my $windowrec = Irssi::active_win();

	my $bookmark = $windowrec->view()->{'startline'};
	window_refresh();
	my $bookmark_new = $windowrec->view()->{'startline'};

	if (window_bookmark_compare($bookmark_new, $bookmark))
	{
		$windowrec->view()->scroll(($windowrec->{'height'} - 1));

		if ($windowrec->view()->{'bottom'})
		{
			$windowrec->view()->set_bookmark_bottom('mh_hold_mode');

		} else
		{
			$windowrec->view()->set_bookmark('mh_hold_mode', $windowrec->view()->{'startline'});
		}
	}

	statusbar_more_redraw();
}

sub statusbar_more_redraw
{
	Irssi::statusbar_items_redraw('mh_sbmore');
}

##############################################################################
#
# irssi signal handlers
#
##############################################################################

sub signal_key_send_line_first
{
	$lastcommand  = Irssi::parse_special('$L');
	my $windowrec = Irssi::active_win();

	if (($lastcommand eq '' or config_window_get($windowrec)->{'scroll_always'}) and $more)
	{
		window_scroll();
	}
}

sub signal_key_send_line_last
{
	my $windowrec = Irssi::active_win();

	if  (config_window_get($windowrec)->{'hold_mode'})
	{
		if ($windowrec->view()->{'bottom'})
		{
			$windowrec->view()->set_bookmark_bottom('mh_hold_mode');

			if ($more)
			{
				$windowrec->view()->scroll($more);
				window_refresh();
			}

		} else
		{
			window_refresh();
		}
	}
}

sub signal_window_changed_last
{
	my ($windowrec_new, $windowrec_old) = @_;

	if (config_window_get($windowrec_new)->{'hold_mode'})
	{
		window_refresh();
	}
}

sub signal_print_text_last
{
	my ($textdest, $text, $stripped) = @_;

	my $windowrec = Irssi::active_win();
	my $window    = config_window_get($windowrec);

	if ($textdest->{'window'}->{'refnum'} == $windowrec->{'refnum'} and config_window_get($windowrec)->{'hold_mode'} and $more <= $windowrec->{'height'})
	{
		window_refresh();
	}
}

##############################################################################
#
# irssi command functions
#
##############################################################################

sub command_hold_mode
{
	my ($data, $server, $windowitem) = @_;

	my $windowrec  = Irssi::active_win();
	my $window     = config_window_get($windowrec);
	my $showstatus = 1;

	if (defined($data))
	{
		$data = trim_space($data);

		if ($data ne '')
		{
			if ($data =~ m/^toggle$/i)
			{
				if (not $window->{'hold_mode'})
				{
					$window->{'hold_mode'} = 1;

				} else
				{
					$window->{'hold_mode'} = 0;
				}

			} elsif ($data =~ m/^on$/i)
			{
				$window->{'hold_mode'} = 1;
				$windowrec->view()->set_bookmark_bottom('mh_hold_mode');
				window_refresh();

			} elsif ($data =~ m/^off$/i)
         	{
            	$window->{'hold_mode'} = 0;
				$windowrec->view()->set_bookmark_bottom('mh_hold_mode');
				window_refresh();

			} elsif ($data =~ m/^flush$/i)
			{
				$showstatus = 0;
				$windowrec->view()->set_bookmark_bottom('mh_hold_mode');
				window_refresh();

			} elsif ($data =~ m/^scroll_always(.*)$/i)
			{
				$showstatus = 0;

				if (defined($1))
				{
					$data = trim_space($1);

					if ($data =~ m/^toggle$/i)
					{
						$data = (($window->{'scroll_always'}) ? 'off' : 'on' );
					}

					if ($data =~ m/^on$/i)
					{
						$window->{'scroll_always'} = 1;

					} elsif ($data =~ m/^off$/i)
					{
						$window->{'scroll_always'} = 0;
					}
				}

				$windowrec->print('hold_mode (' . (($window->{'hold_mode'}) ? 'on' : 'off' ) . '): scroll_always is ' . (($window->{'scroll_always'}) ? 'on' : 'off' ), Irssi::MSGLEVEL_CLIENTCRAP);
			}
		}
	}

	if ($showstatus)
	{
		$windowrec->print('hold_mode is ' . (($window->{'hold_mode'}) ? 'on' : 'off' ), Irssi::MSGLEVEL_CLIENTCRAP);
	}
}

sub command_help
{
	my ($data, $server, $windowitem) = @_;

	$data = lc(trim_space($data));

	if ($data =~ m/^hold_mode$/i)
	{
		Irssi::print('', Irssi::MSGLEVEL_CLIENTCRAP);
		Irssi::print('HOLD_MODE %|[on|off|toggle|flush]', Irssi::MSGLEVEL_CLIENTCRAP);
		Irssi::print('HOLD_MODE %|scroll_always [on|off|toggle]', Irssi::MSGLEVEL_CLIENTCRAP);
		Irssi::print('', Irssi::MSGLEVEL_CLIENTCRAP);
		Irssi::print('%|Show/enable/disable/toggle/flush status of window hold_mode or show/enable/disable/toggle the scroll_always setting.', Irssi::MSGLEVEL_CLIENTCRAP);
		Irssi::print('', Irssi::MSGLEVEL_CLIENTCRAP);

		Irssi::signal_stop();
	}
}

##############################################################################
#
# statusbar item handlers
#
##############################################################################

sub statusbar_more
{
	my ($statusbaritem, $get_size_only) = @_;

	my $window = Irssi::active_win();

	my $local_more = 0;

	$local_more = ($window->view()->{'ypos'} - $window->view()->{'height'} + $window->view()->{'empty_linecount'} + 1);
	$more       = $local_more;

	if ($more)
	{
		my $format = "{sb $more more}";

		if (Irssi::settings_get_bool('mh_hold_mode_more_oldstyle'))
		{
				$format = " -- $more more --";
		}

		$statusbaritem->default_handler($get_size_only, $format, '', 0);

	} else
	{
		$statusbaritem->default_handler($get_size_only, '', '', 0);
	}
}

##############################################################################
#
# script on load
#
##############################################################################

Irssi::settings_add_bool('mh_hold_mode', 'mh_hold_mode_more_oldstyle',         0);
Irssi::settings_add_bool('mh_hold_mode', 'mh_hold_mode_default_hold_mode',     0);
Irssi::settings_add_bool('mh_hold_mode', 'mh_hold_mode_default_scroll_always', 1);

Irssi::signal_add_last('window created',   'config_window_get');
Irssi::signal_add_last('window destroyed', 'config_window_del');
Irssi::signal_add_first('key send_line',   'signal_key_send_line_first');
Irssi::signal_add_last('key send_line',    'signal_key_send_line_last');
Irssi::signal_add_last('window changed',   'signal_window_changed_last');
Irssi::signal_add_last('print text',       'signal_print_text_last');

Irssi::command_bind('hold_mode', 'command_hold_mode', 'mh_hold_mode');
Irssi::command_bind('help',      'command_help');

Irssi::statusbar_item_register('mh_sbmore', '', 'statusbar_more');
Irssi::statusbars_recreate_items();

Irssi::command('^SET SCROLL ON');

for my $window (Irssi::windows)
{
	$window->view()->set_scroll(1);
	$window->view()->set_bookmark_bottom('mh_hold_mode');
	config_window_get($window);
}

statusbar_more_redraw();
Irssi::command('^REDRAW');

Irssi::timeout_add(1000, 'statusbar_more_redraw', undef);

1;

##############################################################################
#
# eof mh_hold_mode.pl
#
##############################################################################
