package Organisms2.g4;

class SocialEntity extends Entity {
    final static int TILE_SIZE = 8;
    final static int[][] tile = {
        { 3, 2, 3, 2, 3, 2, 3, 2 },
        { 2, 0, 1, 0, 2, 0, 1, 0 },
        { 2, 3, 1, 3, 2, 3, 1, 3 },
        { 0, 2, 0, 1, 0, 2, 0, 1 },
        { 3, 2, 3, 2, 3, 2, 3, 2 },
        { 2, 0, 1, 0, 2, 0, 1, 0 },
        { 2, 3, 1, 3, 2, 3, 1, 3 },
        { 0, 2, 0, 1, 0, 2, 0, 1 } };

    final int r_move;
    final int r_absent;
    final int e_clone;
    final int e_hungry;

    // food_amount:  less 3 2 1 0 more
    int food_amount = 1;
    int year_move = -1;

    public SocialEntity( int[] params, int key ) {
        super( params );

        setdimension( TILE_SIZE, TILE_SIZE );

        if ( -1 == key )
        {
            setYear( 0 );
        }
        else
        {
            setYear( (key>>16) & 0xffff );
            food_amount = (key>>6) & 3;
            sethome( -(key&7), -((key>>3)&7) );
        }
/*
        System.out.println( "A new entity is created at " + x() + ","
                            + y() + "[food]" + food_amount );
*/
        r_move = ev;
        r_absent = 3*ev/eu+1;
        e_clone = (int)( em * (Math.random() * 0.1 + 0.88 ) );
        e_hungry = em-eu;
    }

    final static int unwrap( int pos, int range ) {
        return ( pos + (range<<16) ) % range;
    }

    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;
    }
    
    final int rank_unwrap( int x, int y ) {
        return tile[ unwrap( y, TILE_SIZE ) ][ unwrap( x, TILE_SIZE ) ];
    }

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

    int eval( int d ) {
        return rank( d );
    }
    
    int[] sort_directions() {
        int[] ret = new int[4];
        
        ret[0] = ( -eval( EAST ) << 8 ) | EAST;
        ret[1] = ( -eval( WEST ) << 8 ) | WEST;
        ret[2] = ( -eval( SOUTH ) << 8 ) | SOUTH;
        ret[3] = ( -eval( NORTH ) << 8 ) | NORTH;

        java.util.Arrays.sort( ret );
        ret[0] &= 3;
        ret[1] &= 3;
        ret[2] &= 3;
        ret[3] &= 3;

        return ret;
    }

    int status( int d ) {
        int x = xof( x(), d );
        int y = yof( y(), d );

        return 0xff & ( ( food_amount << 6 ) | ( (y&7) << 3 ) | (x&7) );
    }

    void clonestatus( int d ) {
        setCloneAttr( ( year() << 16 ) | status( d ) );
    }

    int translate( int bits, int d ) {
        int x = xof( bits&7, d );
        int y = yof( (bits>>3)&7, d );

        return 0xff & ( (bits&0xc0) | ( (y&7) << 3 ) | (x&7) );
    }

    void listen() {
        int[] msg = new int[4];
        int n_msg = 0;
        if ( hasEntity(NORTH) )
            msg[n_msg++] = translate( entity( NORTH ), NORTH );
        if ( hasEntity(WEST) )
            msg[n_msg++] = translate( entity( WEST ), WEST );
        if ( hasEntity(EAST) )
            msg[n_msg++] = translate( entity( EAST ), EAST );
        if ( hasEntity(SOUTH) )
            msg[n_msg++] = translate( entity( SOUTH ), SOUTH );

        if ( n_msg == 0 )
            return;
        
        int fa = ( (msg[0]>>6) + (msg[1]>>6) + (msg[2]>>6) 
                   + (msg[3]>>6) + n_msg/2 ) / n_msg;

        if ( fa > food_amount && food_amount < 3 )
            food_amount++;
        else if ( fa < food_amount && food_amount > 0 )
            food_amount--;

        int x = x() % TILE_SIZE;
        int y = y() % TILE_SIZE;

        double r = Math.random();
        if ( ( n_msg > 1 && (msg[0]&7) == (msg[1]&7) && r < 0.8  )
             || ( n_msg > 0 && r < 0.2 ) )
            x = msg[0] & 7;

        r = Math.random();
        if ( ( n_msg > 1 && (msg[0]&0x38) == (msg[1]&0x38) && r < 0.8 )
             || ( n_msg > 0 && r < 0.2 ) )
            y = ( msg[0] >> 3 ) & 7;

        sethome( -x, -y );
    }

    public int action_inner() {
        listen();

        if ( food() >= fk/2 )
            food_amount = 0;
        else if ( food() >= fk/4 )
            food_amount = 1;
        else if ( rank(-1) == 0 && food_amount <= 1 )
            food_amount = 3;
        
        int[] ds = sort_directions();
        int cur_rank = rank(-1);

        if ( rank(-1) > food_amount && energy() >= e_clone ) 
        {
            if ( roundNoSameEntity( ds[0] ) > r_absent + 5
                 && rank( ds[0] ) > food_amount )
                return CLONE | ds[0];
            if ( roundNoSameEntity( ds[1] ) > r_absent + 5
                 && rank( ds[1] ) > food_amount )
                return CLONE | ds[1];
            if ( roundNoSameEntity( ds[2] ) > r_absent + 5
                 && rank( ds[2] ) > food_amount )
                return CLONE | ds[2];
            if ( roundNoSameEntity( ds[3] ) > r_absent + 5
                 && rank( ds[3] ) > food_amount )
                return CLONE | ds[3];
        }

        if ( year_move + r_move <= year() )
        {
            if ( roundNoSameEntity( ds[0] ) > r_absent+1 && rank( ds[0] ) > 0 )
                return MOVE | ds[0];
            if ( roundNoSameEntity( ds[1] ) > r_absent+1 && rank( ds[1] ) > 0 )
                return MOVE | ds[1];
            if ( roundNoSameEntity( ds[2] ) > r_absent+1 && rank( ds[2] ) > 0 )
                return MOVE | ds[2];
            if ( roundNoSameEntity( ds[3] ) > r_absent+1 && rank( ds[3] ) > 0 )
                return MOVE | ds[3];
        }
        else if ( year_move + r_absent - 1 <= year() )
        {
            if ( roundNoSameEntity( ds[0] ) > r_absent + 1
                 && rank( ds[0] ) > cur_rank )
                return MOVE | ds[0];
            if ( roundNoSameEntity( ds[1] ) > r_absent + 1
                 && rank( ds[1] ) > cur_rank )
                return MOVE | ds[1];
            if ( roundNoSameEntity( ds[2] ) > r_absent + 1
                 && rank( ds[2] ) > cur_rank )
                return MOVE | ds[2];
            if ( roundNoSameEntity( ds[3] ) > r_absent + 1
                 && rank( ds[3] ) > cur_rank )
                return MOVE | ds[3];

            if ( 0 == cur_rank )
            {
                if ( !hasEntity(ds[0]) )
                    return MOVE | ds[0];
                if ( !hasEntity(ds[1]) )
                    return MOVE | ds[1];
                if ( !hasEntity(ds[2]) )
                    return MOVE | ds[2];                    
                if ( !hasEntity(ds[3]) )
                    return MOVE | ds[3];
            }
        }

        if ( energy() <= e_hungry * ( rank(-1) + 4 ) / 8 )
        {
            if ( food() > 0 )
                return STAY;

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

        return STAY;
    }

    public int action() {
        int ret = action_inner();
        if ( ( ret & MOVE ) != 0 )
            year_move = year();
        else if ( ( ret & CLONE ) != 0 )
            clonestatus( ret & 3 );
        return ret;
    }

    public boolean sameEntity( int direction ) {
        return hasEntity( direction );
    }

    public int talk() {
        return status( -1 );
    }
}
