package com.beolnix.marvin.im;

import com.beolnix.marvin.config.api.BotSettings;
import com.beolnix.marvin.config.api.ConfigurationProvider;
import com.beolnix.marvin.config.api.error.ConfigurationException;
import com.beolnix.marvin.im.api.*;
import com.beolnix.marvin.im.api.model.*;
import com.beolnix.marvin.im.api.IMSessionState;
import com.beolnix.marvin.plugins.api.PluginsManager;
import org.apache.log4j.Logger;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * Created by beolnix on 31/10/15.
 */
public class IMSessionManagerImpl implements IMSessionManager {

    // dependencies
    private final ConfigurationProvider configProvider;
    private final Executor senderExecutor;

    // state
    private Map<String, IMSession> sessionsMap = new ConcurrentHashMap<>();
    private Map<String, IMSessionProvider> sessionProvidersMap = new ConcurrentHashMap<>();

    // constants
    private static final Logger logger = Logger.getLogger(IMSessionManagerImpl.class);

    public IMSessionManagerImpl(ConfigurationProvider configProvider, Executor senderExecutor) {
        this.configProvider = configProvider;
        this.senderExecutor = senderExecutor;
    }

    @Override
    public void registerIMSessionProvider(IMSessionProvider imSessionProvider) {
        sessionProvidersMap.putIfAbsent(imSessionProvider.getProtocol(), imSessionProvider);
        logger.info("New IM Session provider has been registered for protocol: " + imSessionProvider.getProtocol());
    }

    @Override
    public void createSessions(PluginsManager pluginsManager) throws ConfigurationException {
        for (String botName : configProvider.getBotSettings().keySet()) {
            try {
                createSessionFor(botName, pluginsManager);
            } catch (ConfigurationException e) {
                logger.error("Session configuration error: " + e, e);
            } catch (Exception e) {
                logger.error("Session creation error: " + e, e);
            }
        }
    }

    @Override
    public void createSessionFor(String botName, PluginsManager pluginsManager) throws ConfigurationException {
        if (sessionsMap.get(botName) != null) {
            throw new ConfigurationException("Session for bot " + botName + " has been already created");
        }

        BotSettings botSettings = configProvider.getBotSettings().get(botName);

        IMSessionProvider imSessionProvider = sessionProvidersMap.get(botSettings.getProtocol());

        if (imSessionProvider != null) {
            IMSession imSession = imSessionProvider.createNewSession(botSettings, pluginsManager);
            imSession.connect();
            sessionsMap.put(botName, imSession);
            logger.info("New bot created: " + botName + "; protocol: " + imSession.getProtocol());
        } else {
            throw new ConfigurationException("Can't find IM Session Provider for the protocol: " + botSettings.getProtocol());
        }
    }

    @Override
    public void sendMessage(final IMOutgoingMessage outMsg) {
        logger.trace("New message scheduled to be sent from: " + outMsg.getBotName() +
                "; protocol: " + outMsg.getProtocol() +
                "; msgBody: " + outMsg.getRawMessageBody());

        this.senderExecutor.execute(() -> send(outMsg));
    }

    private void send(IMOutgoingMessage outMsg) {
        IMSession imSession = sessionsMap.get(outMsg.getBotName());
        if (imSession == null) {
            String errorMsg = "session " + outMsg.getBotName() + " not found. outMsg dump: " + outMsg.toString();
            logger.error(errorMsg);
            return;
        }

        if (imSession.getState() != IMSessionState.CONNECTED) {
            logger.error("Bot '" + outMsg.getBotName() + "' can't send the message because it isn't connected");
            return;
        }

        imSession.sendMessage(outMsg);
        logger.trace("Message passed to imSession from bot name: " + outMsg.getBotName() +
                "; protocol: " + outMsg.getProtocol() +
                "; msgBody: " + outMsg.getRawMessageBody());
    }

    @Override
    public Map<String, IMSession> getIMSessions() {
        return new HashMap<>(sessionsMap);
    }

    @Override
    public IMSession getIMSession(String botName) {
        return sessionsMap.get(botName);
    }

}

@@ -3097,35 +3097,111 @@
 goingMessage
+...
  outM
+essage
 s
-g
 ) %7B%0A
+        if (outMessages.length %3C 1) %7B%0A            return;%0A        %7D%0A
         logg
@@ -3249,34 +3249,42 @@
 t from: %22 + outM
-sg
+essages%5B0%5D
 .getBotName() +%0A
@@ -3312,34 +3312,42 @@
 otocol: %22 + outM
-sg
+essages%5B0%5D
 .getProtocol() +
@@ -3348,69 +3348,8 @@
 ol()
- +%0A                %22; msgBody: %22 + outMsg.getRawMessageBody()
 );%0A%0A
@@ -3399,18 +3399,23 @@
 end(outM
+essage
 s
-g
 ));%0A    
@@ -3460,19 +3460,28 @@
 sage
+...
  outM
+essage
 s
-g
 ) %7B%0A
+%0A
     
@@ -3522,26 +3522,34 @@
 Map.get(outM
-sg
+essages%5B0%5D
 .getBotName(
@@ -3601,26 +3601,21 @@
     
-String errorMsg = 
+logger.error(
 %22ses
@@ -3619,34 +3619,42 @@
 session %22 + outM
-sg
+essages%5B0%5D
 .getBotName() + 
@@ -3669,78 +3669,9 @@
 und.
- outMsg dump: %22 + outMsg.toString();%0A            logger.error(errorMsg
+%22
 );%0A 
@@ -3795,34 +3795,42 @@
 r(%22Bot '%22 + outM
-sg
+essages%5B0%5D
 .getBotName() + 
@@ -3907,32 +3907,83 @@
 turn;%0A        %7D%0A
+        for (IMOutgoingMessage msg : outMessages) %7B
 %0A        imSessi
@@ -3967,32 +3967,36 @@
 ages) %7B%0A        
+    
 imSession.sendMe
@@ -4001,20 +4001,17 @@
 Message(
-outM
+m
 sg);%0A   
@@ -4007,32 +4007,36 @@
 e(msg);%0A        
+   
+ 
 logger.trace(%22Me
@@ -4076,28 +4076,25 @@
 t name: %22 + 
-outM
+m
 sg.getBotNam
@@ -4107,32 +4107,36 @@
                 
+    
 %22; protocol: %22 +
@@ -4132,28 +4132,25 @@
 otocol: %22 + 
-outM
+m
 sg.getProtoc
@@ -4172,16 +4172,20 @@
         
+    
 %22; msgBo
@@ -4192,20 +4192,17 @@
 dy: %22 + 
-outM
+m
 sg.getRa
@@ -4214,24 +4214,35 @@
 ageBody());%0A
+        %7D%0A%0A
     %7D%0A%0A    @

support sending multiple replies at once introduced in API 0.2