package Organisms2.g4;

class NetEntity extends Entity {
    final int e_hungry; // the energy of hungry
    final int e_clone;  // the energy to clone

    final int d_move;         // the main direction of move
    final int r_move;         // the minimum round before a move
    final int r_move_1;       // r_move if staying in a zero rank cell
    final int r_move_2;       // r_move if staying in a negative rank cell
    final int r_clone;          // the minimum round before a clone
    int age_move = -1;        // last move age
    int age_clone = -100;      // last clone age
    int age_request = -100;    // the last request age
    
    /** the constructor
     */
    public NetEntity( int[] params, int key ) {
        super( params );

        if ( -1 != key )
        {
            sethome( -((key<<22)>>22), -((key<<12)>>22) );
            setYear( (key>>22)&0x3ff );
        }

        d_move = (key>>20) & 3;
        // setdimension( 20, 15 );

        e_hungry = Math.max( em-eu-eu, ev+ev );
        double factor = Math.exp( - year() / 1000.0 );
        e_clone = (int) ( em * ( 0.5 * factor + Math.random() * (1-factor) ) );

        r_move = ev - 1;
        r_clone = ev - 1;

        r_move_1 = Math.min( r_move, eu/ev+4 );
        r_move_2 = Math.min( r_move_1, 4*ev/eu + 1 );
/*
        System.out.println( "I am a new entity, at pos(" + x() + "," + y() 
                            +") and born in " + year() 
                            + " with default direction " + d_move 
                            + " move params:" + r_move + "," + r_move_1
                            + "," + r_move_2);
*/
    }

    /** a neighboring entity is requiring information
     */
    public int request( int d ) {
        return ( x() & 0x3ff ) + ( ( y() & 0x3ff ) << 10 );
    }

    int xof( int x, int d ) {
        switch ( d ) 
        {
        case EAST:
            return unwrap( x+1, width() );
        case WEST:
            return unwrap( x-1, width() );
        }
        return x;
    }

    int yof( int y, int d ) {
        switch ( d )
        {
        case SOUTH:
            return unwrap( y+1, height() );
        case NORTH:
            return unwrap( y-1, height() );
        }
        return y;
    }

    /** get information from a neighboring entity
     */
    public void notify( int value, int d ) {
        int x = xof( (value<<22)>>22, turnBack(d) );
        int y = yof( (value<<12)>>22, turnBack(d) );
        
        if ( x != width() )
            setdimension( Math.abs( x-width() ), height() );
        if ( y != height() )
            setdimension( width(), Math.abs( y-height() ) );
/*
        System.out.println( "Getting dimensions from neighbour: ("
                            + width() + "," + height() );
*/
        age_request = -100;
    }

    /** hungry?
     */
    public boolean hungry() {
        return energy() <= e_hungry;
    }

    /** returns the feasibility of stay for a cell
     */
    public int rank( int x, int y ) {
        if ( 0 == x && 0 == y )
            return 1;

        if ( 0 == (x+30000) % 3 )
        {
            if ( 0 == (y+30000) % 3 )
                return 0;
            return 1;
        }
        if ( 0 == (y+30000) % 3 )
            return 1;
        return -10;
    }

    /** functions of unwrapping */
    final static int unwrap( int pos, int range ) {
        pos += range/2;
        while ( pos >= range )
            pos -= range;
        while ( pos < 0 )
            pos += range;
        return pos - range/2;
    }

    final int rank_unwrap( int x, int y ) {
        return rank( unwrap( x, width() ), unwrap( y, height() ) );
    }

    public int rank( int d ) {
        switch( d )
        {
        case EAST:
            return rank_unwrap( x()+1, y() );
        case WEST:
            return rank_unwrap( x()-1, y() );
        case NORTH:
            return rank_unwrap( x(), y()-1 );
        case SOUTH:
            return rank_unwrap( x(), y()+1 );
        default:
            return rank_unwrap( x(), y() );
        }
    }

    /** returns the default direction of a cell
     */
    public int direction( int x, int y ) {
        return -1;
    }

    final int direction_unwrap( int x, int y ) {
        return direction( unwrap( x, width() ), unwrap( y, height() ) );
    }
    
    public int direction( int d ) {
        switch( d )
        {
        case EAST:
            return direction_unwrap( x()+1, y() );
        case WEST:
            return direction_unwrap( x()-1, y() );
        case NORTH:
            return direction_unwrap( x(), y()-1 );
        case SOUTH:
            return direction_unwrap( x(), y()+1 );
        default:
            return direction_unwrap( x(), y() );
        }
    }


    boolean need_move() {
        if ( age_move + r_move <= age() )
            return true;
        if ( rank( -1 ) <= 0 && age_move + r_move_1 <= age() )
            return true;
        if ( rank( -1 ) < 0 && age_move + r_move_2 <= age() )
            return true;
        return false;
    }

    boolean cloneable( int d ) {
        if ( roundNoSameEntity( d ) < r_move_1 + 1 )
            return false;

        if ( rank( d ) > 0 )
            return true;
        if ( rank( d ) == 0 && Math.random() < 0.01 )
            return true;
        return false;
    }

    final int do_request() {
        ask();
        return STAY;
    }

    int eval( int d, boolean divert ) {
        int ret = rank( d ) * 4;
        if ( d_move >= 0 )
        {
            if ( divert )
            {
                if ( d == turnLeft( d_move ) || d == turnRight( d_move ) )
                    ret += 5;
            }
            else
            {
                if ( d == d_move )
                    ret+=5;
                else if ( d == turnBack( d_move ) )
                    ret-=5;
            }
        }

        if ( d == direction( -1 ) )
            ret += 10;

        ret += roundNoSameEntity( d );
/*
        System.out.println( " Direction " + d + " has rank "
                            + rank(d) + " and RNSE " + roundNoSameEntity(d) 
                            + " combined eval " + ret );
*/
        return ret;
    }

    int[] sort_directions( boolean divert ) {
        int[] ret = new int[4];
        
        ret[0] = ( -eval( EAST, divert ) << 8 ) | EAST;
        ret[1] = ( -eval( WEST, divert ) << 8 ) | WEST;
        ret[2] = ( -eval( SOUTH, divert ) << 8 ) | SOUTH;
        ret[3] = ( -eval( NORTH, divert ) << 8 ) | NORTH;

        java.util.Arrays.sort( ret );
        ret[0] &= 3;
        ret[1] &= 3;
        ret[2] &= 3;
        ret[3] &= 3;
/*
        System.out.println( "The sorted direction are: " + ret[0] + ","
                            + ret[1] + "," + ret[2] + "," + ret[3] 
                            + " and default direction is " + d_move );
*/
        return ret;
    }

    void setCloneAttr( int x, int y, int d, int age ) {
/*
        System.out.println( "Clone to " + x + "," + y 
                            + " with default direction " + d 
                            + " at age " + age );
*/
        setCloneAttr( (age<<22) | ((d&3)<<20) | ((y&0x3ff)<<10) | (x&0x3ff) );
    }

    /** the main action function
     */
    public int action() {
        if ( age() - age_request < 10 )
            return STAY;
/*
        if ( width() > 1000 )
        {
            if ( x() > 0 )
            {
                if ( hasEntity( EAST ) )
                    return do_request();
            }
            else if ( x() < 0 )
            {
                if ( hasEntity( WEST ) )
                    return do_request();
            }
        }

        if ( height() > 1000 )
        {
            if ( y() > 0 )
            {
                if ( hasEntity( SOUTH ) )
                    return do_request();
            }
            else
            {
                if ( hasEntity( NORTH ) )
                    return do_request();
            }
        }
*/
        int[] ds = sort_directions( true );

        if ( rank(-1)>0 && energy()>=e_clone && age_clone+r_clone <= age() )
        {
            if ( cloneable( ds[0] ) )
            {
                age_clone = age();
                setCloneAttr( xof(x(),ds[0]), yof(y(),ds[0]), ds[0], age()+1 );
                return CLONE | ds[0];
            }
            if ( cloneable( ds[1] ) )
            {
                age_clone = age();
                setCloneAttr( xof(x(),ds[1]), yof(y(),ds[1]), ds[1], age()+1 );
                return CLONE | ds[1];
            }
            if ( cloneable( ds[2] ) )
            {
                age_clone = age();
                setCloneAttr( xof(x(),ds[2]), yof(y(),ds[2]), ds[2], age()+1 );
                return CLONE | ds[2];
            }
            if ( cloneable( ds[3] ) )
            {
                age_clone = age();
                setCloneAttr( xof(x(),ds[3]), yof(y(),ds[3]), ds[3], age()+1 );
                return CLONE | ds[3];
            }
        }

        ds = sort_directions( false );

        if ( need_move() ) 
        {
            if ( !hasEntity( ds[0] ) && rank( ds[0] ) >= 0 )
            {
                age_move = age();
                return MOVE | ds[0];
            }
            if ( !hasEntity( ds[1] ) && rank( ds[1] ) >= 0 )
            {
                age_move = age();
                return MOVE | ds[1];
            }
            if ( !hasEntity( ds[2] ) && rank( ds[2] ) >= 0 )
            {
                age_move = age();
                return MOVE | ds[2];
            }
            if ( !hasEntity( ds[3] ) && rank( ds[3] ) >= 0 )
            {
                age_move = age();
                return MOVE | ds[3];
            }
        }

        if ( hungry() ) 
        {
            if ( food() > 0 )
                return STAY;

            if ( hasFood( ds[0] ) )
            {
                age_move = age();
                return MOVE | ds[0];
            }
            if ( hasFood( ds[1] ) )
            {
                age_move = age();
                return MOVE | ds[1];
            }
            if ( hasFood( ds[2] ) )
            {
                age_move = age();
                return MOVE | ds[2];
            }
            if ( hasFood( ds[3] ) )
            {
                age_move = age();
                return MOVE | ds[3];
            }
        }

        return STAY;
    }
}
