/*
 * Grid.java
 *
 * Created on 07 October 2006, 01:36
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package ants;

import ants.control.AntControl;
import ants.event.AntEvent;
import ants.event.AntListener;
import ants.event.FoodUpdateEvent;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Vector;

/**
 *
 * @author James Hamilton
 */
public class Grid extends Canvas implements Runnable {
    
    private GridSquare[][] grid;
    private GridVector gridVector;
    
    private BufferedImage bi;
    private Graphics big;
    private Thread animate;
    private Thread resetFoodStrength;
    
    private static Grid singleton = null;
    private Vector<MyObject> objects;
    
    private Vector<FoodCluster> foodClusters;
    
    private Nest nest;
    
    private int size = 100;
    
    /** Creates a new instance of Grid */
    private Grid(int size) {
        this.size = size;
        initialise();
    }
    
    private void initialise() {
        grid = new GridSquare[size][size];
        setGridVector(new GridVector());
        setSize(size * GridSquare.SIZE, size * GridSquare.SIZE);
        
        for(int x = 0; x < grid.length; x++) {
            for(int y = 0; y < grid.length; y++) {
                
                grid[x][y] = new GridSquare();
                grid[x][y].setLocation(x * GridSquare.SIZE, y * GridSquare.SIZE);
                getGridSquares().add(grid[x][y]);
            }
            
        }
        bi = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_RGB);
        big = bi.createGraphics();
        
        setNest(new Nest(grid[20][20]));
      
        objects = new Vector<MyObject>();
        objects.add(getNest());
        
        
        
        setFoodClusters(new Vector<FoodCluster>());
        
        animate = null;
        animate = new Thread(this);
        animate.start();    
        
        addMouseListener( new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                System.out.println(getGridSquareAt(e.getX(), e.getY()));
            }
        });
        
        addAntListener(AntControl.getInstance());
    }

    private Vector<AntListener> listeners = new Vector<AntListener>();
    
    public void addAntListener(AntListener listener) {
        listeners.add(listener);
    }
    
    public void notifyListeners(AntEvent e) {
        for(AntListener listener : listeners)
            listener.antEventHandler(e);
    }
    
    public synchronized static Grid getInstance(int n) {
        if(singleton == null)
            singleton = new Grid(n);
        return singleton;
    }
    
    public synchronized static Grid getInstance() {
        if(singleton == null)
            singleton = new Grid(100);
        return singleton;
    }
    
    public void reset() {
        initialise();
    }

    public void recalculateFoodStrengths() {
        for(GridSquare gs : getGridSquares()) {
            gs.recalculateFoodStrength();
        }
    }
    
    public void setFoodStrengthRadius(int radius) {
        for(FoodCluster cluster : foodClusters) {
            cluster.setStrengthRadius(radius);
        }      
    }
    
    public void resetFoodStrengths() {
        recalculateFoodStrengths();
    }
    
    public synchronized GridSquare getGridSquare(int x, int y) throws Exception {
        try {
            return grid[x][y];
        }catch (ArrayIndexOutOfBoundsException e) {
            throw new Exception("Invalid Square");
        }
    }
    
    public GridSquare getGridSquare(Point p) throws Exception {
        return getGridSquare(p.x, p.y);
    }

    public GridSquare getGridSquareAt(int x, int y) {
        for(GridSquare gs : gridVector) {
            if(gs.contains(x, y)) return gs;
        }
        return null;
    }
    
    public Vector<MyObject> getObjects() {
        return objects;
    }
    
    public void addObject(MyObject object) {
        objects.add(object);
    }
    
    public void populate(int n) {

        
    }
    
    public void populateWithFood(int n) {
        
        for(int j = 0; j < n; j++) {
            
            boolean stop = false;
            
            while(!stop) {
                
                int x = (int)(Math.random() * grid.length);
                int y = (int)(Math.random() * grid.length);
                
                if(grid[x][y].empty()) {

                    int size = (int)(Math.random() * 5) + 1;
                    
                    try {
                        addFoodCluster(x, y, size);
                    }catch (Exception e) {
                        //no such gridsquare
                    }
                    
       
                    stop = true;
                }
            }
        }
        
        this.notifyListeners(new FoodUpdateEvent(this));
    }
 
    public void addFoodCluster(int x, int y, int radius) throws Exception {
        
        getGridSquare(x, y).getGridSquares(radius, true).getFreeGridSquares().addFood();
        
        notifyListeners(new FoodUpdateEvent(this));
    }
    
    public void addObstacleCluster(int x, int y, int radius) throws Exception {
        getGridSquare(x, y).getGridSquares(radius, true).addObstacles(); 
    }
    
    public void populateWithObstacles(int n) {
        
        for(int j = 0; j < n; j++) {
            
            boolean stop = false;
            
            while(!stop) {
                
                int x = (int)(Math.random() * grid.length);
                int y = (int)(Math.random() * grid.length);
                
                if(grid[x][y].empty()) {
                    try {
                        addObstacleCluster(x, y, 3);
                    }catch (Exception e) {
                        //no such gridsquare
                    }
                    
            
                    stop = true;
                }
            }
        }
        
        for(int x = 0; x < grid.length; x++) {
            try {
                Obstacle obstacle = new Obstacle(getGridSquare(x, 0));
                objects.add(obstacle);
                getGridSquare(x, 0).setObject(obstacle);
                
                Obstacle obstacle1 = new Obstacle(getGridSquare(x, grid.length-1));
                objects.add(obstacle1);
                getGridSquare(x,grid.length-1).setObject(obstacle1);                
                
            }catch (Exception e) {
                //
            }
        }
 
       for(int y = 0; y < grid.length; y++) {
            try {
                Obstacle obstacle = new Obstacle(getGridSquare(0, y));
                objects.add(obstacle);
                getGridSquare(0, y).setObject(obstacle);
                
                Obstacle obstacle1 = new Obstacle(getGridSquare(grid.length-1, y));
                objects.add(obstacle1);
                getGridSquare(grid.length-1,y).setObject(obstacle1);                
                
            }catch (Exception e) {
                //
            }
        }
        

    }
    
    
    public int countAnts() {
        int count = 0;
        for(int x = 0; x < grid.length; x++) {
            for(int y = 0; y < grid.length; y++) {
                if(grid[x][y].containsAnt()) count++;
            }
        }
        return count;
    }
    
    public int countCarriedFood() {
        int count = 0;
        for(int x = 0; x < grid.length; x++) {
            for(int y = 0; y < grid.length; y++) {
                if(grid[x][y].containsAnt() && grid[x][y].getAnt().carryingFood()) count++;
            }
        }
        return count;
    }
    
    public int countNests() {
        int count = 0;
        for(int x = 0; x < grid.length; x++) {
            for(int y = 0; y < grid.length; y++) {
                if(grid[x][y].isNest()) count++;
            }
        }
        return count;
    }
    
    
    
    public void setAntAt(Ant ant, int x, int y) {
        grid[x][y].setAnt(ant);
    }
    
    
    public void paint(Graphics g) {
        update(g);
    }
    
    public void update(Graphics g) {
        clear();
         
        for(GridSquare object : gridVector) {
            
            object.draw(big);
            
        }
        
        g.drawImage(bi, 0, 0, this);
    }
    
    public void clear() {
        big.setColor(Color.black);
        big.fillRect(0,0,getWidth(),getHeight());
    }
    
    public void run() {
        while(animate!=null) {
            
            try {
                animate.sleep(300);
                
                
                Thread.yield();
                //printStats();
                
                repaint();
            }catch (Exception e) {
                System.out.println(e);
            }
            
        }
        big.dispose();
    }

    
    public void printStats() {
        System.out.print(countAnts() + " ants. ");
        System.out.print(countCarriedFood() + " carrying food");
        System.out.println();
    }

    public Nest getNest() {
        return nest;
    }

    public void setNest(Nest nest) {
        this.nest = nest;
    }

    public Vector<FoodCluster> getFoodClusters() {
        return foodClusters;
    }

    public void setFoodClusters(Vector<FoodCluster> foodClusters) {
        this.foodClusters = foodClusters;
    }

    public GridVector getGridSquares() {
        return gridVector;
    }
    
    public GridVector getGridSquaresWithFood() {
        return getGridSquares().getFoodGridSquares();
    }
    
    public int countFood() {
        return getGridSquaresWithFood().size();
    }

    public void setGridVector(GridVector gridVector) {
        this.gridVector = gridVector;
    }
}

