package org.maachang.util;

/**
 * バイナリ操作ユーティリティ.
 * 
 * @version 2008/07/02
 * @author masahito suzuki
 * @since MaachangBase 1.04
 */
public class BinaryUtil {
    
    /**
     * バイナリ検索.
     * @param binary 対象のバイナリを設定します.
     * @param data 検索データを設定します.
     * @param off 検索開始位置を設定します.
     * @return int 検索結果位置が返されます.<br>
     *             [-1]の場合は見つかりませんでした.
     * @exception Exception 例外.
     */
    public static final int binaryIndexOf( byte[] binary,byte[] data,int off )
        throws Exception {
        return binaryIndexOf( binary,data,off,0 ) ;
    }
    
    /**
     * バイナリ検索.
     * @param binary 対象のバイナリを設定します.
     * @param data 検索データを設定します.
     * @param off 検索開始位置を設定します.
     * @param len 検索サイズを設定します.
     * @return int 検索結果位置が返されます.<br>
     *             [-1]の場合は見つかりませんでした.
     * @exception Exception 例外.
     */
    public static final int binaryIndexOf( byte[] binary,byte[] data,int off,int len )
        throws Exception {
        if( binary == null || binary.length <= 0 ||
            data == null || data.length <= 0 ) {
            return -1 ;
        }
        int dataLen = data.length ;
        if( len <= 0 || len >= binary.length ) {
            len = binary.length ;
        }
        int ret = -1 ;
        for( int i = off ; i < len ; i ++ ) {
            if( binary[ i ] == data[ 0 ] && i+dataLen <= len ) {
                ret = i ;
                for( int j = 1 ; j < dataLen ; j ++ ) {
                    if( binary[ i+j ] != data[ j ] ) {
                        ret = -1 ;
                        break ;
                    }
                }
                if( ret != -1 ) {
                    return ret ;
                }
            }
        }
        return -1 ;
    }
    
    /**
     * 指定範囲内のバイナリを取得.
     * @param binary 対象のバイナリを設定します.
     * @param off 開始ポジションを設定します.
     * @return byte[] バイナリが返されます.
     * @exception Exception 例外.
     */
    public static final byte[] subBinary( byte[] binary,int off )
        throws Exception {
        return subBinary( binary,off,0 ) ;
    }
    
    /**
     * 指定範囲内のバイナリを取得.
     * @param binary 対象のバイナリを設定します.
     * @param off 開始ポジションを設定します.
     * @param len 開始ポジションからの長さを設定します.
     * @return byte[] バイナリが返されます.
     * @exception Exception 例外.
     */
    public static final byte[] subBinary( byte[] binary,int off,int len )
        throws Exception {
        if( binary == null || binary.length <= 0 || off <= -1 || off >= binary.length ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( len <= 0 ) {
            len = binary.length ;
        }
        if( binary.length <= off + len ) {
            len = binary.length - off ;
        }
        byte[] ret = new byte[ len ] ;
        System.arraycopy( binary,off,ret,0,len ) ;
        return ret ;
    }
    
    /**
     * 指定内容を置き換える.
     * @param binary 対象のバイナリを設定します.
     * @param src 変換元のバイナリを設定します.
     * @param dest 変換先のバイナリを設定します.
     * @return byte[] 変換されたバイナリが返されます.
     * @exception Exception 例外.
     */
    public static final byte[] changeBinary( byte[] binary,byte[] src,byte[] dest )
        throws Exception {
        int pnt;
        int end;
        int srcLen;
        ArrayBinary buf = null;
        if (binary == null || binary.length <= 0 || src == null
                || (srcLen = src.length) <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }
        if (dest == null || dest.length <= 0) {
            dest = new byte[ 0 ] ;
        }
        buf = new ArrayBinary();
        pnt = binaryIndexOf( binary,src,0 ) ;
        if (pnt == -1) {
            return binary;
        } else if (pnt == 0) {
            buf.write(dest);
            end = srcLen;
        } else {
            buf.write(subBinary(binary,0,pnt));
            buf.write(dest);
            end = pnt + srcLen;
        }
        for (pnt += srcLen;;) {
            
            if ((pnt = binaryIndexOf( binary, src, pnt)) == -1) {
                buf.write(subBinary(binary,end));
                break;
            }
            if (pnt == end) {
                buf.write(dest);
            } else {
                buf.write(subBinary(binary,end,pnt-end)) ;
                buf.write(dest);
            }
            end = pnt + srcLen;
            pnt += srcLen;
        }
        return buf.getBinary();
    }
    
    /**
     * バイナリを文字列表示.
     * @param binary 対象のバイナリを設定します.
     * @return String 文字列が返されます.
     */
    public static final String toString( byte[] binary ) {
        if( binary == null ) {
            return "" ;
        }
        int len = binary.length ;
        StringBuilder buf = new StringBuilder() ;
        buf.append( "        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F  0123456789ABCDEF\n" ) ;
        int next = 0 ;
        int loop1 = len-16 ;
        for( int i = 0 ; i < loop1 ; i += 16 ) {
            String cntStr = Integer.toHexString( i ) ;
            buf.append( "000000".substring( cntStr.length() ) ).append( cntStr ).append( "  " ) ;
            for( int j = 0 ; j < 16 ; j ++ ) {
                String s = Integer.toHexString( ( int )( binary[ i+j ] & 0x000000ff ) ) ;
                if( s.length() == 1 ) {
                    buf.append( "0" ) ;
                }
                buf.append( s ).append( " " ) ;
            }
            buf.append( " " ) ;
            for( int j = 0 ; j < 16 ; j ++ ) {
                char o = ( char )( binary[ i+j ] & 0x0000ffff ) ;
                if( ( o >= 'a' && o <= 'z' ) || ( o >= 'A' && o <= 'Z' ) ||
                    ( o >= '0' && o <= '9' ) || o == ' ' || o == ',' || o == '.' ||
                    o == '(' || o == ')' || o == '{' || o == '}' || o == '@' || o == '*' ||
                    o == '+' || o == '-' || o == '=' || o == '/' || o == '[' || o == ']' ||
                    o == '_' || o == '#' || o == '%' || o == '$' || o == '&' || o == '\'' ||
                    o == '\"' || o == '^' || o == '~' || o == '\\' || o == '|' || o == '!' ||
                    o == '?' || o == ';' || o == ':' || o == '<' || o == '>' ) {
                    buf.append( o ) ;
                }
                else {
                    buf.append( "･" ) ;
                }
            }
            buf.append( "\n" ) ;
            next ++ ;
        }
        if( ( len & 0x0000000f ) != 0 ) {
            String cntStr = Integer.toHexString( next * 16 ) ;
            buf.append( "000000".substring( cntStr.length() ) ).append( cntStr ).append( "  " ) ;
            int pnt = next * 16 ;
            len = len - pnt ;
            for( int i = 0 ; i < len ; i ++ ) {
                String s = Integer.toHexString( ( int )( binary[ i+pnt ] & 0x000000ff ) ) ;
                if( s.length() == 1 ) {
                    buf.append( "0" ) ;
                }
                buf.append( s ).append( " " ) ;
            }
            for( int i = len ; i < 16 ; i ++ ) {
                buf.append( "   " ) ;
            }
            buf.append( " " ) ;
            for( int i = 0 ; i < len ; i ++ ) {
                char o = ( char )( binary[ i+pnt ] & 0x0000ffff ) ;
                if( ( o >= 'a' && o <= 'z' ) || ( o >= 'A' && o <= 'Z' ) ||
                    ( o >= '0' && o <= '9' ) || o == ' ' || o == ',' || o == '.' ||
                    o == '(' || o == ')' || o == '{' || o == '}' || o == '@' || o == '*' ||
                    o == '+' || o == '-' || o == '=' || o == '/' || o == '[' || o == ']' ||
                    o == '_' || o == '#' || o == '%' || o == '$' || o == '&' || o == '\'' ||
                    o == '\"' || o == '^' || o == '~' || o == '\\' || o == '|' || o == '!' ||
                    o == '?' || o == ';' || o == ':' || o == '<' || o == '>' ) {
                    buf.append( o ) ;
                }
                else {
                    buf.append( "･" ) ;
                }
            }
            buf.append( "\n" ) ;
        }
        return buf.toString() ;
    }
}
