package org.fidonet.binkp.handler;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.fidonet.binkp.SessionContext;
import org.fidonet.binkp.SessionState;
import org.fidonet.binkp.codec.DataBulk;
import org.fidonet.binkp.codec.TrafficCrypter;
import org.fidonet.binkp.commands.*;
import org.fidonet.binkp.commands.share.BinkCommand;
import org.fidonet.binkp.commands.share.Command;
import org.fidonet.binkp.commands.share.CommandFactory;
import org.fidonet.binkp.commands.share.CompositeMessage;
import org.fidonet.binkp.config.ServerRole;
import org.fidonet.binkp.events.DisconnectedEvent;
import org.fidonet.binkp.events.FileReceivedEvent;
import org.fidonet.binkp.io.BinkData;
import org.fidonet.binkp.io.BinkFrame;
import org.fidonet.binkp.io.FileData;
import org.fidonet.binkp.io.FileInfo;
import org.fidonet.events.EventBus;

import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * Author: Vladimir Kravets
 * E-Mail: vova.kravets@gmail.com
 * Date: 9/19/12
 * Time: 1:18 PM
 */
public class BinkSessionHandler extends IoHandlerAdapter{

    private SessionContext sessionContext;
    private EventBus eventBus;

    public BinkSessionHandler(EventBus eventBus) {
        this.sessionContext = null;
        if (eventBus == null) {
            throw new UnsupportedOperationException();
        }
        this.eventBus = eventBus;
    }

    public BinkSessionHandler(SessionContext context, EventBus eventBus) {
        this(eventBus);
        this.sessionContext = context;
    }

    private SessionContext getSessionContext(IoSession session) {
        if (sessionContext != null) {
            return sessionContext;
        } else {
            return (SessionContext)session.getAttribute(SessionContext.SESSION_CONTEXT_KEY);
        }
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        super.sessionOpened(session);    //To change body of overridden methods use File | Settings | File Templates.

        SessionContext sessionContext = getSessionContext(session);
        session.setAttribute(SessionContext.SESSION_CONTEXT_KEY, sessionContext);
        session.setAttribute(TrafficCrypter.TRAFFIC_CRYPTER_KEY, new TrafficCrypter());

        boolean isClient = sessionContext.getServerRole().equals(ServerRole.CLIENT);

        if (!isClient) {
            if (sessionContext.isBusy()) {
                Command bsy = new BSYCommand();
                sessionContext.setLastErrorMessage("To many connections");
                bsy.send(session, sessionContext);
                sessionContext.setState(SessionState.STATE_BSY);
                session.close(false);
            } else {
                Command opt_md5 = new CramOPTCommand(MessageDigest.getInstance("MD5"));
                opt_md5.send(session, sessionContext);
            }
        }

        List<MessageCommand> commands = new ArrayList<MessageCommand>();
        commands.add(new SYSCommand());
        commands.add(new ZYZCommand());
        commands.add(new LOCCommand());
        commands.add(new NDLCommand());
        commands.add(new VERCommand());
        commands.add(new TIMECommand());
        commands.add(new ADRCommand());

        CompositeMessage greeting = new CompositeMessage(commands);
        greeting.send(session, sessionContext);
        if (!isClient) {
            sessionContext.setState(SessionState.STATE_WAITPWD);
        }

    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        SessionContext sessionContext = getSessionContext(session);
        BinkFrame data = (BinkFrame) message;
        BinkData binkData = BinkFrame.toBinkData(data);
        Command command;
        try {
            command = CommandFactory.createCommand(sessionContext, binkData);
        } catch (UnknownCommandException ex) {
            sessionContext.setState(SessionState.STATE_ERR);
            sessionContext.setLastErrorMessage(ex.getMessage());
            Command error = new ERRCommand();
            error.send(session, sessionContext);
            sessionContext.sendEvent(new DisconnectedEvent(sessionContext));
            session.close(false);
            throw ex;
        }
        if (command != null) {
            System.out.println(BinkCommand.findCommand(binkData.getCommand()));
            System.out.println(new String(binkData.getData()));
            command.handle(session, sessionContext, new String(binkData.getData()));
        } else {
            // try to get data bulk
            DataBulk dataFile = new DataBulk(binkData.getData());
            System.out.println("Received data block with size " + dataFile.getRawData().getData().length + " bytes");
            FileData<OutputStream> fileData = sessionContext.getRecvFiles().peek();
            if (fileData != null) {
                FileInfo info = fileData.getInfo();
                long curSize = info.getCurSize() + dataFile.getRawData().getData().length;
                fileData.getStream().write(dataFile.getRawData().getData());
                info.setCurSize(curSize);
                info.setFinished(curSize == info.getSize());
                if (info.isFinished()) {
                    GOTCommand confirmRecv = new GOTCommand();
                    confirmRecv.send(session, sessionContext);
                    eventBus.notify(new FileReceivedEvent(sessionContext, fileData));
                    System.out.println(info);
                }
            }
        }
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        super.sessionIdle(session, status);  //To change body of overridden methods use File | Settings | File Templates.
    }
}

@@ -933,16 +933,92 @@
 ventBus;
+%0Aimport org.fidonet.logger.ILogger;%0Aimport org.fidonet.logger.LoggerFactory;
 %0A%0Aimport
@@ -1320,16 +1320,101 @@
 apter%7B%0A%0A
+    private static ILogger log = LoggerFactory.getLogger(BinkSessionHandler.class);%0A%0A
     priv
@@ -2377,32 +2377,151 @@
 ntext(session);%0A
+        log.debug(String.format(%22Session is opened with %25s%22, sessionContext.getLinksInfo().getCurLink().toString()));%0A%0A
         session.
@@ -3783,32 +3783,97 @@
 f (!isClient) %7B%0A
+            log.debug(%22Greeting was sent. Waiting password...%22);%0A
             sess
@@ -4052,30 +4052,43 @@
     
-cause.printStackTrace(
+log.error(cause.getMessage(), cause
 );%0A 
@@ -4476,24 +4476,24 @@
  binkData);%0A
-
         %7D ca
@@ -4523,24 +4523,68 @@
 ption ex) %7B%0A
+            log.error(ex.getMessage(), ex);%0A
             

Add small logging