###############################################################################
# 
# 差分を表示するプラグイン
# 
###############################################################################
package plugin::core::Diff;
use Algorithm::Diff qw(traverse_sequences);
use strict;

#==============================================================================
# コンストラクタ
#==============================================================================
sub new {
	my $class = shift;
	my $self = {};
	
	return bless $self,$class;
}

#==============================================================================
# アクションの実行
#==============================================================================
sub do_action {
	my $self = shift;
	my $wiki = shift;
	my $cgi = $wiki->get_CGI;
	
	my $pagename = $cgi->param("page");
	if($pagename eq ""){
		$pagename = $wiki->config("frontpage");
	}
	unless($wiki->can_show($pagename)){
		return $wiki->error("参照権限がありません。");
	}
	if($cgi->param('rollback') ne ''){
		return $self->rollback($wiki, $pagename, $cgi->param('rollback'));
	} elsif($wiki->{storage}->backup_type eq 'all'){
		if($cgi->param('generation') eq '' && $cgi->param('diff') eq ''){
			return $self->show_history($wiki, $pagename);
		} else {
			if($cgi->param('generation') ne ''){
				return $self->show_diff($wiki, $pagename, '', $cgi->param('generation'));
			}
			return $self->show_diff($wiki, $pagename, $cgi->param('from'), $cgi->param('to'));
		}
	} else {
		return $self->show_diff($wiki, $pagename, 0);
	}
}

#==============================================================================
# 履歴からページを復元
#==============================================================================
sub rollback {
	my $self = shift;
	my $wiki = shift;
	my $page = shift;
	my $gen  = shift;
	unless($wiki->can_modify_page($page)){
		return $wiki->error("更新権限がありません。");
	}
	my $source = $wiki->get_backup($page,$gen);
	$wiki->save_page($page, $source);
	return $wiki->redirect($page);
}

#==============================================================================
# 履歴の一覧を表示
# ストレージのbackup_type=allのとき
#==============================================================================
sub show_history {
	my $self = shift;
	my $wiki = shift;
	my $pagename = shift;
	
	$wiki->set_title($pagename."の変更履歴");
	my $buf   = "<form><ul>\n";
	my $count = 0;
	my @list  = $wiki->{storage}->get_backup_list($pagename);
	
	if($#list == -1){
		return "履歴はありません。";
	}
	
	foreach my $time (@list){
		$buf .= "<li>";
		if($count == 0){
			$buf .= "<input type=\"radio\" name=\"from\" value=\"\" checked>".
			        "<input type=\"radio\" name=\"to\" value=\"\" checked>";
		} else {
			$buf .= "<input type=\"radio\" name=\"from\" value=\"".($#list-$count+1)."\">".
			        "<input type=\"radio\" name=\"to\" value=\"".($#list-$count+1)."\">";
		}
		$buf .= "<a href=\"".$wiki->create_url({ action=>"DIFF",page=>$pagename,generation=>($#list-$count) })."\">".&Util::escapeHTML($time).
		        "</a>　<a href=\"".$wiki->create_url({ action=>"SOURCE",page=>$pagename,generation=>($#list-$count) })."\">ソース</a>".
		        "</li>\n";
		$count++;
	}
	return $buf."</ul>".
	"<input type=\"hidden\" name=\"page\" value=\"".Util::escapeHTML($pagename)."\">".
	"<input type=\"hidden\" name=\"action\" value=\"DIFF\">".
	"<input type=\"submit\" name=\"diff\" value=\"選択したリビジョン間の差分を表示\"></form>\n";
}

#==============================================================================
# 差分を表示
#==============================================================================
sub show_diff {
	my $self     = shift;
	my $wiki     = shift;
	my $pagename = shift;
	my $from     = shift;
	my $to       = shift;
	
	$wiki->set_title($pagename."の変更点");
	my ($diff, $rollback) = $self->get_diff_html($wiki,$pagename, $from, $to);
	
	$diff =~ s/\n/<br>/g;
	
	my $buf = qq|
		<ul>
		  <li>追加された行は<ins class="diff">このように</ins>表示されます。</li>
		  <li>削除された行は<del class="diff">このように</del>表示されます。</li>
		</ul>
		<div class="diff">$diff</div>
	|;
	
	if($wiki->can_modify_page($pagename) && $rollback && $wiki->get_CGI->param('diff') eq ''){
		$buf .= qq|
			<form action="@{[$wiki->create_url()]}" method="POST">
				<input type="submit" value="このバージョンに戻す"/>
				<input type="hidden" name="action" value="DIFF"/>
				<input type="hidden" name="page" value="@{[Util::escapeHTML($pagename)]}"/>
				<input type="hidden" name="rollback" value="@{[Util::escapeHTML($from)]}"/>
			</form>
		|;
	}
	
	return $buf;
}

#==============================================================================
# 差分文字列を取得
#==============================================================================
sub get_diff_text {
	my $self       = shift;
	my $wiki       = shift;
	my $pagename   = shift;
	my $generation = shift;
	
	my $source1 = $wiki->get_page($pagename);
	my $source2 = $wiki->get_backup($pagename,$generation);
	my $format  = $wiki->get_edit_format();
	
	$source1 = $wiki->convert_from_fswiki($source1,$format);
	$source2 = $wiki->convert_from_fswiki($source2,$format);
	
	my $diff_text = "";
	my @msg1 = split(/\n/,$source1);
	my @msg2 = split(/\n/,$source2);
	my $msgrefA = \@msg2;
	my $msgrefB = \@msg1;
	
	traverse_sequences($msgrefA, $msgrefB,
		{
			MATCH => sub {},
			DISCARD_A => sub {
				my ($a, $b) = @_;
				$diff_text .= "-".$msgrefA->[$a]."\n";
			},
			DISCARD_B => sub {
				my ($a, $b) = @_;
				$diff_text .= "+".$msgrefB->[$b]."\n";
			}
		});
	
	return $diff_text;
}

#==============================================================================
# 差分文字列を表示用HTMLとして取得
#==============================================================================
sub get_diff_html {
	my $self     = shift;
	my $wiki     = shift;
	my $pagename = shift;
	my $from     = shift;
	my $to       = shift;
	
	my $source1 = '';
	if($from ne ''){
		$source1 = $wiki->get_backup($pagename, $from);
	} else {
		$source1 = $wiki->get_page($pagename);
	}
	my $source2 = '';
	if($to ne ''){
		$source2 = $wiki->get_backup($pagename, $to);
	} else {
		$source2 = $wiki->get_page($pagename);
	}
	my $format  = $wiki->get_edit_format();
	
	$source1 = $wiki->convert_from_fswiki($source1, $format);
	$source2 = $wiki->convert_from_fswiki($source2, $format);
	
	my $diff_text = "";
=pod
	my @msg1 = split(/\n/,$source1);
	return "ページが大きすぎるため差分を表示できません。" if($#msg1 >= 999);
	my @msg2 = split(/\n/,$source2);
	return "ページが大きすぎるため差分を表示できません。" if($#msg2 >= 999);
=cut
	my @msg1 = _str_jfold($source1, 1);
	my @msg2 = _str_jfold($source2, 1);
	
	my $msgrefA = \@msg2;
	my $msgrefB = \@msg1;
	
	traverse_sequences($msgrefA, $msgrefB,
		{
			MATCH => sub {
				my ($a, $b) = @_;
				$diff_text .= Util::escapeHTML($msgrefA->[$a]);
			},
			DISCARD_A => sub {
				my ($a, $b) = @_;
				$diff_text .= "<del class=\"diff\">".Util::escapeHTML($msgrefA->[$a])."</del><wbr>";
			},
			DISCARD_B => sub {
				my ($a, $b) = @_;
				$diff_text .= "<ins class=\"diff\">".Util::escapeHTML($msgrefB->[$b])."</ins><wbr>";
			}
		});
	
	return ($diff_text, $source2 ne "");
}

#==============================================================================
# 文字列を指定文字数を分割
#==============================================================================
sub _str_jfold {
  my $str    = shift;       #指定文字列
  my $byte   = shift;       #指定バイト
  my $j      = new Jcode($str);
  my @result = ();

  foreach my $buff ( $j->jfold($byte) ){
    push(@result, $buff);
  }

  return(@result);
}

#==============================================================================
# ページ表示時のフックメソッド
# 「差分」メニューを有効にします
#==============================================================================
sub hook {
	my $self = shift;
	my $wiki = shift;
	my $cgi  = $wiki->get_CGI;
	
	my $pagename = $cgi->param("page");
	$wiki->add_menu("差分",$wiki->create_url({ action=>"DIFF",page=>$pagename }));
}

1;
