// ----------------------------------------------------------------------------
//  LOC.C
// ----------------------------------------------------------------------------

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef PURPLE_PLUGINS
#define PURPLE_PLUGINS
#endif

#include <glib.h>

#ifndef G_GNUC_NULL_TERMINATED
#if __GNUC__ >= 4
#define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
#else
#define G_GNUC_NULL_TERMINATED
#endif /* __GNUC__ >= 4 */
#endif /* G_GNUC_NULL_TERMINATED */

#include <debug.h>
#include <notify.h>
#include <plugin.h>
#include <version.h>
#include <network.h>
#include <savedstatuses.h>
#define PLUGIN_ID "core-location"

// ----------------------------------------------------------------------------

PurplePlugin* loc_plugin = NULL;

// ----------------------------------------------------------------------------

typedef struct
{
  unsigned net;
  unsigned msk;
  char* stat;
} Location;

static
Location*
free_location(Location* loc)
{
  g_free(loc->stat);
  g_free(loc);

  return NULL;
};

static
GList*
free_locations(GList* locs)
{
  Location* loc;

  while(locs)
  {
    loc = locs->data;
    locs = g_list_remove(locs, loc);
    free_location(loc);
  };

  return NULL;
};

static
Location*
parse_location(xmlnode* xmlloc)
{
  const char* attr;
  const unsigned char* ip;
  Location* loc;
  
  loc = g_new0(Location, 1);
  
  attr = xmlnode_get_attrib(xmlloc, "network");
  if(attr && (ip=purple_network_ip_atoi(attr)) != NULL)
  {
    loc->net = *(const unsigned*)ip;
  }
  else
  {
    loc = free_location(loc);
    return NULL;
  };

  attr = xmlnode_get_attrib(xmlloc, "netmask");
  if(attr && (ip=purple_network_ip_atoi(attr)) != NULL)
  {
    loc->msk = *(const unsigned*)ip;
    loc->net &= loc->msk;
  }
  else
  {
    loc = free_location(loc);
    return NULL;
  };

  attr = xmlnode_get_attrib(xmlloc, "status");
  if(attr)
  {
    loc->stat = g_strdup(attr);
  }
  else
  {
    loc = free_location(loc);
    return NULL;
  };

  return loc;
};

static 
gint
locations_sort(gconstpointer l, gconstpointer r)
{
	const Location* loc_l = l;
	const Location* loc_r = r;
	
	if(loc_l->msk > loc_r->msk)
		return 1;
	if(loc_l->msk < loc_r->msk)
		return -1;
	return 0;
}

static 
GList*
load_locations(void)
{
  xmlnode* xmllocs;
  xmlnode* xmlloc;
  GList* locs = NULL;
 
  xmllocs = purple_util_read_xml_from_file("loc.xml", "Known locations");
  if(xmllocs)
  {
    for(xmlloc=xmlnode_get_child(xmllocs, "loc"); xmlloc; xmlloc=xmlnode_get_next_twin(xmlloc))
    {
      Location* loc;
      loc = parse_location(xmlloc);
      if(loc)
        locs = g_list_append(locs, loc);    
    };

    locs = g_list_sort(locs, locations_sort);

    xmlnode_free(xmllocs);
  }
  else
    purple_debug_warning(PLUGIN_ID, "Cannot read loc.xml\n");

  return locs;
};

static
char*
loc_match(GList* locations, unsigned ip)
{
  char* ret = NULL;

  for( ; locations ; locations=g_list_next(locations))
  {
    Location* loc = locations->data;
    unsigned match_ip = ip & loc->msk;

    purple_debug_info(PLUGIN_ID, "Trying 0x%08X 0x%08X\n", loc->net, loc->msk);
    if(match_ip == loc->net)
    {
      ret = g_strdup(loc->stat);
      purple_debug_info(PLUGIN_ID, "Match for %s\n", ret);
      break;
    };
  };
  return ret;
};

static
void
set_location(void)
{
  const char* ipaddr;
  char* status;
  unsigned ip;
  GList* locations;
  
  // determine ip address
  ipaddr = purple_network_get_my_ip(-1);
  if(ipaddr)
  {
    ip = *(unsigned*)(purple_network_ip_atoi(ipaddr));
    purple_debug_info(PLUGIN_ID, "Using ip addr 0x%08X\n", ip);

    // match ip address
    locations =  load_locations();
    status = loc_match(locations, ip);
    if(status)
    {
      PurpleSavedStatus* ss = purple_savedstatus_find(status);

      if(ss)
        purple_savedstatus_activate(ss);
      else
        purple_debug_warning(PLUGIN_ID, "No saved status for %s\n", status);

      g_free(status);
    };
    locations = free_locations(locations);
  };
};

static
void
connect_cb(PurplePluginAction* action)
{
  purple_debug_info(PLUGIN_ID, "Signed on\n");

  set_location();
};

static 
gboolean
plugin_load(PurplePlugin* plugin)
{
  loc_plugin = plugin;

  purple_signal_connect(purple_connections_get_handle(), "signed-on",
    loc_plugin, PURPLE_CALLBACK(connect_cb), NULL);

  set_location();

  return TRUE;
};

static 
void
init_plugin(PurplePlugin* plugin)
{
}

static PurplePluginInfo info = 
{
	PURPLE_PLUGIN_MAGIC,
	PURPLE_MAJOR_VERSION,
	PURPLE_MINOR_VERSION,
	PURPLE_PLUGIN_STANDARD,
	NULL,                           // ui
	0,                              // flags
	NULL,                           // dependency list
	PURPLE_PRIORITY_DEFAULT,        // priority
	PLUGIN_ID,                      // id
	"Location",                     // display
	DISPLAY_VERSION,                // version
	"Location Plugin",              // summary
	"Update status depending "
   "on the network connected to", // description
	"Luc <luc@daphnia.com>",        // author
	"http://www.daphnia.com/",      // url
	plugin_load,                    // called on load
	NULL,                           // called on unload
	NULL,                           // called on destroy
	NULL,                           // pidgin plugin ui info
	NULL,
	NULL,                           // purple plugin ui info
	NULL,                           // menu construction
	NULL,                           // reserved
	NULL,                           // reserved
	NULL,                           // reserved
	NULL                            // reserved
};

PURPLE_INIT_PLUGIN (PLUGIN_ID, init_plugin, info)
