/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package timgittos;

import ch.idsia.ai.agents.Agent;
import ch.idsia.ai.agents.RegisterableAgent;
import ch.idsia.mario.environments.Environment;
import ch.idsia.mario.engine.sprites.Mario;
import ch.idsia.mario.engine.GlobalOptions;
import ch.idsia.utils.MathX;

import java.util.Vector;

/**
 *
 * @author tim
 */
public class BlogAgent extends RegisterableAgent implements Agent 
{
    private int jumpCount = 0;
    private int rightCount = 0;
            
    private int jumpHeight = 0; //7 max
    private int jumpLength = 0; //14 max
    private boolean jumping = false;
    
    //Index of columns containing a pit
    private boolean[] pits;
    private Vector cannons;
    private Vector pipes;
    
    
    public BlogAgent()
    {
        super("Blog Agent");
        reset();
    }
    
    public void reset()
    {
        action = new boolean[Environment.numberOfButtons];
        action[Mario.KEY_RIGHT] = true;
        action[Mario.KEY_SPEED] = true;
    }
    
    public boolean[] getAction(Environment observation)
    {
        byte[][] levelState = observation.getLevelSceneObservation();

        processTerrain(levelState);
        int deltaX = 11;
        int deltaJump = 11;

        //Analyse pipes       
        int pipeDelta = setDeltaForObstacles(pipes);  
        if (pipeDelta < deltaX && pipeDelta > 0)
            deltaX = pipeDelta;
        //Analyse cannons
        int cannonDelta = setDeltaForObstacles(cannons);
        if (cannonDelta < deltaX && cannonDelta > 0)
            deltaX = cannonDelta;

        //Analyse the gaps
        boolean gap = false;
        for(int y = 0; y < 22; y++)
        {
            if (pits[y])
            {
                if (action[Mario.KEY_RIGHT] && y > 11)
                {
                    int localDelta = y - 11;
                    if (localDelta < deltaJump)
                        deltaJump = localDelta;
                }
                else if (action[Mario.KEY_LEFT] && y < 11)
                {
                    int localDelta = 11 - y;
                    if (localDelta < deltaJump)
                        deltaJump = localDelta;
                }
                gap = true;
            }
        }


        //Analyse ground above Mario
        boolean raisedGround = false;
        if (action[Mario.KEY_RIGHT])
        {
            for (int x = 12; x < 22; x++)
            {
                for (int y = 12; y >= 0; y--)
                {
                    if (levelState[y][x] == -10)
                    {
                        int localDelta = x - 11;
                        if (localDelta < deltaX)
                            deltaX = localDelta;
                        raisedGround = true;
                        break;
                    }
                }
                if (raisedGround)
                    break;
            }
        }

        jumpHeight = 7;
        jumpLength = 14;

        if (((pipes.size() > 0 || cannons.size() > 0 || raisedGround) && deltaX >= 0 && deltaX < 5))
        {
            if (observation.mayMarioJump() && !action[Mario.KEY_JUMP])
                action[Mario.KEY_JUMP] = true;
        }

        //If mario is jumping, there must be an obstacle in the way
        if (action[Mario.KEY_JUMP])
        {
            if (gap && deltaJump >= 3)
            {
                jumpHeight = 7;
                jumpLength = 1;
            }
        }
        //No obstacles, but there is a gap
        else if (gap && deltaJump >= 0 && deltaJump <= 3)
        {
            if (observation.mayMarioJump() && !action[Mario.KEY_JUMP])
                action[Mario.KEY_JUMP] = true;
        }

        maintainJump();
        return action;

    }
    
    //Analyse terrain
    private void processTerrain(byte[][] state)
    {
        //Init features
        pits = new boolean[22];
        cannons = new Vector();
        pipes = new Vector();

        //Init pits
        for (int i = 0; i < 22; i++)
        {
            pits[i] = true;
        }

        for (int y = 21; y >= 0; y--)
        {
            for (int x = 0; x < 22; x++)
            {
                //Look for gaps
                pits[x] = pits[x] && state[y][x] == 0;

                //Look for cannons and pipes
                if (state[y][x] == 20)
                {
                    boolean pipeFound = false;
                    //Check if x,y is in pipes
                    int pipeLength = pipes.size();
                    for (int pi = 0; pi < pipeLength; pi++)
                    {
                        int[] coords = (int[])pipes.elementAt(pi);
                        if (coords != null && coords[0] == x && coords[1] == y)
                        {
                            //Pipe found, skip it
                            pipeFound = true;
                        }
                    }
                    if (!pipeFound)
                    {
                        //Peek ahead to determine if it's a pipe or a cannon
                        if(x < 21 && state[y][x + 1] == 20)
                        {
                            //It's a pipe, add it and the next one to the pipe array
                            pipes.add(new int[]{x + 1, y});
                        }
                        else
                        {
                            //It's a cannon
                            cannons.add(new int[]{x, y});
                        }
                    }
                }
            }
        }
    }
    
    private int setDeltaForObstacles(Vector tiles)
    {
        int length = tiles.size();
        int buffer = 0;
        int localDeltaX = 0;
        //Loop through every tile
        for (int counter = 0; counter < length; counter++)
        {
            //Get coords of tile
            int[] coords = (int[])tiles.elementAt(counter);
            //Calculate the delta, then check to see if it's good enough
            localDeltaX = coords[0] - 11;
            if (action[Mario.KEY_RIGHT] && coords[0] > 11)
            {
                //If the tile is after mario, and we're heading right, delta is good
                break;
            }
            else if (action[Mario.KEY_LEFT] && coords[0] < 11)
            {
                //If the tile is before mario and we're heading left, add to buffer
                if (coords[0] > buffer)
                    buffer = coords[0];
            }
            else if (action[Mario.KEY_LEFT])
            {
                //If the tile is after mario, but we're heading left, get the last tile loc from the buffer
                localDeltaX = buffer - 11;
                break;
            }
        }
        return localDeltaX;
    }

    //Do jump calculations
    private void maintainJump()
    {        
        if(action[Mario.KEY_JUMP] && (jumpCount > jumpHeight))
        {
            action[Mario.KEY_JUMP] = false;
            jumpCount = 0;
        }
        // otherwise you're in the middle of jump, increment jumpCount
        else if(action[Mario.KEY_JUMP])
        {
            jumpCount++;
        }

        if (jumping)
        {
            if (action[Mario.KEY_RIGHT] && (rightCount > jumpLength))
            {
                action[Mario.KEY_RIGHT] = false;
                rightCount = 0;
            }
            else if (action[Mario.KEY_RIGHT])
            {
                rightCount++;
            }
        }
    }
    
    //Debugging purposes
    private void printCodeView(byte[][] levelState, byte[][] enemyState)
    {
        //Debug information
        for (int y = 0; y < 22; y++)
        {
            for (int x = 0; x < 22; x++)
            {
                //Fill mario
                if (x == 11 && y == 11)
                {
                    System.out.print("mmm");
                    
                    System.out.print("|");
                    continue;
                }
                
                //Fill current target
                /*
                if (x == highest[0] && y == highest[1])
                {
                    System.out.print("ttt");
                    System.out.print("|");
                    continue;
                }
                 */
                
                //Check for enemies first
                if (enemyState[y][x] > 0)
                {
                    String enemyValue = Integer.toString(enemyState[y][x]);
                    int padding = 3 - enemyValue.length();
                    if (padding == 2)
                        System.out.print(" " + enemyValue + " ");
                    else if (padding == 1)
                        System.out.print(" " + enemyValue);
                    else
                        System.out.print(enemyValue);
                }
                else
                {
                    //Check for terrain next
                    String terrainValue = Integer.toString(levelState[y][x]);
                    int padding = 3 - terrainValue.length();
                    if (padding == 2)
                        System.out.print(" " + terrainValue + " ");
                    else if (padding == 1)
                        System.out.print(" " + terrainValue);
                    else
                        System.out.print(terrainValue);
                }
                System.out.print("|");
            }
            System.out.print("\n");
        }
        System.out.print("---------------\n");
    }
}