#include "toonsplat.h"
#include <cstdio>
#include <vector>
#include <algorithm>
#include <deque>
#include <string.h>
using namespace std;

// Botlets will be 0 indexed
vector<Position> player_botlets;
vector<Position> enemy_botlets;
vector<deque<Operation>> operation_queues;
vector<vector<int>> board;
deque<Operation> default_operation_queue;

char tempstr[1000];

int sign(int x){
    if (x > 0) return 1;
    if (x < 0) return -1;
    return 0;
}

bool made_invalid_call = false;

char error_str[1005];

void send_error_message(char * str){
    made_invalid_call=true;
    strcpy(error_str,str);
    // printf(str);
    
}


void move(int botlet, Position position, int mode){
    if (botlet >= INITIAL_BOTLETS || botlet < 0){
        sprintf(tempstr,"Error: Tried to access botlet out of range in move()");
        send_error_message(tempstr);
        throw 1; 
    }
    if (max(position.x,position.y) >= BOARD_SIZE || min(position.x,position.y) < 0){
        sprintf(tempstr,"Error: invalid position given to move()");
        send_error_message(tempstr);
        throw 1;
    }
    if (mode < 0 || mode > 3){
        sprintf(tempstr,"Error: invalid mode given to move()");
        send_error_message(tempstr);
        throw 1;
    }
    operation_queues[botlet].push_back({position,mode});
}

void cancel_all_operations(int botlet){
    if (botlet >= INITIAL_BOTLETS || botlet < 0){
        sprintf(tempstr,"Error: Tried to access botlet out of range in cancel_all_operations()");
        send_error_message(tempstr);
        throw 1; 
    }
    while(!operation_queues[botlet].empty()){
        operation_queues[botlet].pop_back();
    }
}

int cnts[BOARD_SIZE][BOARD_SIZE];
FILE *pipe_output;
FILE *pipe_input;

void transmit_then_exit(){
    if (made_invalid_call){
        fprintf(pipe_output, "1\n%s\n",error_str);
        fflush(pipe_output);
    }
    else{
        fprintf(pipe_output, "2\n");
    }
    exit(0);
}

int cnt_to_thing(int x){
    if (x > 0) return 1;
    if (x < 0) return 2;
    return 0;
}

int main(int argc, char *argv[]){
    if (argc != 3){
        printf("Must give it two arguments: name of the pipefile to write to and read from repsectively");
        return 1;
    }

    pipe_output = fopen(argv[1], "w");
    pipe_input = fopen(argv[2], "r");

    board = vector<vector<int>>(BOARD_SIZE, vector<int> (BOARD_SIZE, 0));
    operation_queues = vector<deque<Operation>>(INITIAL_BOTLETS,default_operation_queue);

    board[0][0] = 1;
    board[BOARD_SIZE-1][BOARD_SIZE-1] = -1;

    for (int i = 0; i < INITIAL_BOTLETS; i++){
        player_botlets.push_back({0,0});
    }

    for (int i = 0; i < INITIAL_BOTLETS; i++){
        enemy_botlets.push_back({BOARD_SIZE-1,BOARD_SIZE-1});
    }

    try {
        init();
    }
    catch (...) {
        transmit_then_exit();
    }
    fprintf(pipe_output,"0\n");    
    fflush(pipe_output);
    // Wait until it gets the go-ahead.
    double remaining_time;
    fscanf(pipe_input,"%lf", &remaining_time);
    // fprintf(stderr,"Got here");
    for (int cur_tick = 1; cur_tick <= NUM_TICKS; cur_tick++){
        // Receive the commands for the bots.
        try {
            tick(board,player_botlets,operation_queues,enemy_botlets,cur_tick,remaining_time);
        }
        catch (...) {
            transmit_then_exit();
            // send_error_message("Bro your program crashed. Maybe put some information here?");
        }
        
        // actually move all the bots based on their operation queues
        for (int i = 0; i < INITIAL_BOTLETS; i++){
            if (!operation_queues[i].empty()){
                Operation cur_op = operation_queues[i].front();
                int tx = cur_op.position.x;
                int ty = cur_op.position.y;
                int cx = player_botlets[i].x;
                int cy = player_botlets[i].y;
                int mode = cur_op.mode;
                if (mode == 0){
                    if (tx != cx){
                        cx += sign(tx-cx);
                    }
                    else {
                        cy += sign(ty-cy);
                    }
                }                
                if (mode == 1){
                    if (ty != cy){
                        cy += sign(ty-cy);
                    }
                    else{
                        cx += sign(tx-cx);
                    }
                }
                if (mode == 2){
                    if (tx == cx || (ty != cy && rand() % 2 == 0)){
                        cy += sign(ty-cy);
                    }
                    else{
                        cx += sign(tx-cx);
                    }
                }
                player_botlets[i] = {cx,cy};
                if (cx == cur_op.position.x && cy == cur_op.position.y){
                    operation_queues[i].pop_front();
                }
                
            }
        }
        
        // Transmit our new bot positions
        fprintf(pipe_output,"0\n");
        for (int i = 0; i < INITIAL_BOTLETS; i++){
            fprintf(pipe_output,"%d %d\n", player_botlets[i].x, player_botlets[i].y);
        }
        fflush(pipe_output);
        if (cur_tick == NUM_TICKS){
            break;
        }        
        // Receive new enemy bot positions,
        for (int i = 0; i < INITIAL_BOTLETS; i++){
            fscanf(pipe_input,"%d %d\n", &enemy_botlets[i].x,&enemy_botlets[i].y);
        }
        fscanf(pipe_input,"%lf", &remaining_time);
        // Then calculate board state.
        for (Position p : player_botlets){
            cnts[p.x][p.y] ++;
        }
        for (Position p : enemy_botlets){
            cnts[p.x][p.y] --;
        }
        for (Position p : player_botlets){
            board[p.x][p.y] = cnt_to_thing(cnts[p.x][p.y]);
        }
        for (Position p : enemy_botlets){
            board[p.x][p.y] = cnt_to_thing(cnts[p.x][p.y]);            
        }
        for (Position p : player_botlets){
            cnts[p.x][p.y] = 0;
        }
        for (Position p : enemy_botlets){
            cnts[p.x][p.y] = 0;
        }
    }
}
