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

import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.util.ArrayList;

import jimmy.ConvertUtil;
import jimmy.Node;
import jimmy.Query;
import jimmy.WinnyProtocolException;
import jimmy.util._ByteArrayInputStream;

import org.apache.commons.lang.ArrayUtils;

/**
 * winny send query command
 * 
 * @author nori090
 * @version $Rev: 17 $ $Date: 2008-03-25 22:45:24 +0900 (Tue, 25 Mar 2008) $
 */
public class Command13
    extends Command {
    enum ORDER {
        RESPONSE, REQUEST;
        static ORDER valueOf( int order ) {
            for ( ORDER t : ORDER.values() ) {
                if ( order == t.ordinal() ) {
                    return t;
                }
            }
            return null;
        }
    }

    ORDER order;

    boolean diffusion;

    boolean downstream;

    boolean bbs;

    int query_id;

    String keyword = "";

    String trip = "";

    ArrayList<Node> nodes;

    ArrayList<Query> queries;

    @Override
    void rawDataPack()
        throws WinnyProtocolException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            out.write( order.ordinal() );
            out.write( diffusion ? 1 : 0 );
            out.write( downstream ? 1 : 0 );
            out.write( bbs ? 1 : 0 );
            out.write( ConvertUtil.toLittleEndianBytes( query_id ) );
            byte[] keyword = this.keyword.getBytes( "windows-31j" );
            out.write( keyword.length );
            out.write( keyword );
            byte[] trip = this.trip.getBytes( "windows-31j" );
            if ( trip.length != 11 ) {
                for ( int c = ( trip.length - 11 ); c < 0; c++ ) {
                    System.out.println( c );
                    trip = ArrayUtils.add( trip, (byte) 0x00 );
                }
            }
            out.write( trip );
            out.write( nodes.size() );
            for ( Node n : nodes ) {
                out.write( InetAddress.getByName( n.getAddress() ).getAddress() );
                out.write( ConvertUtil.toLittleEndianBytes( (short) n.getPort() ) );
            }
            out.write( queries.size() );
            for ( Query q : queries ) {
                out.write( q.pack() );
            }
            data = out.toByteArray();
        }
        catch ( Exception e ) {
            throw new WinnyProtocolException( e );
        }
        finally {
            try {
                out.close();
            }
            catch ( Exception e ) {
                e.printStackTrace();
            }
        }
    }

    @Override
    void rawDataUnpack()
        throws WinnyProtocolException {
        _ByteArrayInputStream in = new _ByteArrayInputStream( data );
        try {
            // TODO test
            order = ORDER.valueOf( in.read() );
            diffusion = ( in.read() == 1 ) ? true : false;
            downstream = ( in.read() == 1 ) ? true : false;
            bbs = ( in.read() == 1 ) ? true : false;
            query_id = ConvertUtil.littleEndianBytes2int( in.read( 4 ) );
            int keyword_length = ConvertUtil.littleEndianBytes2int( in.read( 1 ) );
            keyword = new String( in.read( keyword_length ), "windows-31j" );
            trip = new String( ConvertUtil.getCStringByte( in.read( 11 ) ), "windows-31j" );
            int node_count = ConvertUtil.littleEndianBytes2int( in.read( 1 ) );
            nodes = new ArrayList<Node>( node_count );
            for ( int i = 0; i < node_count; i++ ) {
                Node n =
                    new Node( InetAddress.getByAddress( in.read( 4 ) ).getHostAddress(),
                              ConvertUtil.littleEndianBytes2int( in.read( 2 ) ) );
                nodes.add( n );
            }
            int key_count = ConvertUtil.littleEndianBytes2int( in.read( 2 ) );
            queries = new ArrayList<Query>( key_count );
            for ( int i = 0; i < key_count; i++ ) {
                Query q = new Query();
                q.unpack( in );
                queries.add( q );
            }
        }
        catch ( Exception e ) {
            e.printStackTrace();
            throw new WinnyProtocolException( e );
        }
        finally {
            try {
                in.close();
            }
            catch ( Exception e ) {
                e.printStackTrace();
            }
        }
    }

    public ORDER getOrder() {
        return order;
    }

    public void setOrder( ORDER order ) {
        this.order = order;
    }

    public boolean isDiffusion() {
        return diffusion;
    }

    public void setDiffusion( boolean diffusion ) {
        this.diffusion = diffusion;
    }

    public boolean isDownstream() {
        return downstream;
    }

    public void setDownstream( boolean downstream ) {
        this.downstream = downstream;
    }

    public boolean isBbs() {
        return bbs;
    }

    public void setBbs( boolean bbs ) {
        this.bbs = bbs;
    }

    public int getQuery_id() {
        return query_id;
    }

    public void setQuery_id( int query_id ) {
        this.query_id = query_id;
    }

    public String getKeyword() {
        return keyword;
    }

    public void setKeyword( String keyword ) {
        this.keyword = keyword;
    }

    public String getTrip() {
        return trip;
    }

    public void setTrip( String trip ) {
        this.trip = trip;
    }

    public ArrayList<Node> getNodes() {
        return nodes;
    }

    public void setNodes( ArrayList<Node> nodes ) {
        this.nodes = nodes;
    }

    public ArrayList<Query> getQueries() {
        return queries;
    }

    public void setQueries( ArrayList<Query> queries ) {
        this.queries = queries;
    }
    
}
