#include "input_manager.h"


extern int defeat;
extern int win;

//return 1 if the value given in parameter is in the list.
int in_list(int value, int *list, int len){
    for (int i = 0; i<len; i++){
        if (list[i]==value){
            return 1;
        }
    }
    return 0;
}

// apply the movement from (y,x) and to direction d1 first then d2 if possible.
void make_move(monsters* mon, map* m, int y, int x, direction d1, direction d2){
    int y1=y, x1=x;
    switch (d1)
    {
    case DIR_UP:
        y1 = y - 1;
        break;
    case DIR_DOWN:
        y1 = y + 1;
        break;
    case DIR_RIGHT:
        x1 = x + 1;
        break;
    case DIR_LEFT:
        x1 = x - 1;
        break;
    default:
        break;
    }
    int y2=y1, x2=x1;
    switch (d2){
        case DIR_UP: y2 = y1-1; break;
        case DIR_DOWN: y2 = y1+1; break;
        case DIR_RIGHT: x2 = x1+1; break;
        case DIR_LEFT: x2 = x1-1; break;
        default : break;
    }
    update_monster(mon,m,y,x,y1,x1);
    //apply_events(game);
    if (d2 != DIR_UNKNOWN){
        update_monster(mon,m,y1,x1,y2,x2);
    }
}

// verify if a move is possible and apply it if so
int ok_move(monsters* mon, map* m, int y, int x, direction d1, direction d2){
    int y1 = y, x1 = x;
    switch (d1){
        case DIR_UP: y1 = y-1; break;
        case DIR_DOWN: y1 = y+1; break;
        case DIR_RIGHT: x1 = x+1; break;
        case DIR_LEFT: x1 = x-1; break;
        default : break;
    }
    int y2=y1, x2=x1;
    switch (d2){
        case DIR_UP: y2 = y1-1; break;
        case DIR_DOWN: y2 = y1+1; break;
        case DIR_RIGHT: x2 = x1+1; break;
        case DIR_LEFT: x2 = x1-1; break;
        default : break;
    }
    if (d2 == DIR_UNKNOWN){
        if (!is_obstacle(m->tiles[y1][x1])){
            make_move(mon,m,y,x,d1,d2);
            return 1; 
        }
    } else if (!is_obstacle(m->tiles[y1][x1]) && !is_obstacle(m->tiles[y2][x2])){
        make_move(mon,m,y,x,d1,d2);
        return 1;
    } else if (!is_obstacle(m->tiles[y1][x1])){
        make_move(mon,m,y,x,d1,DIR_UNKNOWN);
        return 1;
    }
    return 0;
}

// move the monster to the right or the left depending on the player's position
int move_up_or_down(monsters* mon, map* m, int y_monster, int x_monster, int y_player){
	if (y_monster > y_player)
		return ok_move(mon, m, y_monster,x_monster, DIR_UP, DIR_UP);
	else if (y_monster < y_player)
		return ok_move(mon, m, y_monster,x_monster, DIR_DOWN, DIR_DOWN);
    return 0;
}

// move the monster to the right or the left depending on the player's position  
int move_left_or_right(monsters* mon, map* m, int y_monster, int x_monster, int x_player){
	if (x_monster > x_player)
       return ok_move(mon, m, y_monster,x_monster, DIR_LEFT, DIR_LEFT);
	else if (x_monster < x_player)
		return ok_move(mon, m, y_monster,x_monster, DIR_RIGHT, DIR_RIGHT);
    return 0;
}

// move the monster to one of the four corners
int move_to_corner(monsters* mon, map* m, int y_monster, int x_monster, int y_player, int x_player){
	if (x_monster > x_player && y_monster > y_player)
		return ok_move(mon, m, y_monster,x_monster, DIR_UP, DIR_LEFT) || ok_move(mon, m, y_monster,x_monster, DIR_LEFT, DIR_UP);
	else if (x_monster > x_player && x_monster < y_player)
		return ok_move(mon, m, y_monster,y_player, DIR_DOWN, DIR_LEFT) || ok_move(mon, m, y_monster,x_monster, DIR_LEFT, DIR_DOWN);
	else if (x_monster < x_player && y_monster > y_player)
		return ok_move(mon, m, y_monster,x_monster, DIR_RIGHT, DIR_UP) || ok_move(mon, m, y_monster,x_monster, DIR_UP, DIR_RIGHT);
	else if (x_monster < x_player && y_monster < y_player)
		return ok_move(mon, m, y_monster,x_monster, DIR_RIGHT, DIR_DOWN) || ok_move(mon, m, y_monster,x_monster, DIR_DOWN, DIR_RIGHT);
    return 0;
}

int follow_player(monsters* mon, map* m, int *  y, int * x){
    int y_monster = *y;
    int x_monster = *x;
    int y_player = m->player_y, x_player = m->player_x;
    if (x_player == x_monster){ // same column
		return move_up_or_down(mon, m, y_monster, x_monster, y_player);
	} else if (y_monster==y_player){ // same line
		return move_left_or_right(mon, m, y_monster, x_monster, x_player);
	} else if ((x_player==x_monster+1 || x_player==x_monster-1) && (y_player==y_monster+1 || y_player==y_monster-1)){ // corners
		return move_to_corner(mon, m, y_monster, x_monster, y_player, x_player);
	} else if (x_player==x_monster+1 || x_player==x_monster-1){  // one tile to the left or the right
        #ifdef line_first
		return move_up_or_down(mon, m, y_monster, x_monster, x_player) || move_to_corner(mon, m, y_monster, x_monster, y_player, x_player);
        #else
        return move_to_corner(mon, m, y_monster, x_monster, y_player, x_player) || move_up_or_down(mon, m, y_monster, x_monster, y_player);
        #endif
	} else if (y_player==y_monster+1 || y_player==y_monster-1){ // one tile upper or lower 
        #ifdef line_first
		return move_left_or_right(mon, m, y_monster, x_monster, x_player) || move_to_corner(mon, m, y_monster, x_monster, y_player, x_player);
        #else
        return move_to_corner(mon, m, y_monster, x_monster, y_player, x_player) || move_left_or_right(mon, m, y_monster, x_monster, x_player);
        #endif
	} else {
        #ifdef line_first
		return move_left_or_right(mon, m, y_monster, x_monster, x_player)  || move_up_or_down(mon, m, y_monster, x_monster, y_player) || move_to_corner(mon, m, y_monster, x_monster, y_player, x_player);
        #else
        #endif
        return move_to_corner(mon, m, y_monster, x_monster, y_player, x_player) || move_up_or_down(mon, m, y_monster, x_monster, y_player) || move_left_or_right(mon, m, y_monster, x_monster, x_player);
	}
}

void circle(monsters* mon, map* m, int y, int x) {
    if (y == 6 && x != 6 && m->tiles[y][x-1] != 'M')
            update_monster(mon,m,y,x,y,x-1); // left
    else if (y == 4 && x!=10 && m->tiles[y][x+1] != 'M')
        update_monster(mon,m,y,x,y,x+1);  //right
    else if(x == 6 && y!=4 && m->tiles[y-1][x] != 'M')
        update_monster(mon,m,y,x,y-1,x);  //up
    else if(x == 10 && y!=6 && m->tiles[y+1][x] != 'M')
        update_monster(mon,m,y,x,y+1,x);  //down
}

int search_monster(monsters* mon, map* m, int y, int x) {
    for (int i = 0; i < mon->length; i++)
    {
        if (mon->info[i].x == x && mon->info[i].y == y)
            return i;
    }
    return -1;
}

void come_and_go(monsters* mon, map * m, int y, int x){
    int monster_id = search_monster(mon, m, y, x);
    if (monster_id == -1)
        return;
    int dir = mon->info[monster_id].dir;

    if(x==m->width-2 || m->tiles[y][x+1] == '@' || m->tiles[y][x+1] == 'M') {
        mon->info[monster_id].dir = DIR_LEFT;
        update_monster(mon,m,y,x,y,x-1);
    }
    else if (x == 2 || m->tiles[y][x-1] == 'M') {
        mon->info[monster_id].dir = DIR_RIGHT;
        update_monster(mon,m,y,x,y,x+1);
    }
    else if (dir == DIR_RIGHT)
        update_monster(mon,m,y,x,y,x+1);
    else if (dir == DIR_LEFT)
        update_monster(mon,m,y,x,y,x-1);
    else if (dir == DIR_DOWN) {
        mon->info[monster_id].dir = DIR_LEFT;
        update_monster(mon,m,y,x,y,x-1);
    }
}

// verify if the player is seen by the monster
void is_player_seen(monsters* mon, map * m, int y, int x){
    int monster_id = search_monster(mon, m, y, x);
    if (monster_id == -1)
        return;
    int dir = mon->info[monster_id].dir;
#ifndef BUG
    if (dir == DIR_RIGHT && m->player_x > x && m->player_y == y)
#else
    if (dir == DIR_RIGHT && m->player_y == y)
#endif
        mon->info[monster_id].player_seen = true;
#ifndef BUG
    else if (dir == DIR_LEFT && m->player_x < x && m->player_y == y)
#else
    else if (dir == DIR_LEFT && m->player_y == y)
#endif
        mon->info[monster_id].player_seen = true;
#ifndef BUG
    else if (dir == DIR_UP && m->player_y < y && m->player_x == x)
#else
    else if (dir == DIR_UP && m->player_x == x)
#endif
        mon->info[monster_id].player_seen = true;
}

// move the monster to the player's position
void catch_player(monsters* mon, map * m, int y, int x){
    int monster_id = search_monster(mon, m, y, x);
    if (y < m->player_y && !is_obstacle(m->tiles[y+1][x]) && m->tiles[y+1][x] != 'M' && m->tiles[y+1][x] != '@') {
        mon->info[monster_id].dir = DIR_DOWN;
        update_monster(mon,m,y,x,y+1,x);
    } else if (y > m->player_y && !is_obstacle(m->tiles[y-1][x]) && m->tiles[y-1][x] != 'M' && m->tiles[y-1][x] != '@') {
        mon->info[monster_id].dir = DIR_UP;
        update_monster(mon,m,y,x,y-1,x);
    } else if (x < m->player_x && !is_obstacle(m->tiles[y][x+1]) && m->tiles[y][x+1] != 'M' && m->tiles[y][x+1] != '@') {
        mon->info[monster_id].dir = DIR_RIGHT;
        update_monster(mon,m,y,x,y,x+1);
    } else if (x > m->player_x && !is_obstacle(m->tiles[y][x-1]) && m->tiles[y][x-1] != 'M' && m->tiles[y][x-1] != '@') {
        mon->info[monster_id].dir = DIR_LEFT;
        update_monster(mon,m,y,x,y,x-1);
    }
}

// move the monster once and verify to not move twice a monster.
void move_monsters(monsters* mon, map * m){
    int done[8], len = 0;  
    for (int y = 0; y < m->height; y++){
        for (int x = 0; x < m->width; x++){
            if (m->tiles[y][x] == 'M'){
                int id = search_monster(mon, m, y, x);
                if (id != -1 && !in_list(id, done, len)){
                    if (strcmp(m->name, "third_room") != 0)
                        is_player_seen(mon,m,y,x);

                    if (mon->info[id].player_seen && strcmp(m->name, "third_room") != 0)
                        catch_player(mon,m,y,x);
                    else if (!strcmp(m->name, "first_room"))
                        come_and_go(mon,m,y,x);
                    else if (!strcmp(m->name, "second_room"))
                        circle(mon,m,y,x);
                    else if (!strcmp(m->name, "third_room"))
                        follow_player(mon,m,&y,&x);
                    
                    done[len] = id;
                    len++;
                }
            }
        }
    }
}