package jp.ac.nii.icpc2010.players;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import jp.ac.nii.icpc2010.players.AbstractPlayer;
import jp.ac.nii.icpc2010.playfield.FieldDirection;
import jp.ac.nii.icpc2010.playfield.IPlayField;


public abstract class RouteSelecter extends AbstractPlayer {

    protected abstract int getNumOfRoutes();

    // Return a route from (0, 0) except for (0, 0).
    protected abstract Point[] getRoute(int routeId);


    private static final int COIN_WORTH = 3;
    private static final int TIME_MARGIN = 10;
    private static final long RAND_SEED = 19830909L;

    Random rand;

    int curRouteId;
    Point curStart;
    int curLen;

    public RouteSelecter(int id, IPlayField playField) {
        super(id, playField);
        this.rand = new Random(RAND_SEED);
        this.curRouteId = 0;
        this.curLen = 0;
        this.curStart = new Point(getX(), getY());
    }

    private FieldDirection getDir(int x, int y){
        if(x > 0){
            return FieldDirection.Right;
        }else if(x < 0){
            return FieldDirection.Left;
        }else if(y > 0){
            return FieldDirection.Down;
        }else{
            return FieldDirection.Up;
        }        
    }

    @Override
    public FieldDirection getInput() {

        int width = playField.getWidth();
        int height = playField.getHeight();

        // get the remain length of current route.
        int remainLen = 0;
        boolean alive = true;
        Point[] route = getRoute(this.curRouteId);
        for(int i = this.curLen; alive && i < route.length; i++){
            int x, y;
            for(x = (this.curStart.x + route[i].x) % width; x < 0; x += width){}
            for(y = (this.curStart.y + route[i].y) % height; y < 0; y += height){}

            int object = playField.getObjectAt(x, y);
            switch(object){
            case OBJECT_FREE:
                remainLen++;
                break;
            case OBJECT_COIN:
                remainLen += COIN_WORTH;
                break;
            default:
                alive = false;
            }
        }

        // get the longest route.
        List<Integer> bestRouteIds = new ArrayList<Integer>();
        int bestLen = 0;
        int numOfRoutes = getNumOfRoutes();
        for(int i = 0; i < numOfRoutes && getRemainingTime() > TIME_MARGIN; i++){
            int len = 0;
            alive = true;
            route = getRoute(i);
            for(int j = 0; alive && j < route.length; j++){
                int x, y;
                for(x = (getX() + route[j].x) % width; x < 0; x += width){}
                for(y = (getY() + route[j].y) % height; y < 0; y += height){}

                int object = playField.getObjectAt(x, y);
                switch(object){
                case OBJECT_FREE:
                    len++;
                    break;
                case OBJECT_COIN:
                    len += COIN_WORTH;
                    break;
                default:
                    alive = false;
                }
            }

            if(len > bestLen){
                bestLen = len;

                bestRouteIds.clear();
                bestRouteIds.add(i);
            }else if(len == bestLen){
                bestRouteIds.add(i);
            }
        }

        if(remainLen == 0 || bestLen > remainLen){
            this.curRouteId = bestRouteIds.get(this.rand.nextInt(bestRouteIds.size()));
            this.curLen = 0;
            this.curStart = new Point(getX(), getY());
        }

        int dx, dy;
        if(this.curLen == 0){
            dx = getRoute(this.curRouteId)[0].x;
            dy = getRoute(this.curRouteId)[0].y;                
        }else{
            dx = getRoute(this.curRouteId)[this.curLen].x - getRoute(this.curRouteId)[this.curLen - 1].x;
            dy = getRoute(this.curRouteId)[this.curLen].y - getRoute(this.curRouteId)[this.curLen - 1].y;                
        }

        this.curLen++;

        return getDir(dx, dy);
    }
}
