/*
 Eteria IRC Client, an RFC 1459 compliant client program written in Java.
 Copyright (C) 2000-2001  Javier Kohen <jkohen at tough.com>
 
 This program 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 program 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.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 package ar.com.jkohen.irc;
 
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Observable;
 
 public class Channel extends Observable
 {
 	public static final int TOPIC_MASK = 0x01;
 	public static final int MODERATED_MASK = 0x02;
 	public static final int SECRET_MASK = 0x04;
 	public static final int INVITATION_MASK = 0x08;
 	public static final int REGISTERED_MASK = 0x10;
 	public static final int NONICK_MASK = 0x20;
 
 	private String tag;
 	private String topic;
 	private int limit = -1;
 	private String key = "";
 	private int modes_mask;
 
 	private Hashtable users;
 
 	public Channel(String tag)
 	{
 		this.tag = tag;
 		this.users = new Hashtable();
 	}
 
 	public String getTag()
 	{
 		return tag;
 	}
 
 	/* Method setTag makes no sense as a channel's tag can't be changed. */
 
 	public String getTopic()
 	{
 		return topic;
 	}
 
 	public void setTopic(String topic)
 	{
 		this.topic = topic;
 		setChanged();
 		notifyObservers("topic");
 	}
 
 	/*
 	 * User related methods.
 	 */
 
 	public void add(String nick)
 	{
 		int mask = Modes.symbolicToMask(nick.charAt(0));
 		nick = Modes.canonicalizeNick(nick);
 		User user = new User(nick);
 		user.setModes(mask);
 		synchronized (users)
 		{
 			users.put(nick, user);
 		}
 
 		setChanged();
 		notifyObservers("add ".concat(nick));
 	}
 
 	public void remove(String nick)
 	{
 		synchronized (users)
 		{
 			User user = (User) users.remove(nick);
 			if (user == null)
 				return;
 		}
 
 		setChanged();
 		notifyObservers("remove ".concat(nick));
 	}
 
 	public void rename(String nick, String new_nick)
 	{
 		synchronized (users)
 		{
 			User user = (User) users.remove(nick);
 			if (user == null)
 				return;
 				
 			user.setTag(new_nick);
 			users.put(new_nick, user);
 		}
 
 		setChanged();
 		notifyObservers("rename ".concat(nick).concat(" ").concat(new_nick));
 	}
 	
 	public void clean()
 	{
 		synchronized (users)
 		{
 			users.clear();
 		}
 		
 		update();
 	}
 	
 	public void update()
 	{
 		setChanged();
 		notifyObservers("refresh");
 	}
 	
 	public void update(String s)
 	{
 		if (contains(s))
 		{
 			setChanged();
 			notifyObservers("update ".concat(s));
 		}
 	}
 
 	public User get(String nick)
 	{
   		return (User) users.get(nick);
 	}
 
 	public boolean contains(String nick)
 	{
 		return users.containsKey(nick);
 	}
 
 	public int number()
 	{
 		return(users.size());
 	}
 	
 	public User [] elements()
 	{
 		User [] a;
 		Enumeration e;
 
 		synchronized (users)
 		{
 			a = new User [users.size()];
 			e = users.elements();
 		}
 
 		for (int i = 0; e.hasMoreElements(); i++)
 			a[i] = (User) e.nextElement();
 
 		return a;
 	}
 
 
 	/*
 	 * Mode related methods.
 	 */
 
 	public int getLimit()
 	{
 		return(limit);
 	}
 
 	public void setLimit(String l)
 	{
 		try
 		{
 			this.limit = Integer.parseInt(l);
 		} catch (NumberFormatException e) { this.limit = -1; }
 	}
 	
 	public String getKey()
 	{
 		return(key);
 	}
 
 	public void setKey(boolean sign, String k)
 	{
 		if (sign)
 			this.key = k;
 		else
 			this.key = "";
 	}
 	
 	/**
 	 * Set channel, users or global modes.
 	 */
 	 
 	public void setModes()
 	{
 		setChanged();
 		notifyObservers("mode");
 	}
 	
 	public void setModes(String modes, String [] params)
 	{
 		char [] mode_ary = modes.toCharArray();
 		boolean sign = false;
 
 		int j = 0;
 		for (int i = 0; i < mode_ary.length; i++)
 		{
 			switch (mode_ary[i])
 			{
 			case '+':
 				sign = true;
 				break;
 			case '-':
 				sign = false;
 				break;
 				
 			/* Channel modes which handle additional parameters */
 			
 			case 'k':
 				if (j < params.length)
 					setKey(sign, params[j++]);
 				break;
 			case 'l':
 				if (j < params.length)
 					setLimit(sign ? params[j++] : "");
 				break;
 		
 				
 			/* Channel modes without parameters */
 				
 			case 't':
 				setChannelModes(sign, Channel.TOPIC_MASK);
 				break;
 			case 'm':
 				setChannelModes(sign, Channel.MODERATED_MASK);
 				break;
 			case 's':
 				setChannelModes(sign, Channel.SECRET_MASK);
 				break;
 			case 'i':
 				setChannelModes(sign, Channel.INVITATION_MASK);
 				break;
 			
 			/* Non RFC1459 channel modes */
 			
 			case 'r':
 				setChannelModes(sign, Channel.REGISTERED_MASK);
 				break;
 			case 'N':
 				setChannelModes(sign, Channel.NONICK_MASK);
 				break;
 			
 			/* These modes aren't handled, but are known to carry a parameter which must be skipped. */
 			
 			case 'b':
 			case 'e':
 			case 'I':
 				j++;
 				break;
 			
 /*
 			case 'v':
 				if (j < params.length)
 					setUserModes(params[j++], sign, User.VOICE_MASK);
 				break;
 			case 'h':
 				if (j < params.length)
 					setUserModes(params[j++], sign, User.HALFOP_MASK);
 				break;
 			case 'o':
 				if (j < params.length)
 					setUserModes(params[j++], sign, User.OP_MASK);
 				break;
 			case 'a':
 				if (j < params.length)
 					setUserModes(params[j++], sign, User.ADMIN_MASK);
 				break;
 			case 'q':
 				if (j < params.length)
 					setUserModes(params[j++], sign, User.OWNER_MASK);
 				break;
 */
 			/* Channel modes which set a prefix to a user */
 			
 			default:
 				if (j < params.length)
 				{
 					if (setUserMode(mode_ary[i], params[j], sign))
 						j++;
 				}
 			}
 		}
 	}
 
 /*
 	private void setUserModes(String nick, boolean sign, int mask)
 	{
 		User user = get(nick);
 		if(user == null)
 			return;
 			
 		int cur_mask = user.getModes();
 
 		if (sign)
 			cur_mask |= mask;
 		else
 			cur_mask &= ~mask;
 		user.setModes(cur_mask);
 		
 		setChanged();
 		notifyObservers("update ".concat(nick));
 	}
 */
 
 	private boolean setUserMode(char mode, String nick, boolean sign)
 	{
 		int mask = Modes.alphaToMask(mode);
 		if (mask == User.NORMAL_MASK)
 			return(false);
 			
 		User user = get(nick);
 		if(user == null)
 			return(true);
 			
 		int cur_mask = user.getModes();
 
 		if (sign)
 			cur_mask |= mask;
 		else
 			cur_mask &= ~mask;
 		user.setModes(cur_mask);
 	
 		setChanged();
 		notifyObservers("update ".concat(nick));		
 		return(true);
 	}
 
 	private void setChannelModes(boolean sign, int mask)
 	{
 		if (sign)
 			modes_mask |= mask;
 		else
 			modes_mask &= ~mask;
 	}
 
 	public boolean isTopicSettable()
 	{
 		return ((modes_mask & TOPIC_MASK) == 0);
 	}
 	
 	public boolean isModerated()
 	{
 		return ((modes_mask & MODERATED_MASK) != 0);
 	}
 
 	public boolean isInvitOnly()
 	{
 		return ((modes_mask & INVITATION_MASK) != 0);
 	}
 
 	public boolean isSecret()
 	{
 		return ((modes_mask & SECRET_MASK) != 0);
 	}
 
 	public boolean isRegistered()
 	{
 		return ((modes_mask & REGISTERED_MASK) != 0);
 	}
 	
 	public boolean canNick()
 	{
 		return ((modes_mask & NONICK_MASK) == 0);
 	}
 		
 	public static boolean isChannel(String s)
 	{
 		if (s != null && !s.equals(""))
 		{
 			char ch = s.charAt(0);
 			return (ch == '&' || ch == '#' || ch == '+' || ch == '!');
 		}
 		return (false);
 	}
 }