#include "General.h"

#define ATOM 4
#define NEUTRON_UP 3
#define NEUTRON_DOWN 5
#define NEUTRON_LEFT 7
#define NEUTRON_RIGHT 9
#define FILE_NAME "test.txt"
#define OPEN_FILE_TYPE "rt"
#define ASCII_COMMA ','
#define ASCII_ZERO '0'
#define ASCII_HYPHEN '-'
#define SM_ROWS 500
#define SM_COLUMNS 600



//---------------------------------------------------------------------------------------
//                                ReadStringFromFile
//                                ------------------
//
// General      : Read from file as string and save the string to array.
//
// Parameters   :  
//          char ** ptr_save_data - save data from file in array
//          unsigned int length   - length of array
//
// Return value : The number (int).
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void ReadStringFromFile(char ** ptr_save_data, unsigned int * length)
{
    *length = ZERO;
    char ch;
    FILE * fp = fopen(FILE_NAME, OPEN_FILE_TYPE);
    if (fp)
    {
        *ptr_save_data = malloc(sizeof(ch));
        while ((ch = fgetc(fp)) != EOF)
        {
            *((*ptr_save_data) + *length) = ch;
            (*length)++;
            *ptr_save_data = realloc(*ptr_save_data, sizeof(ch) * (*length + ONE));
        }
        *ptr_save_data = realloc(*ptr_save_data, sizeof(ch) * (*length));
        fclose(fp);
    }
    else
    {
        printf("Can't read file : \"%s\"\n", FILE_NAME);
    }
}

//---------------------------------------------------------------------------------------
//                                StringToNumber
//                                --------------
//
// General      : Convert string to number.
//
// Parameters   :  
//          char * ptr_str      - array of char
//          unsigned int length - length of array
//
// Return value : The number (int).
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
int StringToNumber(char * ptr_str, unsigned int length)
{
    int number = ZERO;
    unsigned int counter;
    if (*ptr_str == ASCII_HYPHEN)
    {
        number = -(*(++ptr_str));
        ptr_str++;
    }
    for (counter = ZERO; counter < length; counter++)
    {
        number = (number * TEN) + (*(ptr_str++) - ASCII_ZERO);
    }

    return (number);
}


//---------------------------------------------------------------------------------------
//                                FileDataToArray
//                                ---------------
//
// General      : Change the data we get from file as string to numbers array.
//
// Parameters   :  
//          char * ptr_file_data          - pointer to char array
//          unsigned int length_file_data - length of char array
//          int ** ptr_array              - pointer to save data from char array
//          unsigned int * length_array   - pointer of ptr_array length
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void FileDataToArray(char * ptr_file_data, unsigned int length_file_data, int ** ptr_array, unsigned int * length_array)
{
    char * temp_ptr_file_data = ptr_file_data;
    int temp_number;
    unsigned int loop_counter;
    *length_array = ONE;
    *ptr_array = malloc(sizeof(int));

    for (loop_counter = ZERO; loop_counter < length_file_data; loop_counter++)
    {
        if (*temp_ptr_file_data == ASCII_COMMA)
        {
            (*ptr_array)[*length_array - ONE] = StringToNumber(ptr_file_data, temp_ptr_file_data - ptr_file_data);
            (*length_array)++;
            *ptr_array = realloc(*ptr_array, sizeof(int) * (*length_array));
            ptr_file_data = ++temp_ptr_file_data + ONE;
            loop_counter++;
        }
        temp_ptr_file_data++;
    }
    *ptr_array = realloc(*ptr_array, sizeof(int) * (--(*length_array)));
}

//---------------------------------------------------------------------------------------
//                                InitBoard
//                                ---------
//
// General      : Initializing simulation board.
//
// Parameters   :  
//          SM * board        - Simulation board
//          unsigned int rows - Amount of rows in board
//          unsigned int cols - Amount of cols in board
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void InitBoard(SM * board, unsigned int rows, unsigned int cols)
{
    unsigned int counter;
    for (counter = ZERO; counter < rows; counter++)
    {
        AddRowSM(board);
    }
    for (counter = ZERO; counter < cols; counter++)
    {
        AddColSM(board);
    }
    
}

//---------------------------------------------------------------------------------------
//                                ArrayDataToSM
//                                -------------
//
// General      : Copies all points to a sparse matrix from the array.
//
// Parameters   :  
//          SM * board          - Simulation board
//          int * ptr_arr       - pointer of points array (x1, y1, x2, y2, ..)
//          unsigned int length - length of points array
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void ArrayDataToSM(SM * board, int * ptr_arr, unsigned int length)
{
    unsigned int counter;
    for (counter = ZERO; counter < length; counter += TWO)
    {
        AddItemSM(board, ptr_arr[counter], ptr_arr[counter+ONE]);
        GetItemSM(board, ptr_arr[counter], ptr_arr[counter+ONE])->info.int_ = ATOM;
    }
}

//---------------------------------------------------------------------------------------
//                                MoveUp
//                                ------
//
// General      : Creates to the up of the node, a neutron facing the up side 
//                of the simulation board.
//
// Parameters   :  
//          SM * board - Simulation board
//          SM * node  - address of node in simulation board
//          queue * qt - queue of active objects
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void MoveUp(SM * manager, SM * node, queue * qt)
{
    SM * temp = NULL;
    if (node->row > ZERO)
    {
        temp = GetItemSM(manager, node->row - ONE, node->col);
        if (!temp)
        {
            AddItemSM(manager, node->row - ONE, node->col);
            temp->info.int_ = ONE;
        }
        temp->info.int_ *= node->info.int_;
    }
    if (temp != NULL)
    {
        InsertQueue(qt, (Data_Type)(void *)temp);
    }
}

//---------------------------------------------------------------------------------------
//                                MoveDown
//                                --------
//
// General      : Creates to the down of the node, a neutron facing the down side 
//                of the simulation board.
//
// Parameters   :  
//          SM * board - Simulation board
//          SM * node  - address of node in simulation board
//          queue * qt - queue of active objects
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void MoveDown(SM * manager, SM * node, queue * qt)
{
    SM * temp = NULL;
    if ((node->row + ONE) < RowsCountSM(manager))
    {
        temp = GetItemSM(manager, node->row + ONE, node->col);
        if (!temp)
        {
            AddItemSM(manager, node->row + ONE, node->col);
            temp->info.int_ = ONE;
        }
        temp->info.int_ *= NEUTRON_DOWN;
    }
    if (temp != NULL)
    {
        InsertQueue(qt, (Data_Type)(void *)temp);
    }
}

//---------------------------------------------------------------------------------------
//                                MoveLeft
//                                --------
//
// General      : Creates to the left of the node, a neutron facing the left side 
//                of the simulation board.
//
// Parameters   :  
//          SM * board - Simulation board
//          SM * node  - address of node in simulation board
//          queue * qt - queue of active objects
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void MoveLeft(SM * manager, SM * node, queue * qt)
{
    SM * temp = NULL;
    if (node->col > ZERO)
    {
        temp = GetItemSM(manager, node->row, node->col - ONE);
        if (!temp)
        {
            AddItemSM(manager, node->row, node->col - ONE);
            temp->info.int_ = ONE;
        }
        temp->info.int_ *= NEUTRON_LEFT;
    }
    if (temp != NULL)
    {
        InsertQueue(qt, (Data_Type)(void *)temp);
    }
}

//---------------------------------------------------------------------------------------
//                                MoveRight
//                                ---------
//
// General      : Creates to the right of the node, a neutron facing the right side 
//                of the simulation board.
//
// Parameters   :  
//          SM * board - Simulation board
//          SM * node  - address of node in simulation board
//          queue * qt - queue of active objects
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void MoveRight(SM * board, SM * node, queue * qt)
{
    SM * temp = NULL;
    if ((node->col + ONE) < ColsCountSM(board))
    {
        temp = GetItemSM(board, node->row, node->col + ONE);
        if (!temp)
        {
            AddItemSM(board, node->row, node->col + ONE);
            temp->info.int_ = ONE;
        }
        temp->info.int_ *= NEUTRON_RIGHT;
    }
    if (temp != NULL)
    {
        InsertQueue(qt, (Data_Type)(void *)temp);
    }
}

//---------------------------------------------------------------------------------------
//                                Explode
//                                -------
//
// General      : Handles atomic fission mode.
//
// Parameters   :  
//          SM * board - Simulation board
//          SM * node  - address of node in simulation board
//          queue * qt - queue of active objects
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void Explode(SM * board, SM * node, queue * qt)
{
    MoveUp(board, node, qt);
    MoveDown(board, node, qt);
    MoveLeft(board, node, qt);
    MoveRight(board, node, qt);
}   

//---------------------------------------------------------------------------------------
//                                Steps
//                                -----
//
// General      : Handles all objects within the queue..
//
// Parameters   :  
//          SM * manager - pointer of simulation board
//          queue * qt   - queue of objects
//
// Return value : Amount atoms explode.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
unsigned int Steps(SM * manager, queue * qt)
{
    unsigned int booms_count = ZERO;
    SM * main_node;
    while (!IsEmptyQueue(qt))
    {
        main_node = (SM *)RemoveQueue(qt).pointer;
        switch (main_node->info.int_)
        {
        case NEUTRON_UP:
            MoveUp(manager, main_node, qt);
            break;
        case NEUTRON_DOWN:
            MoveDown(manager, main_node, qt);
            break;
        case NEUTRON_LEFT:
            MoveLeft(manager, main_node, qt);
            break;
        case NEUTRON_RIGHT:
            MoveRight(manager, main_node, qt);
            break;
        
        default:
            if (!(main_node->info.int_ % TWO));
            {
                Explode(manager, main_node, qt);
                booms_count++;
            }
            break;
        }
        RemoveItemSM(manager, main_node->row, main_node->col);
    }

    return (booms_count);
}

//---------------------------------------------------------------------------------------
//                             Atomic explosion
//                            -------------------
//
// General    :    The program is essentially an atomic explosion 
//                 simulation
//
// Input      :    A location where we want to blow up the atom
// 
// Process    :    The program reads atoms the locations of the atoms 
//                 and presents them in the matrix. Then we choose a 
//                 place where we want to blow up the atom in the matrix
//                 and count how many nuclear fission happened 
//
// Output     :    None
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 26.01.2020
//---------------------------------------------------------------------------------------
void main(void)
{
    SM * board;
    SM * temp;
    queue active_objects;
    char * chars_from_file;
    unsigned int length_chars_from_file;
    int * atoms_position;
    unsigned int length_atoms_position;
    unsigned int chosen_row;
    unsigned int chosen_col;
    unsigned int booms_count;
    ReadStringFromFile(&chars_from_file, &length_chars_from_file);
    FileDataToArray(chars_from_file, length_chars_from_file, atoms_position, &length_atoms_position);
    free(chars_from_file);
    InitSM(&board);
    InitBoard(board, SM_ROWS, SM_COLUMNS);
    InitQueue(&active_objects);
    ArrayDataToSM(board, atoms_position + ONE, length_atoms_position - ONE);
    free(atoms_position);
    while (!temp)
    {
        printf("Enter X of atom you want to explode: ");
        scanf(" %u", &chosen_col);
        printf("Enter Y of atom you want to explode: ");
        scanf(" %u", &chosen_row);
        temp = GetItemSM(board, chosen_row, chosen_col);
    }
    InsertQueue(&active_objects, (Data_Type)(void *)temp);
    booms_count = Steps(board, &active_objects);

    printf("Energy: %d\n", booms_count);

}