/*
 * Name: Owen Sullivan
 * Date Submitted: 9/19/22
 * Lab Section: 006
 * Assignment Name: Lab 3: Finding Groups Using Recursion
*/

#include "Grouping.h"

#include <iostream>
#include <fstream>
#include <vector>
#include <assert.h>

using namespace std;

// Implement the (parameterized) constructor and findGroup functions below

// Paramaterized constructor for Grouping class
Grouping::Grouping(string filename) {

    // Open a file with filename "filename" in read mode
    ifstream inFile(filename);
    // Check that file opened correctly
    assert(inFile.is_open());

    // Create a temp character for comparisons
    char temp;
    // Loop through the rows in grid
    for (int row = 0; row < (sizeof(grid)/sizeof(grid[0])); row++) {

        // Loop through the columns in grid
        for (int col = 0; col < (sizeof(grid[0])/sizeof(int)); col++) {

            // Read next character from input file and store to temp
            inFile >> temp;

            // Check if character last read is '.'
            if (temp == '.') {

                // Set grid value to 0 to signify unoccupied space
                grid[row][col] = 0;

            } else {

                // Set grid value to 1 to signify occupied space
                grid[row][col] = 1;

            }

        }

    }

    // Loop through the rows in grid
    for (int row = 0; row < (sizeof(grid)/sizeof(grid[0])); row++) {

        // Loop through the columns in grid
        for (int col = 0; col < (sizeof(grid[0])/sizeof(int)); col++) {
            
            // Check if current square is occupied
            if (grid[row][col] != 0) {
            
                // Create a vector to be populated with the coordinates of pairs in group
                vector<GridSquare> currentGroup;
                
                // Call findGroup(), passing current row, column, and currentGroup vector
                findGroup(row, col, currentGroup);

                // If currentGroup is not empty (indicating there was at least one occupied space found)
                if (!currentGroup.empty()) {

                    // Add current group to next index in groups vector
                    groups.push_back({currentGroup});

                }

            }

        }

    }

}

// Function which checks that a square has not already been grouped, then recursively finds squares with common edges and adds all to current group
void Grouping::findGroup(int r, int c, vector<GridSquare> &currentGroup) {

    // If the row or column being checked is outside the bounds of the grid size
    if (r < 0 || r > ((sizeof(grid)/sizeof(grid[0])) - 1) || c < 0 || c > ((sizeof(grid)/sizeof(grid[0])) - 1)) {

        // End function early
        return;

    }
    
    // Check if current square is occupied
    if (grid[r][c] != 0) {

        // Loop through rows in groups vector
        for (int row = 0; row < groups.size(); row++) {

            // Loop through columns in groups vector
            for (int col = 0; col < groups[row].size(); col++) {

                // If the current row and column being checked are equal to the current row and column in groups vector
                if (r == groups[row][col].getRow() && c == groups[row][col].getCol()) {

                    // End function early
                    return;

                }

            }

        }
        
        // Loop through rows in currentGroup vector
        for (int row = 0; row < currentGroup.size(); row++) {

            // If the current row and column being checked are equal to the current row and column in currentGroup vector
            if (r == currentGroup[row].getRow() && c == currentGroup[row].getCol()) {

                // End function early
                return;

            }

        }
        
        // Add the current row and column to the next index in currentGroup vector
        currentGroup.push_back(GridSquare(r, c));

        // Call findGroup(), passing surrounding rows, surrounding columns, and currentGroup vector
        findGroup((r - 1), c, currentGroup);
        findGroup((r + 1), c, currentGroup);
        findGroup(r, (c - 1), currentGroup);
        findGroup(r, (c + 1), currentGroup);

    }

}

// Simple main function to test Grouping
// Be sure to comment out main() before submitting
// int main() {
    
//     Grouping input1("input1.txt");
//     input1.printGroups();
    
//     return 0;

// }

// Do not modify anything below

GridSquare::GridSquare() : row(0), col(0) {} // Default constructor, (0,0) square

GridSquare::GridSquare(int r, int c) : row(r), col(c) {} // (r,c) square

// Compare with == operator, used in test cases
bool GridSquare::operator== (const GridSquare r) const {
    
    if ((row == r.row) && (col == r.col)) {
        
        return true;
    
    }
    
    return false;

}

int GridSquare::getRow() { // return row value
    
    return row;

}

int GridSquare::getCol() { //return column value
    
    return col;

}

// Output using << operator, used in Grouping::printGroups()
// Function definition for <ostream> << <GridSquare>
ostream &operator<< (ostream &os, const GridSquare obj) {
    
    os << "(" << obj.row << "," << obj.col << ")";
    return os;

}

Grouping::Grouping() : grid{}, groups() {} // Default constructor, no groups

void Grouping::printGroups() { // Displays grid's groups of squares
    
    for (int g = 0; g < groups.size(); g++) {
        
        cout << "Group " << (g + 1) << ": ";
        for (int s = 0; s < groups[g].size(); s++) {
            
            cout << " " << groups[g][s];
        
        }
        cout << endl;
    
    }

}

vector<vector<GridSquare>> Grouping::getGroups() { // Needed in unit tests
    
    return groups;

}