package mining;

// Environment code for project jasonTeamSimLocal.mas2j

import jason.asSyntax.Literal;
import jason.asSyntax.Structure;
import jason.asSyntax.Term;
import jason.environment.grid.Location;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.GregorianCalendar;

import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.File;

public class MiningPlanet extends jason.environment.Environment {

    private Logger logger = Logger.getLogger("gold-miners-structured.mas2j." + MiningPlanet.class.getName());
    
    WorldModel  model;
    WorldView   view;
    
	int     simId    = 1; // type of environment
    int     sleep    = 0;
    boolean running  = true;
    boolean hasGUI   = true;
	int		worldSize = 35;
	int		nbAgents  = 4;
	float	goldLoad  = 0.01f;
    
    Term                    up       = Literal.parseLiteral("do(up)");
    Term                    down     = Literal.parseLiteral("do(down)");
    Term                    right    = Literal.parseLiteral("do(right)");
    Term                    left     = Literal.parseLiteral("do(left)");
    Term                    skip     = Literal.parseLiteral("do(skip)");
    Term                    pick     = Literal.parseLiteral("do(pick)");
    Term                    drop     = Literal.parseLiteral("do(drop)");

    public enum Move {
        UP, DOWN, RIGHT, LEFT
    };

    @Override
	public void init(String[] args) {
        sleep  = Integer.parseInt(args[0]);
        hasGUI = args[1].equals("yes"); 
		worldSize = Integer.parseInt(args[2]);
		goldLoad = 	Float.parseFloat(args[3]);
		nbAgents  = Integer.parseInt(args[4]);
        initWorld();
    }
    
    public int getNbAgents() {
        return nbAgents;
    }

    public void setSleep(int s) {
        sleep = s;
    }

    @Override
	public void stop() {
        running = false;
        super.stop();
    }

    @Override
    public boolean executeAction(String ag, Structure action) {
        boolean result = false;
        try {
            if (sleep > 0) {
                Thread.sleep(sleep);
            }
            
			if ( (ag.startsWith("leader")) || (ag.startsWith("boss")) )
			{
				synchronized(model)
				{
				    model.setEndOfSimulation(model.getEndOfSimulation() + 1);
					if (model.getEndOfSimulation() > nbAgents)
					{
						System.exit(0);
					}
				}
				return true;
			}
			
            // get the agent id based on its name
            int agId = getAgIdBasedOnName(ag);
			boolean isBoss = ag.startsWith("boss");

            if (action.equals(up)) {
                result = model.move(Move.UP, agId);
            } else if (action.equals(down)) {
                result = model.move(Move.DOWN, agId);
            } else if (action.equals(right)) {
                result = model.move(Move.RIGHT, agId);
            } else if (action.equals(left)) {
                result = model.move(Move.LEFT, agId);
            } else if (action.equals(skip)) {
                result = true;
            } else if (action.equals(pick)) {
                result = model.pick(agId);
            } else if (action.equals(drop)) {
                result = model.drop(agId);
				if (hasGUI)
					view.udpateCollectedGolds();
				if (model.isAllGoldsCollected()) {
					addPercept(Literal.parseLiteral("end_of_simulation(" + simId + ",0)"));
					logger.info(" End of simulation perceptio added.");
				}
            } else if (action.getFunctor().equals("endOfSimulation"))
			{
				synchronized(model)
				{
				    model.setEndOfSimulation(model.getEndOfSimulation() + 1);
					if (model.getEndOfSimulation() > nbAgents)
					{
						System.exit(0);
					}
				}

				return true;
            } else {
                logger.info("executing: " + action + ", but not implemented!");
            }
            if (result) {
                updateAgPercept(agId);
                return true;
            }
        } catch (InterruptedException e) {
        } catch (Exception e) {
            logger.log(Level.SEVERE, "error executing " + action + " for " + ag, e);
        }
        return false;
    }

    private int getAgIdBasedOnName(String agName) {
		return (Integer.parseInt(agName.substring(5))) - 1;
    }
    
    public void initWorld() {
    	try {
	        model = WorldModel.world(worldSize, goldLoad, nbAgents - 4 );

            clearPercepts();
            addPercept(Literal.parseLiteral("gsize(" + simId + "," + model.getWidth() + "," + model.getHeight() 
			                                         + "," + model.getInitialNbGolds() + "," + nbAgents 
													 + ")"));
            addPercept(Literal.parseLiteral("depot(" + simId + "," + model.getDepot().x + "," + model.getDepot().y + ")"));
            if (hasGUI) {
                view = new WorldView(model);
                view.setEnv(this);
                view.udpateCollectedGolds();
            }
			
			addPercept("boss1", Literal.parseLiteral("pos(0,0)"));
			addPercept("boss2", Literal.parseLiteral("pos(0,"+ (worldSize-1) +")"));
			addPercept("boss3", Literal.parseLiteral("pos("+ (worldSize-1) +",0)"));
			addPercept("boss4", Literal.parseLiteral("pos("+ (worldSize-1) +","+ (worldSize-1) +")"));

			updateAgsPercept();        
            informAgsEnvironmentChanged();
    	} catch (Exception e) {
    		logger.warning("Error creating world "+e);
    	}
    }
    
    public void endSimulation() {
        addPercept(Literal.parseLiteral("end_of_simulation(" + simId + ",0)"));
        informAgsEnvironmentChanged();
        if (view != null) view.setVisible(false);
        WorldModel.destroy();
    }

    private void updateAgsPercept() {
        for (int i = 0; i < model.getNbOfAgs(); i++) {
            updateAgPercept(i);
        }
    }

    private void updateAgPercept(int ag) {
		String agName = "miner" + (ag + 1);
        updateAgPercept(agName, ag);
    }

    private void updateAgPercept(String agName, int ag) {
        clearPercepts(agName);
        // its location
        Location l = model.getAgPos(ag);
        addPercept(agName, Literal.parseLiteral("pos(" + l.x + "," + l.y + ")"));

        if (model.isCarryingGold(ag)) {
            addPercept(agName, Literal.parseLiteral("carrying_gold"));
        }

        // what's around
        updateAgPercept(agName, l.x - 1, l.y - 1);
        updateAgPercept(agName, l.x - 1, l.y);
        updateAgPercept(agName, l.x - 1, l.y + 1);
        updateAgPercept(agName, l.x, l.y - 1);
        updateAgPercept(agName, l.x, l.y);
        updateAgPercept(agName, l.x, l.y + 1);
        updateAgPercept(agName, l.x + 1, l.y - 1);
        updateAgPercept(agName, l.x + 1, l.y);
        updateAgPercept(agName, l.x + 1, l.y + 1);
    }

    
    private void updateAgPercept(String agName, int x, int y) {
        if (model == null || !model.inGrid(x,y)) return;
        if (model.hasObject(WorldModel.OBSTACLE, x, y)) {
            addPercept(agName, Literal.parseLiteral("cell(" + x + "," + y + ",obstacle)"));
        } else {
            if (model.hasObject(WorldModel.GOLD, x, y)) {
                addPercept(agName, Literal.parseLiteral("cell(" + x + "," + y + ",gold)"));
            }
            if (model.hasObject(WorldModel.ENEMY, x, y)) {
                addPercept(agName, Literal.parseLiteral("cell(" + x + "," + y + ",enemy)"));
            }
            if (model.hasObject(WorldModel.AGENT, x, y)) {
                addPercept(agName, Literal.parseLiteral("cell(" + x + "," + y + ",ally)"));
            }
        }
    }
	
}

	
