package com.eactive.eai.batch.encrypt;

import com.eactive.eai.common.lifecycle.Lifecycle;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.lifecycle.LifecycleSupport;
//import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Hashtable;

/**
* 1. 기능 : 전문 생성시 사용자 암호를 생성한다.
* 2. 처리 개요 : CMS은행을 기준으로 암호화 및 복호화 알고리즘을 구현한다.
* 3. 주의사항
*
* @author  : 이윤철 (2006/1/6)
* @version : v 1.0.0
* @see : 관련 기능을 참조
* @since   :
*/
public class EncryptManager implements Lifecycle
{
	//private static Logger logger = LoggerFactory.getLogger(ElinkLogger.LOGGER_DEFAULT);

	/**
     * EncryptManager Single Instance
     */
    private static EncryptManager instance = new EncryptManager();

    /**
     * LifeccyleSupport object
     */
    private LifecycleSupport lifecycle = new LifecycleSupport(this);

    /**
     * 기동 여부
     */
    private boolean started;

    /**
     * 암호화 테이블
     */
    private static Hashtable<String, String> codeTable;

    /**
    * 1. 기능 :  Default Constructor
    * 2. 처리 개요 : Flow Rule 정보를 저장하기 위한 HashMap을 초기화한다.
    * 3. 주의사항
    *
    **/
    private EncryptManager() {
    	codeTable = new Hashtable<String, String>();
        initTable();
    }



    // 암호화 대수 테이블 초기화
    private static void initTable()
    {
        if(codeTable.isEmpty())
        {
            for(int i=65; i<91; i++)
            {
                String key   = new Character((char)i).toString();
                String value = new Integer(100 - i).toString();

                codeTable.put(key, value);
                codeTable.put(value, key);
            }

            for(int i=0; i<10; i++)
            {
                String key = new Integer(i).toString();
                String value = new Integer(9-i).toString();
                codeTable.put(key, value);
            }
            //logger.debug("** if Hashtable : A = : " + codeTable.get("A"));
            //logger.debug("** Hashtable  = : " + codeTable.toString());
        }
        else
        {
            //logger.debug("** else Hashtable : A = : " + codeTable.get("A"));
        }
    }


    /**
    * 1. 기능 :  EncryptManager Singleton Object를 반환하는 getter method
    * 2. 처리 개요 : EncryptManager Singleton Object를 반환한다.
    * 3. 주의사항
    *
    * @return   FlowRuleManager Singleton object
    **/
    public static EncryptManager getInstance() {
        if(instance == null)
            instance = new EncryptManager();
        return instance;
    }

    /* ****************************************************************** */
    /*                  EncryptManager 자체적인 메소드 정의               */
    /* ****************************************************************** */

    /**
     * 1. 기능 : 암호화 알고리즘
     * 2. 처리 개요 :
     *
     * 3. 주의사항
     *
     * @param   전송자명(영문), 전송자암호, 은행코드, 전송일(yymmdd)
     * @return   사용자 암호 평문을 암호화하여 반환
     * @exception
     **/
	 public String getEncrypt(String transferName, String transferPwd, String bankCode, String transferDate) throws Exception
     {
        /**
         *  1. 제한 조건
         *    - 거래일은 YYMMDD 형태(6자리)이다.
         *  2. 입력문장 예시
         *    - transferName = "Kimchungjin";        //전송자명 (영문)
         *      transferPwd = "1234567890123456";    //전송자암호평문
         *      bankCode = "02";                     //은행코드
         *      transferDate = "950815";             //전송년월일
        */

		 
		 //logger.debug( "[EncryptManager][getEncrypt]:transferName(" + transferName + "), transferPwd(" + transferPwd 
		//		        + "), bankCode(" + bankCode + "), transferDate(" + transferDate + ")");
        /* 변수 */
        String encryptSeed = "";      // encryptSeed = 은행코드(2)+거래일(6, yymmdd)+전송자명 앞 8자리, 총16자리
        String encryptMsg = "";       // 암호화된 결과를 보내는 문장

        /* 전송자명 체크
         *   - 전송자명이 8자리보다 적으면 빈자리는 Z로 채운다.
         *   - 전송자명이 8자리보다 크면 앞의 8자리만 사용한다.
         */
        transferName = transferName.toUpperCase();
        if(transferName.length() > 8)
            transferName = transferName.substring(0,8);
        else
        {
            for(; transferName.length() < 8; )
            {
                transferName += "Z";
            }
        }

        /* 전송자암호 체크 : 전송자 암호는 6자리이며 앞에서부터 반복해서 16자리로 만든다. */
        // 암호에 공백이 들어 갈수 없다. 2009.01.08
        if(transferPwd.trim().length() != 6)
            throw new Exception("전송자 암호 평문은 반드시 6자리이어야합니다.(공백불가) : " + transferPwd);
        transferPwd = transferPwd.toUpperCase();
        for(int i=0; transferPwd.length() != 16; i++)
        {
            transferPwd += transferPwd.substring(i, i+1);
        }
        //transferPwd = "1234567890123456"; for test...
        //logger.debug("transferPwd : " + transferPwd);

        /* 전송일 체크 */
        if(transferDate.length() != 6)
            throw new Exception("전송일은 반드시 6자리(YYMMDD)이어야합니다. : " + transferDate);

        /* 암호화 Seed를 생성한다. */
        encryptSeed = bankCode + transferDate + transferName;

        /* 암호화 문장 생성 */
        for(int i=0; i<16; i++)
        {
            int m_Msg = new Integer((String)codeTable.get(transferPwd.substring(i, i+1))).intValue();
            int m_Key = new Integer((String)codeTable.get(encryptSeed.substring(i, i+1))).intValue();
            encryptMsg += (String)codeTable.get(new Integer((m_Msg + m_Key) % 36).toString());
        }

        return encryptMsg;
    }

	 public String getEncryptForKCB(String transferName, String transferPwd, String bankCode, String transferDate) throws Exception
     {
        /**
         *  1. 제한 조건
         *    - 거래일은 YYMMDD 형태(6자리)이다.
         *  2. 입력문장 예시
         *    - transferName = "Kimchungjin";        //전송자명 (영문)
         *      transferPwd = "1234567890123456";    //전송자암호평문
         *      bankCode = "02";                     //은행코드
         *      transferDate = "950815";             //전송년월일
         *      적용 암호키 : A001050815Kimchu
        */

        /* 변수 */
        String encryptSeed = "";      // encryptSeed = 은행코드(A001)+거래일(6, yymmdd)+전송자명 앞 6자리, 총16자리
        String encryptMsg = "";       // 암호화된 결과를 보내는 문장

        /* 전송자명 체크
         *   - 전송자명이 6자리보다 적으면 빈자리는 Z로 채운다.
         *   - 전송자명이 6자리보다 크면 앞의 8자리만 사용한다.
         */
        transferName = transferName.toUpperCase();
        if(transferName.length() > 6)
            transferName = transferName.substring(0,6);
        else
        {
            for(; transferName.length() < 6; )
            {
                transferName += "Z";
            }
        }

        /* 전송자암호 체크 : 전송자 암호는 6자리이며 앞에서부터 반복해서 16자리로 만든다. */
        // 암호에 공백이 들어 갈수 없다. 2009.01.08
//        if(transferPwd.trim().length() != 16)
//            throw new Exception("전송자 암호 평문은 반드시 6자리이어야합니다.(공백불가) : " + transferPwd);
        transferPwd = transferPwd.toUpperCase();
        for(int i=0; transferPwd.length() != 16; i++)
        {
            transferPwd += transferPwd.substring(i, i+1);
        }
        //transferPwd = "1234567890123456"; for test...
        //logger.debug("transferPwd : " + transferPwd);

        /* 전송일 체크 */
        if(transferDate.length() != 6)
            throw new Exception("전송일은 반드시 6자리(YYMMDD)이어야합니다. : " + transferDate);

        /* 암호화 Seed를 생성한다. */
        encryptSeed = bankCode + transferDate + transferName;

        /* 암호화 문장 생성 */
        for(int i=0; i<16; i++)
        {
            int m_Msg = new Integer((String)codeTable.get(transferPwd.substring(i, i+1))).intValue();
            int m_Key = new Integer((String)codeTable.get(encryptSeed.substring(i, i+1))).intValue();
            encryptMsg += (String)codeTable.get(new Integer((m_Msg + m_Key) % 36).toString());
        }

        return encryptMsg;
    }

	 
    /**
     * 1. 기능 : 복호화 알고리즘 메인
     * 2. 처리 개요 :
     *
     * 3. 주의사항
     * @param 전달받은 사용자암호(암호화된것), 암호해독 seed
     * @return
     * @exception
     **/
    public String getDeEncrypt(String encryptMsg, String seed)
    {
        /* 변수 */
        String normalMsg = "";

        seed = seed.toUpperCase();

        for(int i=0; i<16; i++)
        {
            int m_Msg = new Integer((String)codeTable.get(encryptMsg.substring(i, i+1))).intValue() + 36;
            int m_Key = new Integer((String)codeTable.get(seed.substring(i, i+1))).intValue();
            normalMsg += (String)codeTable.get(new Integer((m_Msg - m_Key) % 36).toString());
        }

        return normalMsg;
    }

    /**
     * 1. 기능 : 복호화 알고리즘
     *           복화화 알고리즘 Seed를 개별로 받아들임
     * 2. 처리 개요 :
     *
     * 3. 주의사항
     * @param 전달받은 사용자암호(암호화된것), 은행코드(2), 전송년월일(6), 전송자명(8)
     * @return  사용자 암호 평문
     * @exception
     **/
    public String getDeEncrypt(String encryptMsg, String bankCode, String transferDate, String transferName)
    {
        /* 전송자명 체크
         *   - 전송자명이 8자리보다 적으면 빈자리는 Z로 채운다.
         *   - 전송자명이 8자리보다 크면 앞의 8자리만 사용한다.
         */
        transferName = transferName.toUpperCase();
        if(transferName.length() > 8)
            transferName = transferName.substring(0,8);
        else
        {
            for(; transferName.length() < 8; )
            {
                transferName += "Z";
            }
        }
        String seed = bankCode + transferDate + transferName;
        return this.getDeEncrypt(encryptMsg, seed);
    }
    
    /**
     * 1. 기능 : 암호화 알고리즘
     * 2. 처리 개요 : this.getEncrypt(String, String, String, String)메소드는 코드내에 필요한 데이터를 추출(예. 전송자명 앞 8자리 추출)
     *             getEncryptPwd 메소드는 메소드를 호출하는 부분에서 필요한 모든 데이터의 값을 정의해서 호출하고,
     *             해당 메소드내에서는 전달된 값으로 암호화하는 작업만 수행하도록 한다.
     * 3. 주의사항
     *
     * @param   전송자명(영문), 전송자암호, 은행코드, 전송일(yymmdd)
     * @return   사용자 암호 평문을 암호화하여 반환
     * @exception
     **/
	 public String getEncryptPwd(String transferName, String transferPwd, String bankCode, String transferDate) throws Exception
     {
        /**
         *  1. 제한 조건
         *    - 거래일은 YYMMDD 형태(6자리)이다.
         *  2. 입력문장 예시
         *    - transferName = "KIMHYUNKUK";        //전송자명 (영문)
         *      transferPwd = "1234567890123456";    //전송자암호평문
         *      bankCode = "004";                     //은행코드
         *      transferDate = 2008년 11월 13일;             //전송년월일
        */

        /* 변수 */
        String encryptSeed = "";      // 적용암호키
        String encryptMsg = "";       // 암호화된 결과를 보내는 문장


        transferName = transferName.toUpperCase();
        

        /* 전송자암호 체크 : 전송자 암호는 6자리이며 앞에서부터 반복해서 16자리로 만든다. */
        // 암호에 공백이 들어 갈수 없다. 2009.01.08
        if(transferPwd.trim().length() != 6)
            throw new Exception("전송자 암호 평문은 반드시 6자리이어야합니다.(공백불가) : " + transferPwd);
        transferPwd = transferPwd.toUpperCase();
        for(int i=0; transferPwd.length() != 16; i++)
        {
            transferPwd += transferPwd.substring(i, i+1);
        }

        /* 전송일 체크 */
        if(transferDate.length() != 6)
            throw new Exception("전송일은 반드시 6자리(YYMMDD)이어야합니다. : " + transferDate);

        /* 암호화 Seed를 생성한다. */
        encryptSeed = bankCode + transferDate + transferName;

        /* 암호화 문장 생성 */
        for(int i=0; i<transferPwd.length(); i++)
        {
            int m_Msg = new Integer((String)codeTable.get(transferPwd.substring(i, i+1))).intValue();
            int m_Key = new Integer((String)codeTable.get(encryptSeed.substring(i, i+1))).intValue();
            encryptMsg += (String)codeTable.get(new Integer((m_Msg + m_Key) % 36).toString());
        }

        return encryptMsg;
    }
	 
	 /**
	     * 1. 기능 : 복호화 알고리즘
	     *           복화화 알고리즘 Seed를 개별로 받아들임.
	     *           
	     * 2. 처리 개요 :this.getDeEncrypt(String, String, String, String)메소드는 코드내에 필요한 데이터를 추출(예. 전송자명 앞 8자리 추출)
     *             getDeEncryptPwd 메소드는 메소드를 호출하는 부분에서 필요한 모든 데이터의 값을 정의해서 호출하고,
     *             해당 메소드내에서는 전달된 값으로 암호화하는 작업만 수행하도록 한다.
	     *
	     * 3. 주의사항
	     * @param 전달받은 사용자암호(암호화된것), 은행코드(2), 전송년월일(6), 전송자명(8)
	     * @return  사용자 암호 평문
	     * @exception
	     **/
	 public String getDeEncryptPwd(String encryptMsg, String bankCode, String transferDate, String transferName)
	    {
	        /* 전송자명 체크
	         *   - 전송자명이 8자리보다 적으면 빈자리는 Z로 채운다.
	         *   - 전송자명이 8자리보다 크면 앞의 8자리만 사용한다.
	         */
	        transferName = transferName.toUpperCase();
	        String seed = bankCode + transferDate + transferName;
	        return this.getDeEncrypt(encryptMsg, seed);
	    }


    /* ****************************************************************** */
    /*                  Lifecycle 인터페이스에 정의된 메소드              */
    /* ****************************************************************** */

    /**
    * 1. 기능 :  Lifecycle의 start 메서드로 EAIServerMangaer를 초기화하는 메서드
    * 2. 처리 개요 : EAIServerDAO를 이용해 추출 Rule 정보 모두를 가져와 초기화한다.
    * 3. 주의사항
    *
    * @exception    LifecycleException  이미 시작되었거나,
    *               EAIServerDAO를 통해 Rule 정보를 가져온다.
    *               DAOExcepiton이 발생될 경우
    **/
    public void start() throws LifecycleException
    {
        if (started)
            throw new LifecycleException("BECEAICEH001");   //EncryptManager 가 이미 시작 되었습니다.

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(STARTING_EVENT, this);

        // 암호화 테이블을 초기화 한다.
        initTable();

        started = true;

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
    }

    /**
    * 1. 기능 :  Lifecycle의 stop 메서드로 EAIServerManager를 종료하는 메서드
    * 2. 처리 개요 : 멤버에 캐싱항 EAIServer Rule 정보를 clear한다.
    * 3. 주의사항
    *
    * @exception    LifecycleException  이미 종료된 경우 발생
    **/
    public void stop() throws LifecycleException {
        // Validate and update our current component state
        if (!started)
            throw new LifecycleException("BECEAICEH002");   //EncryptManager 가 이미 종료 되었습니다.

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(STOPING_EVENT, this);


        started = false;
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(STOPPED_EVENT, this);
    }

    /**
    * 1. 기능 :  LifecycleListener를 등록하는 메서드
    * 2. 처리 개요 : LifecycleListener를 등록한다.
    * 3. 주의사항
    *
    * @param    listener    LifecycleEvent를 수신한 LifecycleListener
    **/
    public void addLifecycleListener(LifecycleListener listener)
    {
        lifecycle.addLifecycleListener(listener);
    }

    /**
    * 1. 기능 :  등록된 LifecycleListener 리스트를 반환하는 메서드
    * 2. 처리 개요 : 등록된 LifecycleListener 리스트를 반환하다.
    * 3. 주의사항
    *
    * @return   등록된 LifecycleListener 리스트
    **/
    public LifecycleListener[] findLifecycleListeners()
    {
        return lifecycle.findLifecycleListeners();
    }

    /**
    * 1. 기능 :  등록된 LifecycleListener를 삭제하는 메서드
    * 2. 처리 개요 : 파라미터의 LifecycleListener를 삭제한다.
    * 3. 주의사항
    *
    * @param    listener    삭제할 LifecycleListener
    **/
    public void removeLifecycleListener(LifecycleListener listener)
    {
        lifecycle.removeLifecycleListener(listener);
    }

    /**
    * 1. 기능 :  FlowRuleManager의 초기화 여부를 반환하는 getter 메서드
    * 2. 처리 개요 : FlowRuleManager의 초기화 여부를 반환한다.
    * 3. 주의사항
    *
    * @return   초기화 여부
    **/
    public boolean isStarted() {
        return this.started;
    }

}

