#!/usr/bin/env perl

#
# Script taken from InspIRCd, https://www.inspircd.org/
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#


BEGIN { require 5.8.0; }

use strict;
use warnings FATAL => qw(all);

use File::Copy ();
use Cwd;

sub list_extras ();
sub enable_extras (@);
sub disable_extras (@);

# Routine to list out the extra/ modules that have been enabled.
# Note: when getting any filenames out and comparing, it's important to lc it if the
# file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
# (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
# configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
sub list_extras () {
	use File::Spec;
	# @_ not used
	my $srcdir = File::Spec->catdir("modules");
	my $abs_srcdir = File::Spec->rel2abs($srcdir);
	local $_;
	my $dd;
	opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
	my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
	closedir $dd;
	undef $dd;
	opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
	my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
	closedir $dd;
	undef $dd;
	my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
	my %extras = ();
EXTRA:	for my $extra (@extras) {
		next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
		my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
		my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
		next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
		if (-l $abs_source) {
			# Symlink, is it in the right place?
			my $targ = readlink($abs_source);
			my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
			if ($abs_targ eq $abs_extra) {
				$extras{$extra} = "\e[32;1menabled\e[0m";
			} else {
				$extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
			}
		} elsif (-e $abs_source) {
			my ($devext, $inoext) = stat($abs_extra);
			my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
			if ($lnksrc > 1) {
				if ($devsrc == $devext && $inosrc == $inoext) {
					$extras{$extra} = "\e[32;1menabled\e[0m";
				} else {
					$extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
				}
			} else {
				open my $extfd, "<", $abs_extra;
				open my $srcfd, "<", $abs_source;
				local $/ = undef;
				if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
					$extras{$extra} = "\e[32;1menabled\e[0m";
				} else {
					$extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
				}
			}
		} else {
			$extras{$extra} = "\e[33;1mdisabled\e[0m";
		}
	}

	for my $extra (sort {$a cmp $b} keys(%extras)) {
		my $text = $extras{$extra};
		if ($text =~ m/needed by/ && $text !~ m/enabled/) {
			printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
		} else {
			printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
		}
	}
	return keys(%extras) if wantarray; # Can be used by manage_extras.
}

sub enable_extras (@) {
	my (@extras) = @_;
	for my $extra (@extras) {
		my $extrapath = "modules/extra/$extra";
		if (!-e $extrapath) {
			print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in modules/extra\n";
			next;
		}
		my $source = "modules/$extra";
		if (-e $source) {
			print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in modules exists (might already be enabled?)\n";
			next;
		}
		print "Enabling $extra ... \n";
		symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
	}
}

sub disable_extras (@)
{
	opendir my $dd, "modules/extra/";
	my @files = readdir($dd);
	closedir $dd;
	my (@extras) = @_;
EXTRA:	for my $extra (@extras) {
		my $extrapath = "modules/extra/$extra";
		my $source = "modules/$extra";
		if (!-e $extrapath) {
			print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
			next;
		}
		if ((! -l $source) || readlink($source) ne "extra/$extra") {
			print STDERR "Cannot disable \e[32;1m$extra\e[0m : Source is not a link or doesn't refer to the right file. Remove manually if this is in error.\n";
			next;
		}
		# Now remove.
		print "Disabling $extra ... \n";
		unlink "modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
	}
}

my $clearscreen = `clear`;
print $clearscreen;
while (1)
{
	list_extras;     # print the module list
	print "\nPlease enter the name of the module or type 'q' to quit.: ";
	my $input = <STDIN>;
	chop($input); # remove the trailing \n from the user input

	if ($input eq "q") {
		if (-e "build/CMakeFiles") {
			system("cmake", "build/.");
			print "\nNow cd build, then run make to build Anope.\n\n";
		} else {
			print "\nBuild directory not found. You should run ./Config now.\n\n"
		}
		exit 0;
	}
	print $clearscreen;
	if ($input eq "") {
		next;
	}

	if (-e "modules/$input") {
		disable_extras($input)
	} else {
		enable_extras($input)
	}
}
