/*
 * Copyright (c) 2006-2007 Chris Smith, Shane Mc Cormack, Gregory Holmes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package uk.org.ownage.dmdirc;

import java.util.Hashtable;
import javax.swing.event.InternalFrameEvent;
import uk.org.ownage.dmdirc.logger.ErrorLevel;
import uk.org.ownage.dmdirc.parser.ChannelInfo;
import uk.org.ownage.dmdirc.parser.MyInfo;
import uk.org.ownage.dmdirc.parser.ParserError;
import uk.org.ownage.dmdirc.parser.ServerInfo;
import uk.org.ownage.dmdirc.ui.MainFrame;
import uk.org.ownage.dmdirc.ui.ServerFrame;
import uk.org.ownage.dmdirc.parser.IRCParser;
import uk.org.ownage.dmdirc.logger.Logger;
import javax.swing.event.InternalFrameListener;
import uk.org.ownage.dmdirc.parser.callbacks.interfaces.IChannelSelfJoin;
import uk.org.ownage.dmdirc.parser.callbacks.interfaces.IErrorInfo;
import uk.org.ownage.dmdirc.logger.Logger;
import uk.org.ownage.dmdirc.logger.ErrorLevel;
import uk.org.ownage.dmdirc.parser.callbacks.CallbackNotFound;

/**
 * The Server class represents the client's view of a server. It maintains
 * a list of all channels, queries, etc, and handles parser callbacks pertaining
 * to the server
 * @author chris
 */
public class Server implements IChannelSelfJoin, IErrorInfo, InternalFrameListener {
    
    /**
     * Open channels that currently exist on the server
     */
    private Hashtable<String,Channel> channels  = new Hashtable<String,Channel>();
    
    /**
     * The ServerFrame corresponding to this server
     */
    private ServerFrame frame;
    
    /**
     * The IRC Parser instance handling this server
     */
    private IRCParser parser;
    
    private Raw raw;
    
    /**
     * Creates a new instance of Server
     * @param server The hostname/ip of the server to connect to
     * @param port The port to connect to
     * @param password The server password
     */
    public Server(String server, int port, String password) {
        
        ServerManager.getServerManager().registerServer(this);
        
        frame = new ServerFrame(this);
        frame.setTitle(server+":"+port);
        frame.addInternalFrameListener(this);
        
        MainFrame.getMainFrame().addChild(frame);
        
        frame.addLine("Connecting to "+server+":"+port);
        
        MyInfo myInfo = new MyInfo();
        myInfo.sNickname = Config.getOption("general","defaultnick");
        myInfo.sAltNickname = Config.getOption("general","alternatenick");
                
        parser = new IRCParser(myInfo, new ServerInfo(server, port, password));
        
        try {
            parser.getCallbackManager().addCallback("OnChannelSelfJoin", this);
            parser.getCallbackManager().addCallback("OnErrorInfo", this);
        } catch (CallbackNotFound ex) {
            Logger.error(ErrorLevel.FATAL, ex);
        }
        
        raw = new Raw(this);
        
        try {
            Thread thread = new Thread(parser);
            thread.start();
        } catch (Exception ex) {
            frame.addLine("ERROR: "+ex.getMessage());
        }
    }
    
    /**
     * Retrieves the parser used for this connection
     * @return IRCParser this connection's parser
     */
    public IRCParser getParser() {
        return parser;
    }
    
    public void addLine(String line) {
        frame.addLine(line);
    }
    
    public void close(String reason) {
        // Unregister parser callbacks
        parser.getCallbackManager().delCallback("OnChannelSelfJoin", this);
        parser.getCallbackManager().delCallback("OnErrorInfo", this);
        // Unregister frame callbacks
        frame.removeInternalFrameListener(this);
        // Disconnect from the server
        disconnect(reason);
        // Unregister ourselves with the server manager
        ServerManager.getServerManager().unregisterServer(this);
        // Close all channel windows
        closeChannels();
        // Close the raw window
        raw.close();
        // Close our own window
        frame.setVisible(false);
        MainFrame.getMainFrame().delChild(frame);
        frame = null;
        // Ditch the parser
        parser = null;
    }
    
    public void disconnect(String reason) {
        parser.quit(reason);
    }
    
    private void closeChannels() {
        for (Channel channel : channels.values()) {
            channel.close();
        }
    }
    
    public void delChannel(String chan) {
        channels.remove(chan);
    }
    
    private void addChannel(ChannelInfo chan) {
        channels.put(chan.getName(), new Channel(this, chan));
    }
    
    public void onChannelSelfJoin(IRCParser tParser, ChannelInfo cChannel) {
        if (channels.containsKey(cChannel.getName())) {
            channels.get(cChannel.getName()).setChannelInfo(cChannel);
        } else {
            addChannel(cChannel);
        }
    }
    
    public void onErrorInfo(IRCParser tParser, ParserError errorInfo) {
        ErrorLevel errorLevel;
        if (errorInfo.isFatal()) {
            errorLevel = ErrorLevel.FATAL;
        } else if (errorInfo.isError()) {
            errorLevel = ErrorLevel.ERROR;
        } else if (errorInfo.isWarning()) {
            errorLevel = ErrorLevel.WARNING;
        } else {
            Logger.error(ErrorLevel.WARNING, "Unknown error level for parser error: "+errorInfo.getData());
            return;
        }
        
        if (errorInfo.isException()) {
            Logger.error(errorLevel, errorInfo.getException());
        } else {
            Logger.error(errorLevel, errorInfo.getData());
        }
    }
    
    public void internalFrameOpened(InternalFrameEvent internalFrameEvent) {
    }
    
    public void internalFrameClosing(InternalFrameEvent internalFrameEvent) {
        close(Config.getOption("general","quitmessage"));
    }
    
    public void internalFrameClosed(InternalFrameEvent internalFrameEvent) {
    }
    
    public void internalFrameIconified(InternalFrameEvent internalFrameEvent) {
    }
    
    public void internalFrameDeiconified(InternalFrameEvent internalFrameEvent) {
    }
    
    public void internalFrameActivated(InternalFrameEvent internalFrameEvent) {
    }
    
    public void internalFrameDeactivated(InternalFrameEvent internalFrameEvent) {
    }
    
}

@@ -2682,24 +2682,66 @@
 w raw;%0A    %0A
+    private boolean closing = false;%0A    %0A
     /**%0A    
@@ -3528,24 +3528,16 @@
         
-        
 %0A       
@@ -5323,24 +5323,48 @@
 hannels() %7B%0A
+        closing = true;%0A
         for 
@@ -5399,24 +5399,24 @@
 values()) %7B%0A
-
             
@@ -5434,32 +5434,57 @@
 se();%0A        %7D%0A
+        closing = false;%0A
     %7D%0A    %0A    p
@@ -5512,32 +5512,60 @@
 (String chan) %7B%0A
+        if (!closing) %7B%0A    
         channels
@@ -5575,24 +5575,34 @@
 move(chan);%0A
+        %7D%0A
     %7D%0A    %0A 

Fix concurrent modification error when closing channel windows