package com.nokia.sms.sgip.mo.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.mina.core.filterchain.IoFilter;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.logging.LogLevel;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.executor.UnorderedThreadPoolExecutor;

import com.nokia.sms.sgip.SgipConfig;
import com.nokia.sms.sgip.codec.SgipCodecFactory;
import com.nokia.sms.sgip.filter.SGIPLoggingFilter;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MOServer {
    private CompositeConfiguration config = SgipConfig.getConfig();
    private IoAcceptor acceptor;
    private static MOServer moserver;

    public static synchronized MOServer getInstance() {
        if (moserver == null) {
            moserver = new MOServer();
        }
        return moserver;
    }

    private MOServer() {
        this.acceptor = new NioSocketAcceptor();
        this.acceptor.getSessionConfig().setReadBufferSize(2048);
        int idleTime = this.config.getInt("sgip_mo_socket_timeout", 30);
        this.acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, idleTime);
        try {
            int codeThread = this.config.getInt("sgip_mo_threadpool_code_thread", 2);
            int maxThread = this.config.getInt("sgip_mo_threadpool_max_thread", 4);

            IoFilter coderFilter = new ProtocolCodecFilter(new SgipCodecFactory());
            SGIPLoggingFilter logFilter = new SGIPLoggingFilter();

            logFilter.setExceptionCaughtLogLevel(LogLevel.NONE);
            logFilter.setSessionCreatedLogLevel(LogLevel.NONE);
            logFilter.setSessionOpenedLogLevel(LogLevel.NONE);
            logFilter.setSessionIdleLogLevel(LogLevel.NONE);
            logFilter.setMessageSentLogLevel(LogLevel.NONE);
            logFilter.setMessageReceivedLogLevel(LogLevel.DEBUG);
            logFilter.setSessionClosedLogLevel(LogLevel.NONE);
            this.acceptor.getFilterChain().addLast("logFilter1", logFilter);
            this.acceptor.getFilterChain().addLast("coderFilter", coderFilter);
            this.acceptor.getFilterChain().addLast("logger", new LoggingFilter());
            this.acceptor.setHandler(new MOServerHandler());
            this.acceptor.getFilterChain().addLast("exceutor",
                    new ExecutorFilter(new UnorderedThreadPoolExecutor(codeThread, maxThread)));
            int port = this.config.getInt("sgip_mo_port");
            this.acceptor.bind(new InetSocketAddress(port));
            log.info("Bind port=" + port);
        } catch (IOException e) {
            log.error("", e);
        }
    }

    public void dispose() {
        ExecutorFilter exceutor = (ExecutorFilter) this.acceptor.getFilterChain().get("exceutor");
        this.acceptor.dispose();
        if (exceutor != null) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                log.error("", e);
            }
            ThreadPoolExecutor service = (ThreadPoolExecutor) exceutor.getExecutor();
            service.shutdownNow();
        }
        log.info("Disposed!");
    }
}
