'''Various utility functions.

Copyright 2004-2005 AG Projects
'''

__all__ = ['info', 'warning', 'error', 'abort', 'fatal', 'thisHostIP',
           'unique_sorted', 'address2str', 'str2address', 'Null']

import sys
import socket
import struct
from sets import Set

try:    thisHostIP = socket.gethostbyname(socket.getfqdn())
except: thisHostIP = None

## Helper functions

def info(msg):
    print >>sys.stderr, "info: " + str(msg)

def warning(msg):
    print >>sys.stderr, "warning: " + str(msg)

def error(msg):
    print >>sys.stderr, "error: " + str(msg)

def abort(msg):
    print >>sys.stderr, "fatal: " + str(msg)
    print >>sys.stderr, "fatal: cannot continue. Exiting."
    sys.exit(1)
    
fatal = abort

def unique_sorted(l):
    '''Return all the unique elements from list sorted'''
    ul = list(Set(l))
    ul.sort()
    return ul

def address2str(addr):
    '''Encodes a network address represented as a tuple into a compact string'''
    ip, port = socket.inet_aton(addr[0]), addr[1]
    return struct.pack('!4sH', ip, port).encode('hex')

def str2address(s):
    '''Decodes a string encoded by address2str into network address represented as a tuple'''
    try:
        ip, port = struct.unpack('!4sH', s.decode('hex'))
        return (socket.inet_ntoa(ip), port)
    except:
        raise ValueError, "Wrongly packed address"


## Class that reliably does 'nothing' :P
class Null(object):
    '''Instances of this class always and reliably "do nothing" :P'''
    def __init__(self, *args, **kwargs): pass
    def __call__(self, *args, **kwargs): return self
    def __repr__(self): return self.__class__.__name__
    def __nonzero__(self): return 0
    def __eq__(self, other): return isinstance(other, self.__class__)
    def __ne__(self, other): return not isinstance(other, self.__class__)
    def __getattr__(self, name): return self
    def __setattr__(self, name, value): return self
    def __delattr__(self, name): return self
    __str__ = __repr__


