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();
    }
    
}
