# -*- coding: utf-8 -*-

"""
 *  Copyright (C) 2009, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
 *
 *  Mumble-Django is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This package is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
"""

import mctl

import datetime
from time			import time
from os.path			import join

from django.utils.http		import urlquote
from django.conf		import settings


def cmp_names( a, b ):
	return cmp( a.name, b.name );


class mmChannel( object ):
	"""Represents a channel in Murmur."""
	
	# channels  = list();
	# subchans  = list();
	# chanid    = int();
	# name      = str();
	# parent    = mmChannel();
	# linked    = list();
	# linkedIDs = list();
	
	def __init__( self, server, channelObj, parentChan = None ):
		self.server   = server;
		self.players  = list();
		self.subchans = list();
		self.linked   = list();
		
		self.chanid    = channelObj[0];
		self.name      = channelObj[1];
		parent         = channelObj[2];
		self.linkedIDs = channelObj[3];
		
		if len( channelObj ) == 5:
			self.description = channelObj[4];
		else:
			self.description = "";
		
		self.parent = parentChan;
		if self.parent is not None:
			self.parent.subchans.append( self );
	
	
	def parentChannels( self ):
		"""Return the names of this channel's parents in the channel tree."""
		if self.parent is None or self.parent.is_server or self.parent.chanid == 0:
			return [];
		return self.parent.parentChannels() + [self.parent.name];
	
	
	is_server  = False;
	is_channel = True;
	is_player  = False;
	
	
	playerCount = property(
		lambda self: len( self.players ) + sum( [ chan.playerCount for chan in self.subchans ] ),
		doc="The number of players in this channel."
		);
	
	id   = property(
		lambda self: "channel_%d"%self.chanid,
		doc="A string ready to be used in an id property of an HTML tag."
		);
	
	show = property(
		lambda self: self.parent is None or self.parent.chanid == 0 or self.playerCount > 0,
		doc="True if this channel needs to be shown because it is root, a child of root, or has players."
		);
	
	def __str__( self ):
		return '<Channel "%s" (%d)>' % ( self.name, self.chanid );
	
	def sort( self ):
		"""Sort my subchannels and players, and then iterate over them and sort them recursively."""
		self.subchans.sort( cmp_names );
		self.players.sort( cmp_names );
		for sc in self.subchans:
			sc.sort();
	
	def visit( self, callback, lvl = 0 ):
		"""Call callback on myself, then visit my subchans, then my players."""
		callback( self, lvl );
		for sc in self.subchans:
			sc.visit( callback, lvl + 1 );
		for pl in self.players:
			pl.visit( callback, lvl + 1 );
	
	
	def getURL( self, forUser = None ):
		"""
		Create an URL to connect to this channel. The URL is of the form
		mumble://username@host:port/parentchans/self.name
		"""
		userstr = "";
		
		if forUser is not None:
			userstr = "%s@" % forUser.name;
		
		# create list of all my parents and myself
		chanlist = self.parentChannels() + [self.name];
		# urlencode channel names
		chanlist = [ urlquote( chan ) for chan in chanlist ];
		# create a path by joining the channel names
		chanpath = join( *chanlist );
		
		if self.server.port != settings.MUMBLE_DEFAULT_PORT:
			return "mumble://%s%s:%d/%s" % ( userstr, self.server.addr, self.server.port, chanpath );
		
		return "mumble://%s%s/%s" % ( userstr, self.server.addr, chanpath );
	
	connecturl = property( getURL, doc="A convenience wrapper for getURL." );
	
	def setDefault( self ):
		"Make this the server's default channel."
		self.server.defchan = self.chanid;
		self.server.save();
	
	is_default = property(
		lambda self: self.server.defchan == self.chanid,
		doc="True if this channel is the server's default channel."
		);



class mmPlayer( object ):
	"""Represents a Player in Murmur."""
	
	# muted        = bool;
	# deafened     = bool;
	# suppressed   = bool;
	# selfmuted    = bool;
	# selfdeafened = bool;
	
	# channel      = mmChannel();
	# dbaseid      = int();
	# userid       = int();
	# name         = str();
	# onlinesince  = time();
	# bytesPerSec  = int();
	
	# mumbleuser   = models.MumbleUser();
	
	def __init__( self, srvInstance, playerObj, playerChan ):
		( self.userid, self.muted, self.deafened, self.suppressed, self.selfmuted, self.selfdeafened, chanID, self.dbaseid, self.name, onlinetime, self.bytesPerSec ) = playerObj;
		self.onlinesince = datetime.datetime.fromtimestamp( float( time() - onlinetime ) );
		self.channel = playerChan;
		self.channel.players.append( self );
		
		if self.isAuthed:
			from models import Mumble, MumbleUser
			try:
				self.mumbleuser = MumbleUser.objects.get( mumbleid=self.dbaseid, server=srvInstance );
			except MumbleUser.DoesNotExist:
				self.mumbleuser = None;
		else:
			self.mumbleuser = None;
	
	def __str__( self ):
		return '<Player "%s" (%d, %d)>' % ( self.name, self.userid, self.dbaseid );
	
	isAuthed = property(
		lambda self: self.dbaseid != -1,
		doc="True if this player is authenticated (+A)."
		);
	
	isAdmin = property(
		lambda self: self.mumbleuser and self.mumbleuser.getAdmin(),
		doc="True if this player is in the Admin group in the ACL."
		);
	
	is_server  = False;
	is_channel = False;
	is_player  = True;
	
	# kept for compatibility to mmChannel (useful for traversal funcs)
	playerCount = property( lambda self: -1, doc="Exists only for compatibility to mmChannel." );
	
	id = property(
		lambda self: "player_%d"%self.userid,
		doc="A string ready to be used in an id property of an HTML tag."
		);
	
	def visit( self, callback, lvl = 0 ):
		""" Call callback on myself. """
		callback( self, lvl );



class mmACL:
	"""Represents an ACL for a certain channel."""
	
	def __init__( self, channelId, aclObj ):
		aclsrc, groupsrc, inherit = aclObj;
		
		self.channelId = channelId;
		
		self.acls = [];
		for line in aclsrc:
			acl = {};
			acl['applyHere'], acl['applySubs'], acl['inherited'], acl['playerid'], acl['group'], acl['allow'], acl['deny'] = line;
			self.acls.append( acl );
		
		self.groups = [];
		for line in groupsrc:
			group = {};
			group['name'], group['inherited'], group['inherit'], group['inheritable'], group['add'], group['remove'], group['members'] = line;
			self.groups.append( group );
			if group['name'] == "admin":
				self.admingroup = group;
		
		self.inherit = inherit;
	
	def pack( self ):
		""" Pack the information in this ACL up in a way that it can be passed to DBus. """
		return (
			self.channelId,
			[( acl['applyHere'], acl['applySubs'], acl['inherited'], acl['playerid'], acl['group'], acl['allow'], acl['deny'] ) for acl in self.acls ],
			[( group['name'], group['inherited'], group['inherit'], group['inheritable'], group['add'], group['remove'], group['members'] ) for group in self.groups ],
			self.inherit
			);