package com.eactive.eai.transformer.util;


import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.text.MessageFormat;

import com.eactive.eai.transformer.SystemKeys;


/**
 * @author janet
 * 송수신 메시지 SPEC.
 * llzz + syncFlag + user data(전문포맷전체를 사용자 DATA로 본다.)
 * - llzz(2BYTE/4BYTE) UNSIGNED INTEGER로 처리한다. -- ConfigurationContext에 저장 
 *   default는 4byte로 한다.
 * - syncFlag - 'S' - R/R, 'A' - ASYNC, 'K' - ACK ONLY(이 경우, SEVER의 ACK를 받는다.
 * -                                    llzz+K[ACK|NACK]
 * - user data,...
 */
public class CommonLib {

    public static final char   SPACE = ' ';

    public static final char   ZERO  = '0';
    
    public static final String EMPTY_STRING = "";

    private static final String[] EMPTY_STRING_ARRAY = new String[0];
    
    private static final int NEGOTIATING_PROTOCOL = 1;
    
    private static final int PROCESSING_PROTOCOL  = 2;
    
    private static final byte[] SYNC_PROTOCOL_MESSAGE = "SYNC".getBytes();
    
    private static final byte[] ASYNC_PROTOCOL_MESSAGE = "ASYN".getBytes();
    
    private static final byte[] NACK_MESSAGE = "NACK PROTOCOL VIOLATION".getBytes();
    
    private static final byte[] ACK_MESSAGE  = "ACK".getBytes();
    
    private static final char[] HEX_CHAR_ARRAY = { '0', '1', '2', '3', '4',
    '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    
    private CommonLib() {
    	
    }
    
    /**
     * BYTE ARRAY값을 HEXA STRING으로 변환하여 String을 리턴한다.
     * @param abyte
     * @return
     */
    public static String byte2Hex(byte[] abyte) {
        /*
        StringBuilder s = new StringBuilder();
        if (abyte == null)
            return s.toString();
        for (int i = 0; i < abyte.length; i++)
            s.append( Integer.toHexString((abyte[i] & 0xf0) >> 4).toUpperCase() ).append( Integer.toHexString(abyte[i] & 0xf).toUpperCase() );
        return s.toString();
        */
        
        //-------------------------------------------------
        // New Logic from cjw
        //-------------------------------------------------
        StringBuilder buf = new StringBuilder();
        int len = abyte.length;

        int high = 0;
        int low = 0;
        for (int i = 0; i < len; i++) {
            high = ((abyte[i] & 0xf0) >> 4);
            low = (abyte[i] & 0x0f);
            buf.append(HEX_CHAR_ARRAY[high]);
            buf.append(HEX_CHAR_ARRAY[low]);
        }
        return buf.toString();
        //-------------------------------------------------        
        
    }

    /**
     * HEXA String값을 BYTE ARRAY로 변환한다.
     * @param s
     * @return
     */
    public static byte[] hex2Bytes(String s) {
        byte[] abyte = new byte[s.length() / 2];
        for (int j = 0; j < abyte.length; j++)
            abyte[j] = (byte) Integer.parseInt(s.substring(2 * j, 2 * j + 2), 16);
        return abyte;
    }
    
    /**
     * 주어진 String값을 주어진 길이많큼 Padding/Trim한다.
     * @param svalue - 입력스트링
     * @param isRightJustify - true이면 RIGHT JUSTIFY, false이면 LEFT JUSTIFY 
     * @param padding - Padding 문자
     * @param length - 리턴할 스트링의 바이트수
     * @return 포맷팅된 String결과 
     */
    public static String stringFormat(String svalue, boolean isRightJustify, char padding, int length) {
        if ( svalue == null ) return svalue;
        
        StringBuilder mpad 	= new StringBuilder();
        StringBuilder fmtStr = new StringBuilder();
        
        String tvalue 	= svalue;
        int pLength 	= 0;

        pLength = length - svalue.getBytes().length;  
        
        if (pLength == 0) 
            return svalue;
        else if ( pLength < 0) {
            byte[] abytes = null;
            if (isRightJustify) {
            	abytes = (new String( svalue.getBytes(), -(pLength), length)).getBytes(); 
            } else {
            	abytes = (new String( svalue.getBytes(), 0, length)).getBytes();
            }
            
            if ( abytes.length == length ) {
                return  new String( abytes );
            } else {
              tvalue = new String( abytes );
              pLength = length - tvalue.length();
            }
        }
        
        for(int i =0; i < pLength ; i++) {
            mpad.append( padding );
        }
        
        if ( isRightJustify ) {
            return fmtStr.append(mpad).append(tvalue).toString();
        } else {
            return fmtStr.append(tvalue).append(mpad).toString();
        }
    }
    
    public static java.lang.String getFormatString(long value, int length)
    {
        DecimalFormat df = new DecimalFormat("000000000000000000");
        String  fs = df.format( value );
        return fs.substring(fs.length() - length);
    }

    public static java.lang.String getFormatString(int value, int length)
    {
        DecimalFormat df = new DecimalFormat("000000000000000000");
        String  fs = df.format((long)value);
        return fs.substring(fs.length() - length);
    }
    
    /**
     * 메시지 비교
     */
    public static boolean compare(byte[] source, byte[] target) {
        if ( source.length != target.length ) return false;
        
        for ( int i=0; i < source.length; i++ ) {
            if ( source[i] != target[i] ) return false;
        }
        return true;
    }

    /**
     * 송수신전문에 대한 DUMP를 프린트할 수 있는 포맷으로 변환하여 String Array로 리턴한다.
     * @param bytes
     * @return
     */
    public static String[] makeDumpFormat(byte[] bytes) {
        if (bytes == null || bytes.length == 0)
            return CommonLib.EMPTY_STRING_ARRAY;

        String[] dumps = new String[bytes.length / 16 + ((bytes.length % 16) == 0 ? 4 : 5)];
        dumps[0]	= "    +----------------------------------------------------------------------+";
        dumps[1]	= "    |  Offset  | 0 1 2 3   4 5 6 7   8 9 A B   C D E F  | U S E R  D A T A |";
        dumps[2]	= "    |----------+----------------------------------------+------------------|";
        dumps[dumps.length - 1]	= "    +----------------------------------------------------------------------+";
        
        int		 len   = 0;
        byte	 PERIOD = 0x2E;
        char     aChar  = ' ';
        boolean  isDBCS = false;
        boolean  isHalfDBCS = false;

        
        for (int i = 3; i < dumps.length - 1; i++) {
        
            len = ( ((bytes.length - (i - 2) * 16) >= 0) ? 16 : (bytes.length - (i - 3)*16) );
            byte[]   buf   = new byte[len];
            System.arraycopy( bytes, (i-3)*16, buf, 0, len );
            StringBuilder sb = new StringBuilder();
            //dumps[i] = EMPTY_STRING;
            
            sb.append("    | ").append( stringFormat( new String( Integer.toHexString((i-3)*16) ).toUpperCase(), true, ZERO, 8) ).append(" | ");
            
            String	 	 hexStr = byte2Hex( buf );
            StringBuilder fmtStr = new StringBuilder();
            
            int		 k = hexStr.length()/8 + ((hexStr.length() % 8) == 0 ? 0 : 1);
            for (int j=0; j < k ; j++) {
                int l = ((hexStr.length() - (j+1)*8 ) >= 0 ? 8 : (hexStr.length() - j*8 ));
                fmtStr.append( hexStr.substring(j*8, j*8 + l ) ).append("  ");
            }
            
            sb.append( stringFormat(fmtStr.toString(), false, SPACE, 39) ).append("| ");

            k = 0;
            // 한글 및 특수문자가 깨지게 표시되는 것 을 방지한다.
            for ( int j=0; j < len; j++) {
                if ( buf[j] >> 7 == 0 ) {
                    // Single Byte Character
                    if ( isDBCS ) {
                        isDBCS = false;
                        if ( k != 0 &&  ((k % 2) == 1 )) {
                            buf[j - 1] = PERIOD;
                        }
                    }
                    aChar = (char)buf[j];
                    if ( Character.isWhitespace( aChar ) || Character.isISOControl( aChar ) || buf[j] == 0 ) {
                        buf[j] = PERIOD;
                    }
                    continue;
                }
                // Double Bytes Character.
                if ( !isDBCS ) isDBCS = true;
                if ( j == 0 && isHalfDBCS ) {
                    buf[j] = PERIOD;
                    isDBCS = false;
                    continue;
                }
                // To check the DBCS pairwise
                k++;
            }
            if ( isDBCS && ((k%2)==1) ) {
                buf[ len -1 ] = PERIOD;
                isHalfDBCS = true;
            } else {
                isHalfDBCS = false;
            }
            
            try {
				dumps[i] =  sb.append( stringFormat(new String(buf, SystemKeys.getBytesMessageCharset()), false, SPACE, 16) ).append(" |").toString();
			} catch (UnsupportedEncodingException e) {
				dumps[i] =  sb.append( stringFormat(new String(buf), false, SPACE, 16) ).append(" |").toString();
			}
        }
        return dumps;

    }

    public static String hexDump(byte[] message, boolean printBytesInfo) {
        String[]    dumpMsg = makeDumpFormat( message );
        StringBuilder   dump = new StringBuilder();
        if (printBytesInfo) {
        	dump.append("byte[size=" + message.length + "]");
        }
        dump.append("\n");
        for(int i=0; i < dumpMsg.length; i++) {
            dump.append(dumpMsg[i]);
            if (i < dumpMsg.length-1)
            	dump.append("\n");
        }
        
        return dump.toString();
    }	
    
    public static String hexDump(byte[] message) {
    	return hexDump(message, true);
    }

    public static String hexDumpTab(byte[] message) {
    	return hexDump(message, true).replaceAll("\n    (\\+|\\|)", "\n" + Constants.TAB + " \\|    $1");
    }

    public static String ebcdicHexDumpTab(byte[] message) {
    	byte[] bytes = null; 
		String asc = IConv.ebcdicToASCIISOSISpaceEX(message);
		bytes = asc.getBytes();
    	return hexDumpTab(bytes);
    }	

    private static final MessageFormat ELAPSED_TIME_FORMAT = new MessageFormat("{0}.{1}.{2}");
    public static String elapsedTime(long time) {
    	long elapsed = System.currentTimeMillis() - time;
        long seconds = elapsed / 1000;
        long minutes = seconds / 60;
        long hours = minutes / 60;
        Object[] args = { Long.valueOf(hours), Long.valueOf(minutes%60), Long.valueOf(seconds % 60)};
        return ELAPSED_TIME_FORMAT.format(args);
    }     
}
