/*

  This module logs channels and/or privmsgs from certain nicks to the bot.
It uses a previously created logname to write to.

To log a channel into <logname>:
"log logname #channel"

To log privmsgs from <nick>:
"log logname nick"

If you want to log ALL privmsgs with the bot:
"log logname <privmsg>"

*/

#include "mbot.h"

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

struct log_type {
  CNetServer *s;
  CString what;				// channel or nick to log
  CLog *log;				// where to log
  log_type (CNetServer *server, c_char w, CLog *l) : s (server),
    what (w, CHANNEL_SIZE), log (l) {}
};
CList *log_list;

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

static void log_add (CNetServer *, c_char, CLog *);
static CLog *server_what2log (CNetServer *, c_char);
static void log_event (CNetServer *);
static void log_conf (CNetServer *, c_char);
static void log_stop (CModule *);
static void log_start (CModule *);

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

// add a server to the logs list
static void
log_add (CNetServer *s, c_char what, CLog *l)
{
  log_type *log;
  log = new log_type (s, what, l);
  if (log == NULL)
    s->bot->conf_error ("error initializing log");
  log_list->add ((void *)log);
}

// return the log for a given server/destination, NULL if nonexistant
static CLog *
server_what2log (CNetServer *s, c_char what)
{
  log_type *log;
  log_list->rewind ();
  while ((log = (log_type *)log_list->next ()) != NULL)
    if (log->s == s && (log->what |= what))
      return log->log;
  return NULL;
}

// events watcher, to log most of them
static void
log_event (CNetServer *s)
{
  char buf[MSG_SIZE+1];
  int i;
  CLog *log;

  if (strcmp (CMD[1], "JOIN") == 0)
    {
      log = server_what2log (s, CMD[2]);
      if (log != NULL)
        *log << CMD[0] << " JOIN" << EOL;
    }

  else if (strcmp (CMD[1], "QUIT") == 0)
    {
      mask2nick (CMD[0], buf, NICK_SIZE);
      for (i = 0; i < s->channel_num; i++)	// search in all channels
        // if it was here
        if (CHANNELS[i]->last_deleted_nick |= buf)
          {
            log = server_what2log (s, CHANNELS[i]->name);
            if (log != NULL)
              *log << buf << " QUIT " << CMD[2] << EOL;
          }
    }

  else if (strcmp (CMD[1], "KICK") == 0)
    {
      log = server_what2log (s, CMD[2]);
      if (log != NULL)
        {
          mask2nick (CMD[0], buf, NICK_SIZE);
          *log << buf << " KICK " << CMD[3] << " - " << CMD[4] << EOL;
        }
    }

  else if (strcmp (CMD[1], "PART") == 0)
    {
      log = server_what2log (s, CMD[2]);
      if (log != NULL)
        {
          mask2nick (CMD[0], buf, NICK_SIZE);
          *log << buf << " PART" << EOL;
        }
    }

  else if (strcmp (CMD[1], "NICK") == 0)
    {
      mask2nick (CMD[0], buf, NICK_SIZE);
      for (i = 0; i < s->channel_num; i++)	// search in all channels
        {
          // if it was here
          if (CHANNELS[i]->last_deleted_nick |= buf)
            {
              log = server_what2log (s, CHANNELS[i]->name);
              if (log != NULL)
                *log << buf << " NICK " << CMD[2] << EOL;
            }
        }
    }

  else if (strcmp (CMD[1], "MODE") == 0)
    {
      log = server_what2log (s, CMD[2]);
      if (log != NULL)
        {
          char buf2[MSG_SIZE+1], buf3[MSG_SIZE+1];
          int num_modes;
          mask2nick (CMD[0], buf, NICK_SIZE);
          snprintf (buf2, MSG_SIZE, "%s MODE %s", buf, CMD[3]);
          for (i = num_modes = 0; CMD[3][i] != 0; i++)
            if (CMD[3][i] != '+' && CMD[3][i] != '-')
              {
                num_modes++;
                snprintf (buf3, MSG_SIZE, "%s %s", buf2,
                          CMD[3+num_modes]);
                my_strncpy (buf2, buf3, MSG_SIZE);
              }
          *log << buf2 << EOL;
        }
    }

  else if (strcmp (CMD[1], "PRIVMSG") == 0)
    {
      if (CMD[2][0] == '#')			// if it's a channel
        {
          log = server_what2log (s, CMD[2]);
          if (log != NULL)
            *log << '<' << SOURCE << "> " << CMD[3] << EOL;
        }
      else					// pvt with the bot
        {
          log = server_what2log (s, NULL);	// logs them all
          if (log != NULL)
            *log << CMD[0] << ' ' << CMD[3] << EOL;
          log = server_what2log (s, SOURCE);	// specific pvt
          if (log != NULL)
            *log << CMD[0] << ' ' << CMD[3] << EOL;
        }
    }
}

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

  strsplit (bufread, buf, 3);

  if (strcasecmp (buf[0], "log") == 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
      log_type *l;
      log_list->rewind ();
      while ((l = (log_type *)log_list->next ()) != NULL)
        if (l->s == s)
          {
            first = 0;
            break;
          }
      if (first)
        s->script.events.add ((void *)log_event);

      if (strcasecmp (buf[2], "<privmsg>") == 0)	//log all pvts
        log_add (s, NULL, log);
      else
        log_add (s, buf[2], log);
    }
}

// module termination
static void
log_stop (CModule *m)
{
  log_type *log;
  log_list->rewind ();
  while ((log = (log_type *)log_list->next ()) != NULL)
    {
      log->s->script.events.del ((void *)log_event);
      delete log;
    }
  delete log_list;
}

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

struct CModule::module_type module = {
  MODULE_VERSION,
  "log",
  log_start,
  log_stop,
  log_conf,
  NULL
};
