/*

  This module is useful to track a nick's presence on irc, using the watch
command that most ircds implement. It logs the login/logout time, ircname
and irc server that the person used. To set a watch on some nick, use:

"watch logname nick"

where <logname> is a logging facility created previously by logcreate, and
<nick> is the nick you want to keep an eye on.

*/

#include "mbot.h"

#define SOURCE s->script.source
#define DEST s->script.dest
#define BUF s->script.buf

struct watch_type {
  CNetServer *s;
  CString nick;
  CLog *log;
  watch_type (CNetServer *server, c_char n, CLog *l) : s (server),
    nick (n, NICK_SIZE), log (l) {}
};
CList *watch_list;

///////////////
// prototypes
///////////////

static void watch_add (CNetServer *, c_char, CLog *);
static CLog *server_nick2log (CNetServer *, c_char);
static void watch_reply (CNetServer *);
static void watch_conf (CNetServer *, c_char);
static void watch_stop (CModule *);
static void watch_start (CModule *);

////////////////////
// module managing
////////////////////

// add a server to the watch list
static
void watch_add (CNetServer *s, c_char nick, CLog *log)
{
  watch_type *watch = new watch_type (s, nick, log);
  if (watch == NULL)
    s->bot->conf_error ("error initializing watch");
  watch_list->add ((void *)watch);
}

// return the log for a given server/nick, NULL if nonexistant
static
CLog *server_nick2log (CNetServer *s, c_char nick)
{
  watch_type *watch;
  watch_list->rewind ();
  while ((watch = (watch_type *)watch_list->next ()) != NULL)
    if (watch->s == s && (watch->nick |= nick))
      return watch->log;
  return NULL;
}

static void
watch_reply (CNetServer *s)
{
  // init
  if (strcmp (CMD[1], "005") == 0)
    {
      watch_type *watch;
      watch_list->rewind ();
      while ((watch = (watch_type *)watch_list->next ()) != NULL)
        if (watch->s == s)
          s->irc_watch (watch->nick, 1);
    }

  CLog *log = server_nick2log (s, CMD[3]);
  if (log == NULL)
    return;

  // watch
  if ((strcmp (CMD[1], RPL_WATCHONLINE) == 0)
           || (strcmp (CMD[1], RPL_WATCHSTARTONLINE) == 0))
    {
      *log << "Signon " << CMD[3] << '!' << CMD[4] << '@' << CMD[5] << EOL;
      s->irc_whois (CMD[3]);
    }
  else if (strcmp (CMD[1], RPL_WATCHOFFLINE) == 0)
    *log << "Signoff " << CMD[3] << '!' << CMD[4] << '@' << CMD[5] << EOL;
  else if (strcmp (CMD[1], RPL_WATCHSTOP) == 0)
    s->irc_watch (CMD[3], 1);

  // whois
  else if (strcmp (CMD[1], RPL_AWAY) == 0)
    *log << CMD[3] << " :" << CMD[4] << EOL;
  else if (strcmp (CMD[1], RPL_WHOISUSER) == 0)
    *log << CMD[3] << ' ' << CMD[4] << ' ' << CMD[5] << ' ' << CMD[6] << " :"
         << CMD[7] << EOL;
  else if (strcmp (CMD[1], RPL_WHOISSERVER) == 0)
    *log << CMD[3] << ' ' << CMD[4] << " :" << CMD[5] << EOL;
  else if (strcmp (CMD[1], RPL_WHOISOPERATOR) == 0)
    *log << CMD[3] << " :" << CMD[4] << EOL;
  else if (strcmp (CMD[1], RPL_WHOISIDLE) == 0)
    *log << CMD[3] << ' ' << CMD[4] << ' '<< CMD[5] << " :" << CMD[6] << EOL;
  else if (strcmp (CMD[1], RPL_WHOISCHANNELS) == 0)
    *log << CMD[3] << " :" << CMD[4] << EOL;
}

// configuration file's local parser
static void
watch_conf (CNetServer *s, const char *bufread)
{
  char buf[4][MSG_SIZE+1];

  strsplit (bufread, buf, 3);

  if (strcasecmp (buf[0], "watch") == 0)
    {
      CLog *log = s->bot->log_get (buf[1]);
      if (log == NULL)
        s->bot->conf_error ("inexistant logname");
      bool first = 1;	// if it's the first log for this server
      watch_type *watch;
      watch_list->rewind ();
      while ((watch = (watch_type *)watch_list->next ()) != NULL)
        if (watch->s == s)
          {
            first = 0;
            break;
          }
      if (first)
        s->script.replies.add ((void *)watch_reply);
      watch_add (s, buf[2], log);
    }
}

// module termination
static void
watch_stop (CModule *m)
{
  watch_type *watch;
  watch_list->rewind ();
  while ((watch = (watch_type *)watch_list->next ()) != NULL)
    {
      watch->s->script.replies.del ((void *)watch_reply);
      delete watch;
    }
  delete watch_list;
}

// module initialization
static void
watch_start (CModule *m)
{
  watch_list = new CList ();
}

struct CModule::module_type module = {
  MODULE_VERSION,
  "watch",
  watch_start,
  watch_stop,
  watch_conf,
  NULL
};
