package com.eactive.eai.control;

import java.io.ByteArrayOutputStream;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;

import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.jms.JmsServiceLocator;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.Logger;

public class JMSTopicReceiver {
	static final long serialVersionUID = 1L;
    static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
    
    static final String JMSRES_CONTROL_ERROR    = "RECEAIORC001";
    static final String JMSRES_CONNNECTION_FAIL = "RECEAIORC100";
    static final String JMSRES_TIME_OUT         = "RECEAIORC200";
    
    private TopicConnectionFactory factory; 
    private TopicConnection con = null;
    private TopicSession ses = null;
    private Topic topic = null;
    private String jms_connectionfactory;
    private String topic_name;
    
    private static String topic_surfix;
    
    static
    {
        EAIServerManager eaiServerManager = EAIServerManager.getInstance();
        String serverName = eaiServerManager.getLocalServerName();
        
        if(serverName != null) {
			//juns 서버명 변경에 따른 인스턴스 룰 추출변경 
	        //ex)EAIDSG_i11 => EAIDS01_i01
			//topic_surfix = "_" +serverName.substring(serverName.length()-4);
			topic_surfix = "_" + serverName.substring(serverName.length()-5, serverName.length()-4)
	                           + serverName.substring(serverName.length()-1, serverName.length());
        }
    }
    
    public java.lang.Object receive(String topicConnectionFactory, String topicName, String traceNumber, int timeout) throws java.lang.Exception {
        jms_connectionfactory = topicConnectionFactory;
        topic_name = topicName + topic_surfix;
        startTopicConnection();
        
        TopicSubscriber subscriber = null;
        
        try {
			if (con != null && ses != null) {
                subscriber = ses.createSubscriber(topic, "JMSCorrelationID = '" + traceNumber + "'", true);
            
				Message message = (Message)subscriber.receive(timeout);
                                
                if (message == null) {
                    if (logger.isError()) logger.error("JmsResponseControl]요청에 대한 응답을 수신하지 못했습니다. JMSCorrelationID["+traceNumber+"]");
                    throw new Exception(JMSRES_TIME_OUT);
                }
                
                if (message instanceof ObjectMessage) {
                    return ((ObjectMessage)message).getObject();
                }                
                else if (message instanceof TextMessage) {
                    return ((TextMessage)message).getText();
                }
                else if (message instanceof BytesMessage) {                    
                    return getBytesMessage((BytesMessage)message);
                }               
                else {
                    return null;
                }
			}
            else {
                if (logger.isError()) logger.error("Topic Connection Failed [" + topicConnectionFactory + "]");
                throw new JMSException(JMSRES_CONNNECTION_FAIL);
            }
		} catch (Exception e) {	
            throw new Exception(ExceptionUtil.getErrorCode(e, JMSRES_CONTROL_ERROR));
		} finally {
            if (subscriber != null) {
                try { subscriber.close(); subscriber = null; } 
                catch (Throwable e) {
                	if (logger.isWarn()) logger.warn("subscriber.close Failed", e);
                }
            }
            close();
        }   
    }
    
    private byte[] getBytesMessage(BytesMessage message) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int getSize = -1;
        try {
            while(true) {
                getSize = message.readBytes(buff);
                if(getSize > 0) {
                        bos.write(buff, 0, getSize);
                }
                else {
                    break;
                }                        
            }
            return bos.toByteArray();
        } catch(Exception e) {
            throw new Exception("Read ByteMessage Error : " +e.getMessage());
        } finally {
            try {
                if(bos != null) bos.close();
            } catch(Exception ex) {
            	if (logger.isWarn()) logger.warn("bos.close Failed", ex);
            }
        }
        
    }  
    public void init() {
        //jms_connectionfactory = context.getControlAttribute("jc:jms-topic-receive", "jms-factory-jndi");
        //topic_name = context.getControlAttribute("jc:jms-topic-receive", "jms-topic-jndi");
        jms_connectionfactory = "";
        topic_name = "";
        
        startTopicConnection();
    }
    
    public void close() {
       closeTopicConnection();    
    }
    
    private void startTopicConnection() {
        Context ctx = null;
		try {
			JmsServiceLocator locator = JmsServiceLocator.getInstance();            
            
			factory = locator.getTopicConnectionFactory(jms_connectionfactory);
			if(factory == null) throw new Exception("factory not found - " + jms_connectionfactory);
			con = factory.createTopicConnection();
			if(con == null) throw new Exception("createQueueConnection return null");
			ses = con.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
			if(ses == null) throw new Exception("createTopicSession return null");
			topic = locator.getTopic(topic_name);
			con.start();
		} catch (Throwable e) {
			logger.error("startTopicConnection", e );
			closeTopicConnection();
			if (ctx != null) try { ctx.close(); ctx = null; } catch (Throwable ex) { 
				logger.warn("ctx.close error"); 
			}
		}
    }
    private void closeTopicConnection() {
		if (ses != null) try { ses.close(); ses = null; } catch (Throwable e) { 
			logger.warn("ses.close error"); 
		}
		if (con != null) try { con.stop(); } catch (Throwable e) { 
			logger.warn("con.stop error"); 
		}
    }
}
