/*
 * 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.Checksum.sum8;

import java.nio.charset.Charset;

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

/**
 * winny node
 * 
 * @author nori090
 * @version $Rev: 17 $ $Date: 2008-03-25 22:45:24 +0900 (Tue, 25 Mar 2008) $
 */
public class Node {
    private static final byte[] NODE_KEY =
        new byte[] { 0x6f, 0x70, 0x69, 0x65, 0x77, 0x66, 0x36, 0x61, 0x73, 0x63, 0x78, 0x6c, 0x76 };

    private static final String KEY = "opiewf6ascxlv";

    String address;

    int port;

    String node_string;

    public Node() {
        super();
    }

    public Node( String address, int port )
        throws WinnyProtocolException {
        super();
        this.address = address;
        this.port = port;
        this.node_string = pack( address, port );
    }

    public Node( String node_string )
        throws WinnyProtocolException {
        super();
        Node node = unpack( node_string );
        this.address = node.address;
        this.port = node.port;
        this.node_string = node_string;
    }

    public Node( String address, int port, String node_string ) {
        super();
        this.address = address;
        this.port = port;
        this.node_string = node_string;
    }

    /**
     * unpack node
     * 
     * @param node_string2
     * @return node
     * @throws WinnyProtocolException
     */
    Node unpack( String node_string )
        throws WinnyProtocolException {
        // TODO check data format
        // nodeのサイズが20以上、文字数が@を除くと偶数であること、nodeは@から始まること

        try {
            byte checksum = Hex.decodeHex( node_string.substring( 1, 3 ).toCharArray() )[0];
            byte[] target = Hex.decodeHex( node_string.substring( 3 ).toCharArray() );
            byte[] key = NODE_KEY;
            key[0] = checksum;

            byte[] unpack = RC4.decrypt( key, target );
            byte unpack_sum = sum8( unpack );
            if ( checksum != unpack_sum ) {
                throw new WinnyProtocolException( "sum check error" );
            }
            String host = new String( unpack );
            return new Node( host.substring( 0, host.indexOf( ':' ) ),
                             Integer.parseInt( host.substring( host.indexOf( ':' ) + 1 ) ), node_string );
        }
        catch ( Exception e ) {
            throw new WinnyProtocolException( e );
        }
    }

    /**
     * pack node
     * 
     * @param address2
     * @param port2
     * @return node_string
     * @throws WinnyProtocolException
     */
    String pack( String address, int port )
        throws WinnyProtocolException {
        // TODO キレイに
        String target = address + ":" + port;
        byte[] src = target.getBytes();
        byte[] key = NODE_KEY;
        byte checksum = sum8( src );
        key[0] = checksum;
        try {
            byte[] pack = RC4.encrypt( key, src );
            return new StringBuilder( "@" ).append( Hex.encodeHex( new byte[] { checksum } ) ).append(
                                                                                                       Hex.encodeHex( pack ) ).toString();
        }
        catch ( Exception e ) {
            throw new WinnyProtocolException( e );
        }
    }

    public String getAddress() {
        return address;
    }

    public void setAddress( String address ) {
        this.address = address;
    }

    public int getPort() {
        return port;
    }

    public void setPort( int port ) {
        this.port = port;
    }

    public String getNode_string() {
        return node_string;
    }

    public void setNode_string( String node_string ) {
        this.node_string = node_string;
    }

    public static void main( String[] args ) {
        // TODO テストを追加する。
        try {
            System.out.println( Hex.encodeHex( NODE_KEY ) );
            System.out.println( Charset.defaultCharset().toString().equals( "windows-31j" ) );
            System.out.println( Hex.encodeHex( KEY.getBytes() ) );
            Node n = new Node( "123.1.2.3", 1234 );
            System.out.println( n.node_string );
            Node n1 = n.unpack( n.node_string );
            System.out.println( n1.address );
            System.out.println( n1.port );
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
}
