#!/usr/bin/perl
use IO::Socket;
use Data::Dumper;

use cybot;

$debug = 1;

$bot = cybot::new("config");
#print Dumper($bot) if ($debug);


$version = $cybot::VERSION;

$userlistfile = "userlist.txt";

&read_userlist();

$bot->connect();

$SIG{INT}  = \&handle_sigint;
$SIG{USR1} = \&handle_sigusr1;
$SIG{ALRM} = \&handle_alrm;

my $fulldate;
{
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  $year+=1900;
  $mon++;
  $fulldate = "$year$mon$mday.$hour$min";
}

open $log, ">$fulldate.irc.log" or die "Cannot open log file";
select $log;
$|=1;
select STDOUT;
#
# Main loop
#
my $sock = $bot->{sock};
$bot->{log}=$log;
alarm $bot->{ticksize};
while ($line = <$sock>) {
  chomp($line);
  $line =~ s/\r$//;

  print {$log} $line,"\n";

  if ($line =~ /^PING :(.+)/) {
    print $sock "PONG :$1\r\n"; 
    print STDOUT "PONG :$1\n" if $debug;
  }

  ## parse incoming stuff
  if ($line =~ /:(.+)!(.+\@.+) PRIVMSG ([^:]+) :(.+)/) {
    my ($nick, $hostmask, $chan, $message) = ($1, $2, $3, $4);

    ## see if it is a private message
    if ($chan eq $bot->{nick}) {
      $chan = $nick;
      $priv = 1;
    } else {
      $priv = 0;
    }

    # Check an action
    if ($message =~ /^\x01ACTION[:]*\s*([^\s]+)\s*(.*)\x01/) {
      ($part1,$part2) = (lc($1), $2);
      $action = "$1 $2";
      print "* $nick $action\n";

      $bot->message($Monitor::montochan, "* $nick $action") if $Monitor::monchan eq $chan;

      if ($Triggers::reactions{$part1}) {
	$reaction = $Triggers::reactions{$part1};
	$reaction =~ s/<NICK>/$nick/g;
	$reaction =~ s/<CHAN>/$chan/g;
	$reaction =~ s/<ARGS>/$part2/g;

	# num1
	@reactions = split("\\\\n", $reaction);
	for (0..$#reactions) {
	  #$bot->message($chan, $reactions[1]);
	  if ($reactions[$_] =~ /^me (.*)/) {
	    $bot->action($chan, $1);
	  } else {
	    $bot->message($chan, $reactions[$_]);
	  }
	}
      }
    } elsif ($message =~ /^\x01VERSION\x01$/) {
      print STDOUT "--- Received a CTCP VERSION from $nick\n";
      print $sock "NOTICE $nick :VERSION CyBoT $version\r\n";

    } elsif ($message =~ /^\x01PING ([0-9]+)\x01$/) {
      print "--- Received a CTCP PING $1 from $nick.\n";
      printf $sock ("NOTICE $nick :%cPING $1%c\r\n", 1, 1);

    } elsif ($message =~ /^\x01TIME\x01$/) {
      print "--- Received a CTCP TIME from $nick\n";
      $date = `date`;
      printf $sock ("NOTICE $nick :%cTIME :$date%c\r\n", 1, 1);

    } else {
      if ($priv == 1) {
	print STDOUT "*$nick* $message\n";
      } else {
	print STDOUT "<$nick> $message\n";
	if ($Monitor::monchan eq $chan) {
	  $bot->message($Monitor::montochan, "<$nick> $message");
	}
      }
      if ($priv == 0 && $message =~ /^$bot->{nick} [,;:]\s*(.*)/ix) {
	&command($nick, $hostmask, $chan, $1);
      } elsif ($priv == 1) {
	&command($nick, $hostmask, $chan, $message);
      } else {
	if (exists($bot->{triggers}{$chan})) {
	  my @triggers = @{$bot->{triggers}{$chan}};
	  for (@triggers) {
	    &{$_}($bot,$nick,$hostmask,$chan,$message);
	  }
	}
	# Message from user to user...
      }
    }
  } elsif ($line =~ /:(.+)!(.+)\@.+ NOTICE ([^:]+) :(.+)/) {
    my ($nick, $chan, $message) = ($1,$3,$4);
    if ($message =~ /^\x01\s*PING ([0-9]+)/ && $Misc::pinged == 1) {
      &parse_ping_reply($nick, $Misc::ctcpchan, $1);
    } else {
      # if ($message =~ /^\x01(.*)\x01(.*)/){
      # print STDOUT "-$nick- ^A $1 ^A $2\n";
      # } else {
      print STDOUT "-$nick- $message\n";
      # }
    }

  } elsif ($line =~ /:(.+)!(.+) (PART|JOIN) [:]*([^:\r]+)/) {
    my ($nick, $userhost, $action, $chan) = ($1,$2,$3,$4);
    if ($action eq "JOIN") {
      print STDOUT "--> "; $action = "joined";
    } else {
      print STDOUT "<-- "; $action = "left";
    }
    print STDOUT "$nick ($userhost) has $action $chan.\n";
    @user = split("@", $userhost);
    $nick =~ y/A-Z/a-z/;
    if ($action eq "joined") {
      $userhosts{$nick} = $user[1];
      if (&get_userlevel($nick, $user[1]) >= 25) {
	print $sock "MODE $chan +o $nick\r\n";
      }
      #print $sock "PRIVMSG $chan :Found $user[1] for $nick.\r\n";
    } else {
      #  delete($userhosts{$nick});
    }

  } elsif ($line =~ /:(.+)!(.+) QUIT :(.+)/) {
    $nick = $1; $userhost = $2; $reason = $3;
    print STDOUT "<-- $nick has quit ($reason)\n";
    $nick =~ y/A-Z/a-z/; delete($userhosts{$nick});

  } elsif ($line =~ /:(.+)!.+\@.+ TOPIC ([^:]+) :(.*)/) {
    my $chan = $2; $chan =~ y/A-Z/a-z/;
    print STDOUT "-($2)-- $1 has changed the topic to: $3\n";
    $Misc::topics{$2} = $3;

  } elsif ($line =~ /:(.+)!.+\@.+ NICK :(.+)/) {
    print STDOUT "--- $1 is now known as $2\n";
    $oldnick = $1; $oldnick =~ y/A-Z/a-z/;
    $newnick = $2; $newnick =~ y/A-Z/a-z/;
    $userhosts{$newnick} = $userhosts{$oldnick};

    #if ($userlist{$newnick} > 0){
    #return 0;
    #} else {

    $userlist{$newnick} = $userlist{$oldnick};
    #}
    delete($userhosts{$oldnick});
    #delete($userlist{$oldnick});

  } elsif ($line =~/:(.+)!.+\@.+ KICK (#.+) ([^:]+) :(.*)/) {
    delete($userhosts{$nick});
    print STDOUT "<-- $1 has kicked $3 from $2 ($4)\n";
    if ($3 eq $bot->{nick}) {
      $bot->cjoin($2); $retaliate{"$2"} = "kick:$1";
    } 

  } elsif ($line =~ /:(.+)!.+\@.+ MODE (#.+) (\+[a-z]+|\-[a-z]+)\s*([^\s]*)/i) {
    $nick = $1; $chan = $2; $mode = $3; $vic = $4; 
    print STDOUT "--- $nick sets modes [$chan $mode";
    if ($vic) {
      print STDOUT " $vic";
    }
    print STDOUT "]\n";
    if ($mode eq "+o" && $vic eq $bot->{nick}) {
      &retaliate($chan);
    }
    if ($mode eq "-o" && $vic eq $bot->{nick}) {
      $retaliate{$chan} = "deop:$nick";
    }

  } elsif ($line =~ /:(.+)!.+\@.+ INVITE [^:]+ :(.+)/) {
    print STDOUT "--- $1 invited you to $2\n";
    $bot->cjoin($2);
  } elsif ($line =~ /:[^\s]+ 311 $bot->{nick} ([^\s]+) [^\s]+ ([^\s]+) .*/) {

    # print $sock "PRIVMSG #testfu :found $1 ($2)\r\n";
    $nick = $1; $nick =~ y/A-Z/a-z/;
    $userhosts{$nick} = $2;

    # print $sock "PRIVMSG #testfu :set \$userhosts{\"$1\"} to $userhosts{$1}\r\n";
  } elsif ($line =~ /:[^\s]+ 352 $bot->{nick} #([^\s]+) [^\s]+ ([^\s]+) [^\s]+ ([^\s]+) .*/) {

    $nick = $3; $nick =~ y/A-Z/a-z/;
    print LOG "found $nick ($2) on channel $1 \n";
    $userhosts{$nick} = $2;

  } elsif ($line =~ /:[^\s]+ 332 $bot->{nick} ([^\s]+) :(.*)/) {
    my $chan = $1; $chan =~ y/A-Z/a-z/;
    print STDOUT "--- Topic for $1 is '$2'\n";
    $Misc::topics{$chan} = $2;
  } elsif ($line =~ /^:[^\s]+ 004/) {
    $bot->init_conn();
  }
  ## insert new parsing stuff here
}


####### IT ENDS HERE ###########

sub command{
  my ($nick, $userhost, $chan, $command) = @_;
  print STDOUT $userhost;
  $bot->{chost} = $userhost;
  if ($command =~ /^level ([^\s]+)/) {
    &cmd_level($1, $chan);
  } elsif ($command =~ /^reload/) {
    &cmd_reload();
  } elsif ($command =~ /^adduser ([^\s]+) ([^\s]+)/) {
    &cmd_adduser($1, $2, $chan);
  } elsif ($command =~ /(?:^cycle$|^cycle\s+(.+)$)/) {
    if ($1) {
      $chan = $1;
    }
    &cmd_cycle($chan);
  } else {
    my @args = split /\s+/, $command;
    my $t;
    if (defined($t = $bot->{commands}->{$args[0]})) {
      my $com = shift @args;
      &{$bot->{commands}{$com}}($bot,$chan,$nick,$userhost,@args);
    }
  }
  $bot->{chost} = "";
}


sub parse_ping_reply{
  my $sock = $bot->{sock};
  my ($nick, $chan, $pingstamp) = @_;
  $timestamp = time;
  $diff = $timestamp - $pingstamp;
  if ($diff == 0) {
    $diff = "< 1"; $seconds = "second";
  } elsif ($diff == 1) {
    $seconds = "second";
  } else {
    $seconds = "seconds";
  }
  print STDOUT "--- Ping Reply from $nick: $diff $seconds\n";
  print $sock  "NOTICE $Misc::ctcpchan :$nick: Your ping reply: $diff $seconds\r\n";
  $Misc::pinged = 0;
}

sub retaliate{
  my $sock = $bot->{sock};
  my ($chan) = @_;
  @foo = split(":", $retaliate{$chan});
  $type = $foo[0]; $nick = $foo[1];
  if ($retaliate{"$chan"} ne "" && $nick ne $bot->{nick}) {
    if ($type eq "kick") {
      print $sock "KICK $chan $nick :How VERY rude!\r\n";
      $retaliate{$chan} = "";
    } elsif ($type eq "deop") {
      print $sock "MODE $chan -o $nick\r\n";
      $retaliate{$chan} = "";
    }
  }
}

sub read_userlist{
  %userlist = (); $numusers = 0;
  open (LIST, $userlistfile);
  while ($listline = <LIST>) {
    chomp ($listline);
    # print STDOUT $listline."\n";
    if ($listline =~ /^#.*/) {
      next;
    } elsif ($listline =~ /([^\s]+)\s(.+)/) {
      $nick = $1; $nick =~ y/A-Z/a-z/;
      $userlist{$nick} = $2; $numusers++;
    }
  }
  close(LIST);
  return $numusers;
}

sub write_userlist{
  open (LIST, ">$userlistfile");
  print LIST "#CyBoT userlist.\n";
  while (my ($key, $val) = each(%userlist)) {
    if ($key eq "" || $val eq "") {
      next;
    }
    $key =~ y/A-Z/a-z/;
    print LIST "$key $val\n";
  }
  close (LIST);
}


sub cmd_level{
  my $sock = $bot->{sock};
  my ($nick, $chan) = @_;
  $nick =~ y/A-Z/a-z/;
  if (!$userhosts{$nick}) {
    print $sock "PRIVMSG $chan :I don't know about $nick.\r\n"; return 0;
  }
  my $level = &get_userlevel($nick, $userhosts{$nick});
  if ($level == 0) {
    $bot->message ($chan, "$nick has level 0");
  } else {
    my @nickhostname = split(":", $userlist{$nick});
    if ($userhosts{$nick} =~ /[0-9.]+/) {
      $bot->message($chan,"$nick ($nickhostname[0]*) has level $level");
    } else {
      $bot->message($chan,"$nick (*$nickhostname[0]) has level $level");
    }
  }
}

sub cmd_reload{
  $users = &read_userlist();
  $bot->message("#testfu", "$users users loaded.");
}

sub cmd_adduser{
  my ($nick, $level, $chan) = @_;
  $nick =~ y/A-Z/a-z/;
  if (!$userhosts{$nick}) {
    $bot->message($chan, "No such user in any channel I'm in!");
    return 0;
  } else {
    if ($userhosts{$nick} =~ /^([0-9]{1,3}\.[0-9]{1,3}\.)[0-9]{1,3}\.[0-9]{1,3}/) {
      $hostpart = $1; $ipaddr = 1;
    } elsif ($userhosts{$nick} =~ /^[^.]+(\..*)/) {
      $hostpart = $1; $ipaddr = 0;
    }
    ;
    $userlist{$nick} = "$hostpart:$level";
    if ($ipaddr == 1) {
      $bot->message($chan, "Adding $nick ($hostpart*) with level $level");
    } else {
      $bot->message($chan, "Adding $nick (*$hostpart) with level $level");
    }
    &write_userlist();
  }
}

sub cmd_cycle{
  my $sock = $bot->{sock};
  my ($chan) = @_;
  print $sock "PART $chan\r\nJOIN $chan\r\n";
}

sub quit{
  my $sock = $bot->{sock};
  my ($reason) = @_;
  print $sock "QUIT :$reason\r\n";
  exit;
}

sub handle_sigint{
  my $sock = $bot->{sock};
  print STDOUT "SIGINT Caught.\n";
  printf $sock ("QUIT :Caught signal SIGINT.\r\n");
  #exit;
}

sub handle_sigusr1{
  print STDOUT "--- Received command to rehash ircd.\n";
  print STDOUT "--- Not doing it though.\n"
}

sub handle_alrm {
  my @subs = @{$bot->{ticks}};
  print STDOUT "tick\n";
  for (@subs) {
    &{$_}($bot);
  }
  alarm $bot->{ticksize};
}
