import java.util.*;
import catchthesheep.*;
import java.awt.*;

/**
 *
 * @author Prof. Dr. Hessling, Daniel Prusseit, Jens Klinker
 *
 * Wolf mit analytischem Vorgehen, siehe Doku
 *
 */
public class WSPlayerWolf extends catchthesheep.Player
{
    public void init() {
        context.message("Init Wolf");
    }

    private static Random rndmgen = new Random();

    // the position of the leading wolf
    private int xLeadingWolf = 0;
    private int yLeadingWolf = 0;

    private int xmax = 8 - 1;  // Brett mit 8x8 Feldern
    private int ymax = 8 - 1;

    private int[][] spielfeld = new int[ymax+1][xmax+1];  // Diskrepanz zw. Def. und Aufruf
    // von Arrays: Null-Problem!

    public CommandMap process( GameBoardModel input ) {
        Hashtable woelfe = input.getWoelfe();
        Hashtable schafe = input.getSchafe();
        int dir=0;
        int delta=0;

        for ( int j=0; j<=ymax; j++ )    // Initialisierung des Spielfelds
        {
            for ( int i=0; i<=xmax; i++ )
                spielfeld[j][i] = 0;
        }

        Enumeration e = woelfe.keys();
        while(e.hasMoreElements())
        {
            Integer key = (Integer)e.nextElement();
            Point p = (Point)woelfe.get(key);
            int x = (int)p.getX();  // x - Koordinate
            int y = (int)p.getY();  // y - Koordinate
            //  int n = key.intValue(); // Index des  Wolfes
            spielfeld[x][y] = 1;
        }

        e = schafe.keys();
        while(e.hasMoreElements())
        {
            Integer key = (Integer)e.nextElement();
            Point p = (Point)schafe.get(key);
            int x = (int)p.getX();  // x - Koordinate
            int y = (int)p.getY();  // y - Koordinate

            //  int n = key.intValue(); // Index des  Wolfes
            spielfeld[x][y] = -1;
        }

        e = woelfe.keys();
        while(e.hasMoreElements())
        {
            Integer key = (Integer)e.nextElement();
            Point p = (Point)woelfe.get(key);
            // create a leading wolve and remember its position
            if (key.intValue() == woelfe.size()-1)
            {
                xLeadingWolf = (int)p.getX();
                yLeadingWolf = (int)p.getY();
            }
            makeMove(p, key, schafe.size(), woelfe.size());
        }

        return new CommandMap(woelfe);
    }

    // processes and makes a move for a single wolf
    private void makeMove (Point p, Integer key, int nrOfSheep, int nrOfWolves)
    {
        int dir = -1;

        int delta=0;
        int valid=0;

        int xGoto = 0;
        int yGoto = 0;

        int x = (int)p.getX();  // x coord of the wolve
        int y = (int)p.getY();  // y coord of the wolve
        int n = key.intValue(); // the wolve's index

        // if the following conditions are all true, we follow the leading wolf:
        //   1) the number of sheep is greater or equal to the number of wolves
        //   2) there is more than one wolf in the game (otherwise no team is possible ;-)
        //   3) we are not the leading wolf ourselves
        boolean teamworkNeeded = (nrOfSheep >= nrOfWolves && nrOfWolves > 1 && key.intValue() != nrOfWolves-1 );
        dir = gotoMySheep(x,y,teamworkNeeded);

        // find moves while no valid move found
        while ( valid == 0 )
        {
            switch ( dir )
            {
            case 0: // up
                xGoto = x;
                yGoto = y - 1;
                break;

            case 1: // down
                xGoto = x;
                yGoto = y + 1;
                break;

            case 2: // left
                xGoto = x - 1;
                yGoto = y;
                break;

            case 3: // right
                xGoto = x + 1;
                yGoto = y;
                break;
            case -1: // stay where we are and wait for better times
                xGoto = x;
                yGoto = y;
                valid = 1;
                break;
            }

            // if we want to move -> check where we go to
            if ( dir != -1 )
            {
                switch ( lookAt(xGoto,yGoto) )
                {
                // a wolf is there or we're standing at the border
                // -> move is invalid
                case 1 :
                case 2 :
                    valid = 0;
                    break;

                // nothing is there -> move is valid
                case 0 :
                    spielfeld[x][y] = 0;
                    spielfeld[xGoto][yGoto] = 1;
                    valid = 1;
                    break;

                // a sheep is there -> move is VERY valid!!! ;-)
                case -1:
                    spielfeld[xGoto][yGoto] = 1;
                    valid = 1;
                    break;
                }
            }

            // if no valid move has been found
            if ( valid == 0 )
            {
                // check if we're blocked
                if ( checkDeadlock(x,y) )
                {
                    dir = -1;
                }
                // if not so, do a random move
                else
                {
                    dir = rndmgen.nextInt(4);
                }
            }
        }
        // perform our move
        p.translate(xGoto-x,yGoto-y);
    }

    // look at a certain position of the game board
    // returns:      2 - position is out of the board
    //         0,1,-1 - nothing, a wolf, a sheep is there
    private int lookAt(int x, int y)
    {
        if ( x > xmax || x < 0 || y > xmax || y < 0 )
            return 2;
        else
            return spielfeld[x][y];
    }

    // check if a move is possible from a certain position
    private boolean checkDeadlock(int x, int y)
    {
        if (
           lookAt(x-1,y) >0 &&
           lookAt(x+1,y) >0 &&
           lookAt(x,y-1) >0 &&
           lookAt(x,y+1) >0
           )
            return true;
        else return false;
    }

    // counts the number of sheep and finds the nearest sheep to a given position
    // returns an array of 3 integers containing:
    // nr of sheep - x position of nearest sheep - y position of nearest sheep
    private int[] findNearestSheep (int x, int y)
    {
        int sheep[] = new int[3];
        int sheepcount = 0;
        double distmin = xmax * ymax;
        double distcalc = 0;
        int xsheep = 0;
        int ysheep = 0;

        // run through the whole game board and find the nearest sheep
        for ( int i=0; i <= xmax; i++ )
        {
            for ( int j=0; j <= ymax; j++ )
            {
                if ( spielfeld[i][j] == -1 )
                {
                    sheepcount++;
                    distcalc = Math.sqrt ( ((x-i) * (x-i) + (y-j) * (y-j)) );
                    if ( distcalc < distmin )
                    {
                        distmin = distcalc;
                        xsheep = i;
                        ysheep = j;
                    }
                }
            }
        }

        // if no sheep found, set number of sheep to zero and return
        if (sheepcount == 0)
        {
            sheep[0] = 0;
            return sheep;
        }

        // if the nearest sheep is very far away, first run to the center of the board
        // (we just create an imaginary sheep for our hungry wolf)
        if (distmin > (Math.sqrt(xmax * xmax + ymax * ymax) * .55))
        {
            xsheep = xmax /2;
            ysheep = ymax /2;
        }

        // assemble the return array
        sheep[0] = sheepcount;
        sheep[1] = xsheep;
        sheep[2] = ysheep;

        return sheep;
    }

    // finds the best moving direction to catch a sheep
    // returns 0,1,2,3 for up, down, left, right
    private int gotoMySheep (int x, int y, boolean followLeader)
    {
        int dir;
        int nearest[];

        // if we have the order to follow the leading wolve
        if (followLeader)
        {
            // find out which sheep our leader is hunting
            nearest = findNearestSheep(xLeadingWolf,yLeadingWolf);
        }
        else
        {
            // or hunt a sheep on our own
            nearest = findNearestSheep(x,y);
        }

        // if no sheep found -> stay where you and die from hunger
        if ( nearest[0] == 0 )
        {
            return -1;
        }

        // analyze possible horizontal and vertical moves
        // both times, we get the best moving direction
        int hor = tryHor(nearest[1], x, y);
        int ver = tryVer(nearest[2], x, y);

        // if a sheep is next to us -> lunch time!
        if (hor>9)
        {
            return hor-10;
        }
        if (ver>9)
        {
            return ver-10;
        }

        // try to move first into that direction where the sheep
        // is farther away
        int xyDiff = Math.abs( nearest[1] - x ) - Math.abs( nearest[2] - y );
        if ( xyDiff > 0 )
             dir = 0;
        else if ( xyDiff < 0 )
             dir = 1;
        else dir = rndmgen.nextInt(2);

        if (dir == 0)
        {
            if (hor != -1)
            {
                return hor;
            }
            return ver;
        }

        if (ver != -1)
        {
            return ver;
        }
        return hor;
    }

    // analyze horizontal moving direction
    // returns 2 if sheep is to the left
    //         3 if sheep is to the right
    //     12,13 if sheep is next to us on the left/right
    //        -1 if no horizontal move is possible
    private int tryHor(int xsheep, int x, int y)
    {
        int returnValue = -1;

        if ( xsheep < x && lookAt(x-1,y)<1 )
        {
            returnValue = 2;
        }

        if ( xsheep > x && lookAt(x+1,y)<1 )
        {
            returnValue = 3;
        }

        if (lookAt(x-1,y) == -1 || lookAt(x+1,y) == -1)
        {
            returnValue += 10;
        }

        return returnValue;
    }

    // analyze vertical moving direction
    // returns 0 if sheep is higher than we are
    //         1 if sheep is lower than we are
    //     10,11 if sheep is next to us on top/bottom
    //        -1 if no vertical move is possible
    private int tryVer(int ysheep, int x, int y)
    {
        int returnValue = -1;

        if ( ysheep < y  && lookAt(x, y-1)<1 )
        {
            returnValue = 0;
        }

        if ( ysheep > y  && lookAt(x, y+1)<1 )
        {
            returnValue = 1;
        }

        if (lookAt(x,y+1) == -1 || lookAt(x,y-1) == -1)
        {
            returnValue += 10;
        }

        return returnValue;
    }

    public void destroy()
    {
        context.message("destroying");
    }
}

