#include "General.h"

#define WORLD_SIZE 25
#define FIVETEEN 15
#define FORTY 40
#define FIVTY 50
#define THIRTY 30
#define TWENTY 20


//---------------------------------------------------------------------------------------
//                                Random
//                                ------
//
// General      : Create random number.
//
// Parameters   :  
//          seed - seed of random (int **).
//          min  - minimum for result.
//          max  - maximum for result.
//
// Return value : random number.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
int Random(int ** seed, int min, int max)
{
    return ((*((*seed)++) % (++max)) + min);
}

//---------------------------------------------------------------------------------------
//                                InitWorld
//                                ---------
//
// General      : Initialize the world.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//          rows  - Amount of rows in the world.
//          cols  - Amount of columns in the world.
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void InitWorld(SM * world, int rows, int cols)
{
    unsigned short counter;
    for (counter = ZERO; counter < rows; counter++)
    {
        AddRowSM(world);
    }
    for (counter = ZERO; counter < cols; counter++)
    {
        AddColSM(world);
    }
}

//---------------------------------------------------------------------------------------
//                                RemoveWorld
//                                -----------
//
// General      : Deletes the world, freeing up space in memory.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void RemoveWorld(SM * world)
{
    unsigned short counter;
    unsigned short rows = RowsCountSM(world);
    unsigned short cols = ColsCountSM(world);
    for (counter = ZERO; counter < rows; counter++)
    {
        RemoveRowSM(world);
    }
    for (counter = ZERO; counter < cols; counter++)
    {
        RemoveColSM(world);
    }
    free(world);
}

//---------------------------------------------------------------------------------------
//                                RandomWorld
//                                -----------
//
// General      : Creates a random population.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//          seed  - seed for random (int **).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void RandomWorld(SM * world, int ** seed)
{
    unsigned short rows = RowsCountSM(world);
    unsigned short cols = ColsCountSM(world);
    unsigned short counter;
    unsigned short length = rows * cols;
    for (counter = ZERO; counter < length; counter++)
    {
        Random(seed, ZERO, ONE) ? AddItemSM(world, counter / rows, counter % cols) : ZERO;
    }
}

//---------------------------------------------------------------------------------------
//                                Earthquake
//                                ----------
//
// General      : Decreases world size because of earthquake.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//          row   - row of point to check (unsigned int).
//          col   - column of point to check (unsigned int).
//
// Return value : Amount of neughbors (short (0-8)).
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
short CountNeighbors(SM * world, unsigned int row, unsigned int col)
{
    short count = -ONE;
    short loop_counter;
    for (loop_counter = -ONE; loop_counter < TWO; loop_counter++)
    {
        count += GetItemSM(world, row - ONE , col + loop_counter) != NULL;
        count += GetItemSM(world, row, col + loop_counter) != NULL;
        count += GetItemSM(world, row + ONE, col + loop_counter) != NULL;
    }
    
    return (count);
}

//---------------------------------------------------------------------------------------
//                                Earthquake
//                                ----------
//
// General      : Decreases world size because of earthquake.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void Earthquake(SM * world)
{
    int del_rows_count = RowsCountSM(world) / TEN;
    int del_cols_count = ColsCountSM(world) / TEN;
    unsigned short counter;
    for (counter = ZERO; counter < del_rows_count; counter++)
    {
        RemoveRowSM(world);
    }
    for (counter = ZERO; counter < del_cols_count; counter++)
    {
        RemoveColSM(world);
    }
}

//---------------------------------------------------------------------------------------
//                                Epidemic
//                                --------
//
// General      : Expands the world in rows and columns because of ocean retreat.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void WithdrawalOceans(SM * world)
{
    int add_rows_count = RowsCountSM(world) / FIVE;
    int add_cols_count = ColsCountSM(world) / FIVE;
    unsigned short counter;
    for (counter = ZERO; counter < add_rows_count; counter++)
    {
        AddRowSM(world);
    }
    for (counter = ZERO; counter < add_cols_count; counter++)
    {
        AddColSM(world);
    }
}

//---------------------------------------------------------------------------------------
//                                Epidemic
//                                --------
//
// General      : Reduces the amount of ranks in the world because of an epidemic.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void Epidemic(SM * world)
{
    int del_rows_count = RowsCountSM(world) / FIVETEEN;
    unsigned short counter;
    for (counter = ZERO; counter < del_rows_count; counter++)
    {
        RemoveRowSM(world);
    }
}

//---------------------------------------------------------------------------------------
//                                Drying
//                                ------
//
// General      : Expands the world space due to drying and lakes.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void Drying(SM * world)
{
    int add_cols_count = RowsCountSM(world) / FIVETEEN;
    unsigned short counter;
    for (counter = ZERO; counter < add_cols_count; counter++)
    {
        AddColSM(world);
    }
}

//---------------------------------------------------------------------------------------
//                                PercentagePopulation
//                                --------------------
//
// General      : Checks the amount of population relative to world size.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : Percentage (int).
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
int PercentagePopulation(SM * world)
{
    unsigned short alives = ZERO;
    unsigned short counter;
    unsigned short rows = RowsCountSM(world);
    unsigned short cols = ColsCountSM(world);
    unsigned short length = rows * cols;
    for (counter = ZERO; counter < length; counter++)
    {
        alives += (GetItemSM(world, counter / rows, counter % cols) != NULL);
    }
    
    return ((alives / length) * ONE_HUNDRED);
}

//---------------------------------------------------------------------------------------
//                                OneGenerationWorld
//                                ------------------
//
// General      : Performs a one-generation world-wide process.
//
// Parameters   :  
//          world - sparce matrix pointer (SM *).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void OneGenerationWorld(SM * world)
{
    int percentage = PercentagePopulation(world);
    (percentage >= FIVTY) ? Earthquake(world) : ZERO;
    (percentage < TWENTY) ? WithdrawalOceans(world) : ZERO;
    (percentage == THIRTY) ?  Epidemic(world) : ZERO;
    (percentage == FORTY) ? Drying(world) : ZERO;
}

//---------------------------------------------------------------------------------------
//                                OneGenerationPopulation
//                                -----------------------
//
// General      : Performs one generation process in population.
//
// Parameters   :  
//          world - pointer of sparce matrix pointer (SM **).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void OneGenerationPopulation(SM ** world)
{
    SM * new_world;
    SM * temp;
    unsigned short counter;
    unsigned short rows = RowsCountSM(*world);
    unsigned short cols = ColsCountSM(*world);
    unsigned short length = rows * cols;
    short count_neighbors;
    InitSM(&new_world);
    InitWorld(new_world, rows, cols);
    for (counter = ZERO; counter < length; counter++)
    {
        count_neighbors = CountNeighbors(*world, counter / rows, counter % cols);
        temp = GetItemSM(*world, counter / rows, counter % cols);
        if ((temp == NULL && count_neighbors == TWO) ||
            (temp != NULL && count_neighbors <= THREE && count_neighbors >= TWO))
        {
            AddItemSM(new_world, counter / rows, counter % cols);
        }
    }
    RemoveWorld(*world);
    *world = new_world;
}

//---------------------------------------------------------------------------------------
//                                OneGeneralGeneration
//                                --------------------
//
// General      : Performs one generation procedure and prints it.
//
// Parameters   :  
//          world - pointer of sparce matrix pointer (SM **).
//
// Return value : None.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void OneGeneralGeneration(SM ** world)
{
    OneGenerationPopulation(world);
    OneGenerationWorld(*world);
    PrintSM(*world, PrintIntDataType);
}

//---------------------------------------------------------------------------------------
//                                      Minesweeper
//                                      -----------
//
// General : The game is a zero-player game, meaning that its evolution is determined by
//           its initial state, requiring no further input. One interacts with the Game
//           of Life by creating an initial configuration and observing how it evolves.
//
// Input   : Accepts like the generations the user wants to do.
//
// Process : The program accepts the number of generations and loops each time it
//           processes the world and prints to the user what happens in each generation.
//
// Output  : What the world looks like in each generation.
//
//---------------------------------------------------------------------------------------
// Programmer : Cohen Idan
// Student No : 211675038
// Date       : 30.12.2019
//---------------------------------------------------------------------------------------
void main(void)
{
    unsigned int generations;
    unsigned int counter;
    int * seed;
    SM * world;
    InitSM(&world);
    InitWorld(world, WORLD_SIZE, WORLD_SIZE);
    RandomWorld(world, &seed);
    PrintSM(world, PrintIntDataType);
    printf("Enter count of generations: ");
    scanf("%u", &generations);
    for (counter = ZERO; counter < generations; counter++)
    {
        OneGeneralGeneration(&world);
    }
    RemoveWorld(world);
}