package com.eactive.eai.adapter.socket2.protocol.proxy;

import com.eactive.eai.adapter.socket2.common.CommonLib;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.eactive.eai.adapter.socket2.protocol.SessionPool;
import org.apache.mina.common.*;
import org.apache.mina.transport.socket.nio.SocketConnector;

import java.net.InetSocketAddress;


public class ClientToProxyHandler extends AbstractProxyHandler {
	private final ServerToProxyHandler connectorHandler;
    private final IoConnector connector;
    private final InetSocketAddress[] addresses;

    public ClientToProxyHandler(ISocketAdapter adapter) {
		super(adapter);
		

		this.connectorHandler = new ServerToProxyHandler(adapter);
        this.connector = new SocketConnector();
        // Set connect timeout.
        ((IoConnectorConfig) connector.getDefaultConfig()).setConnectTimeout(30);
        ((IoConnectorConfig) connector.getDefaultConfig()).setThreadModel(ThreadModel.MANUAL); // 2014.07.07
        
        String[] hosts = context.getLocalHostName().split(";");
        this.addresses = new InetSocketAddress[hosts.length]; 
        for (int i=0; i<hosts.length; i++) { 	
        	this.addresses[i] = new InetSocketAddress(hosts[i], context.getLocalPortNumber());
        }

	}

    @Override
	public void sessionOpened(final IoSession session) throws Exception {
		for (int i=0; i<this.addresses.length; i++) {
	        ConnectFuture future = connector.connect(this.addresses[i], connectorHandler);
	        future.addListener(
	            new IoFutureListener() {
	                public void operationComplete(IoFuture f) {
	                    ConnectFuture future = (ConnectFuture) f;
	                    try {
	                        future.getSession().setAttachment(session);
	                            
	                        SessionPool pool = (SessionPool) session.getAttachment();
	                        if (pool == null)
	                            pool = new SessionPool(adapter, context);
	                            
							pool.release(future.getSession());
                            session.setAttachment(pool);
                            future.getSession().setTrafficMask(TrafficMask.ALL);
                        } catch (RuntimeIOException e) {
                        	logger.error(e);
                            // Connect failed
                            session.close();
                        } catch(Throwable t) {
                        	logger.error(t);
                        } finally {
                            session.setTrafficMask(TrafficMask.ALL);
                        }
                    }
                });
		}
    }
	
	@Override
    public void sessionClosed(IoSession session) throws Exception {
    	super.sessionClosed(session);
    	
    	SessionPool pool = (SessionPool) session.getAttachment();
        if (pool != null) {
            pool.shutdown();
            session.setAttachment(null);
        }
        
        session.close();
    }
	
	@Override
    public void messageReceived(IoSession session, Object message) throws Exception {
		logger.debug(session + CommonLib.hexDump((ByteBuffer) message, context));
		
		ByteBuffer rb = (ByteBuffer) message;
        ByteBuffer wb = ByteBuffer.allocate(rb.remaining());
        rb.mark();
        wb.put(rb);
        wb.flip();
        
        SessionPool pool = (SessionPool) session.getAttachment();
        if (pool != null) {
        	IoSession sess = null;
        	try {
	        	sess = pool.acquire();
	        	if(sess != null) sess.write(wb);
        	} finally {
        		if (sess != null)
        			pool.release(sess);
        	}
        }
        
        rb.reset();
    }
}
