/*
 *  Copyright 2002-2009 Mikhail Kryshen <mikhail@kryshen.net>
 *
 *  This file is part of BMTron.
 *
 *  BMTron is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  BMTron is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with BMTron.  If not, see <http://www.gnu.org/licenses/>.
 */
 
package kryshen.bmtron;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Enumeration;
import java.util.Vector;

/**
 * Computer player which is supposed to be the best of 3, 
 * but it's not finished yet. Currently disabled.
 */
public class ComputerPlayer3 extends ThreadedPlayer {
    // FIXME: hangs in MSIE.

    /* Algorithm parameters */
    private final static float FOLLOW = 25f;
    private final static float COMPACT = 0f;
    private final static float COMPACT_ADD = 0f;
    private final static float COMPACT_MAX = 50f;
    private final static float TRACE = 0.75f;
    private final static float RANDOM = 1f;

    private final static Object PRESENT = new Object();
    private Vector nodes = new Vector();
    private Vector cNodes = new Vector();
    private Vector nNodes = new Vector();

    private int moveCount = 0;

    private FieldRectangle[] rectToGo = new FieldRectangle[4];

    ComputerPlayer3(Field field, String name, Color color, int team) {
	super(field, name, color, team);
    }

    synchronized void start() {
	nodes.addElement(new FieldRectangle(0, 0, field.cols, field.rows));
	moveCount = 0;
        super.start();
    }
    
    synchronized void stop() {
	super.stop();
	nodes.removeAllElements();
    }

    protected void move() {
	moveCount++;

	for (int i = 0; i < 4; i++) {
	    rectToGo[i] = null;
	}

	for (int i = 0; i < field.players.length; i++) {
	    if (!field.players[i].isStoped()) {
		splitRects(field.players[i].getLocation(), 
                           field.players[i] == this,
                           field.players[i].team == team);
	    }
	}
	
	FieldRectangle best = null;
	float bestWeight = -1f/0f;

	Point location = getLocation();

	for (int i = 0; i < 4; i++) {
	    if (rectToGo[i] != null) {		
		float currentWeight = 
		    rectToGo[i].weight(location);
		
		if (currentWeight > bestWeight) {
		    best = rectToGo[i];
		    bestWeight = currentWeight;
		    setDirection(i);
		}
	    }
	}

	//System.err.println(moveCount + "; " + best + "; " + bestWeight);
    }

    void drawRects(Graphics g) {
	for (Enumeration ie = nodes.elements(); ie.hasMoreElements(); ) {
	    FieldRectangle ri = (FieldRectangle)ie.nextElement();

	    g.setColor(new Color(255, 0, 0, 32));
	    
	    g.fillRect(ri.x * field.c2x, ri.y * field.r2y, 
		       ri.width * field.c2x, ri.height * field.r2y);

	    g.setColor(Color.red);

	    g.drawRect(ri.x * field.c2x, ri.y * field.r2y, 
		       ri.width * field.c2x, ri.height * field.r2y);

	    //for (int i = 0; i < 4; i++) {
	    //if (rectToGo[i] == ri) {
		    g.drawLine(ri.x * field.c2x,
			       ri.y * field.r2y, 
			       (ri.x + ri.width) * field.c2x, 
			       (ri.y + ri.height) * field.r2y);
		    
		    g.drawLine(ri.x * field.c2x, 
			       (ri.y + ri.height) * field.r2y,
			       (ri.x + ri.width) * field.c2x, 
			       ri.y * field.r2y);

		    //  g.drawString(String.valueOf(i),
		    //		 ri.x * field.c2x + 10,
		    //		 ri.y * field.r2y + 20);
		    //}
		//}
	}
    }

    private synchronized void splitRects(Point p, boolean head, 
                                         boolean friend) {
	Object[] rects = nodes.toArray();
	for (int i = 0; i < rects.length; i++) {
	    FieldRectangle r = (FieldRectangle)rects[i];

	    if (r.contains(p)) {
		nodes.removeElement(r);
		
		for (int j = 0; j < 4; j++) {
		    if (rectToGo[j] == r) {
			rectToGo[j] = null;
			break;
		    }
		}

                //r.area -= 1;

		if (!friend)
		    r.enemyTrace = moveCount;

		addNode(new FieldRectangle
                        (r.x, r.y, r.width, p.y - r.y, r),
			head ? field.UP : -1);
		addNode(new FieldRectangle
                        (p.x + 1, r.y, r.x + r.width - p.x - 1, r.height, r),
			head ? field.RIGHT : -1);	       	       
		addNode(new FieldRectangle
                        (r.x, p.y + 1, r.width, r.y + r.height - p.y - 1, r),
			head ? field.DOWN : -1);
		addNode(new FieldRectangle
                        (r.x, r.y, p.x - r.x, r.height, r),
			head ? field.LEFT : -1);
	    }
	}
    }

    private void addNode(FieldRectangle r, int dir) {
	if (r.isEmpty()) return;

	// remove nested rectangles
	for (Enumeration ie = nodes.elements(); ie.hasMoreElements(); ) {
	    FieldRectangle ri = (FieldRectangle)ie.nextElement();

	    if (ri.contains(r)) {
		if (dir >= 0) rectToGo[dir] = ri;
		return;
	    }

	    if (r.contains(ri)) {
		nodes.removeElement(ri);
		for (int i = 0; i < 4; i++) {
		    if (rectToGo[i] == ri) {
			rectToGo[i] = r;
			break;
		    }
		}
	    }
	}

	if (dir >= 0) rectToGo[dir] = r;
	nodes.addElement(r);
    }   

    private class FieldRectangle extends Rectangle {
	float area;

	int enemyTrace;
	private float parentWeight;
	private float parentArea;

	FieldRectangle(int x, int y, int width, int height) {
	    this(x, y, width, height, null);
	}

	FieldRectangle(int x, int y, int width, int height, 
		       FieldRectangle parent) {
	    super(x, y, width, height);	   	    

	    if (parent != null) {
		enemyTrace = parent.enemyTrace;
		parentArea = parent.area - 1;
		parentWeight = parent.weight();

		area = (float)width * (float)height;
	    } else {
		enemyTrace = 0;
		parentArea = 0f;
		parentWeight = 0f;

		area = (float)width * (float)height +
		    (float)Math.random() * RANDOM;
	    }
	}

	float weight() {
            float cw = 0;

            if (parentArea > 0) {
                cw = parentWeight * 
                    (1 - (float)area / parentArea) *
                    TRACE;
            }

            cw += area;
            //cw += Math.min(width, height);
            cw += FOLLOW / (moveCount - enemyTrace + 1);

            return cw;
	}

	float weight(Point p) {
	    int x1 = x + width - 1;
	    int y1 = y + height - 1;

	    int w1 = Math.abs(p.x - x);
	    int w2 = Math.abs(x1 - p.x);
	    int h1 = Math.abs(p.y - y);
	    int h2 = Math.abs(y1 - p.y);
	    
	    float w = (float)Math.min(w1, w2);
	    float h = (float)Math.min(h1, h2);
	    
	    return weight() + 
		Math.min(COMPACT_MAX, 
                         (COMPACT + COMPACT_ADD * (moveCount - enemyTrace)))
		/ (w + h + 1f);
	}
    }
}
