/*
 * Copyright 2007 nori090
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jimmy;

import static jimmy.ConvertUtil.littleEndianBytes2int;

import java.io.InputStream;
import java.net.Socket;
import java.util.Arrays;

import jimmy.command.Command;
import jimmy.command.Command00;
import jimmy.command.Command01;
import jimmy.command.Command02;
import jimmy.command.Command03;
import junit.framework.TestCase;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.ArrayUtils;

/**
 * dump winny network
 * 
 * @author nori090
 * @version $Rev: 17 $ $Date: 2008-03-25 22:45:24 +0900 (Tue, 25 Mar 2008) $
 */
public class SocketDumpTest
    extends TestCase {
    public void test() {
        Socket s = null;
        InputStream in = null;
        try {
            s = new Socket( "192.168.77.128", 6191 );
            s.setSoTimeout( 5000 );
            in = s.getInputStream();
            byte[] key = new byte[6];
            in.read( key );
            byte[] rc4key = ArrayUtils.subarray( key, 2, key.length );
            byte[] head = new byte[5];
            in.read( head );
            ARC4Cipher rc4 = new ARC4Cipher( rc4key );
            head = rc4.decrypt( head );
            assertEquals( "0100000061", String.valueOf( Hex.encodeHex( head ) ) );

            // cmd00 test
            head = new byte[5];
            if ( in.read( head ) != 5 ) {
                throw new Exception();
            }
            byte[] h_unpack = rc4.decrypt( head );
            print( "h_unpack", h_unpack );
            int block_length = littleEndianBytes2int( ArrayUtils.subarray( h_unpack, 0, 4 ) );
            System.out.println( "block_length:" + block_length );
            byte[] data = new byte[block_length - 1];
            if ( in.read( data ) != ( block_length - 1 ) ) {
                throw new Exception();
            }
            byte[] join = ArrayUtils.addAll( h_unpack, rc4.decrypt( data ) );
            print( "join array", join );
            Command c = Command.getCommandInstance( join );
            System.out.println( c.getClass().getSimpleName() );
            Command00 c00 = (Command00) c;
            System.out.println( "cmd00:" + c00.getMajor() + ":" + c00.getMinor() );
            assertEquals( "Winny Ver2.0b1  ", c00.getMajor() );
            assertEquals( 12710, c00.getMinor() );

            rc4key = Command.toStep2Key( rc4key );
            rc4 = new ARC4Cipher( rc4key );
            boolean bleak_flg = false;
            while ( !bleak_flg ) {
                int count = in.available();
                if ( count >= 5 ) {
                    head = new byte[5];
                    if ( in.read( head ) != 5 ) {
                        throw new Exception();
                    }
                    h_unpack = rc4.decrypt( head );
                    print( "h_unpack", h_unpack );
                    block_length = littleEndianBytes2int( ArrayUtils.subarray( h_unpack, 0, 4 ) );
                    System.out.println( "block_length:" + block_length );
                    data = new byte[block_length - 1];
                    if ( in.read( data ) != ( block_length - 1 ) ) {
                        throw new Exception();
                    }
                    join = ArrayUtils.addAll( h_unpack, rc4.decrypt( data ) );
                    print( "join array", join );
                    c = Command.getCommandInstance( join );
                    String cname = c.getClass().getSimpleName();
                    System.out.println( cname );
                    // cmd01-03 test
                    if ( cname.equals( "Command01" ) ) {
                        Command01 c01 = (Command01) c;
                        System.out.println( "cmd01:接続先転送速度:" + c01.getSpeed() );
                    }
                    else if ( cname.equals( "Command02" ) ) {
                        Command02 c02 = (Command02) c;
                        System.out.println( "cmd02:接続タイプ:" + c02.getType() );
                        System.out.println( "cmd02:ポート0接続か:" + c02.isPort0() );
                        System.out.println( "cmd02:諸事情によりポート0接続か:" + c02.isBadPort0() );
                        System.out.println( "cmd02:BBS接続か:" + c02.isBbsLink() );
                    }
                    else if ( cname.equals( "Command03" ) ) {
                        Command03 c03 = (Command03) c;
                        System.out.println("cmd03:address:"+c03.getAddress().getHostAddress());
                        System.out.println("cmd03:port:"+c03.getListen_port());
                        System.out.println("cmd03:ddns:"+c03.getDdns_name());
                        System.out.println("cmd03:クラスタキー:"+Arrays.toString( c03.getClustering_key() ));
                        bleak_flg = true;
                    }
                }
            }
        }
        catch ( Exception e ) {
            e.printStackTrace();
            fail();
        }
        finally {
            try {
                if ( in != null )
                    in.close();
                if ( s != null )
                    s.close();
            }
            catch ( Exception e ) {
                e.printStackTrace();
            }
        }
    }

    void print( String s, byte[] bs ) {
        try {
            System.out.format( "%s: [%s]\n", s, String.valueOf( Hex.encodeHex( bs ) ) );
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
}
