/*
 * Copyright (C) 2022 by Steve Litt
 * Permission is hereby granted, free of charge, to any
 * person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the
 * Software without restriction, including without
 * limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the
 * Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall
 * be included in all copies or substantial portions of
 * the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * The preceding license is the Expat license, visible at: 
 * https://directory.fsf.org/wiki/License:Expat
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int ** creMy2dArray(int ***pMy2dArray, int numRows, int numCols){
   // If you want to access an (int **) as a multidimensional
   // array like my2dArray[2][5], you MUST NOT malloc or calloc
   // the (int **) all at one time. The all at once method
   // causes no errors or warnings with gcc -Wall, but it
   // causes voluminous Valgrind errors and segfaults when
   // you try to write to it as my2dArray[2][5].
   
   // Instead, you must do your allocations one dimension 
   //  at a time, as follows in this function...


   // First calloc (*pMy2dArray) as an array of int pointers.
   // Remember, (*pMy2dArray) is a 2 dimensional array, because
   // pMy2dArray is a POINTER to a 2 dimensional array. Passing
   // it in as a pointer is necessary because this function
   // modifies the 2d array, by allocating it and filling in
   // the ints (the cells if it were a spreadsheet).
   // If you use malloc() instead of calloc(), 
   // Valgrind will pitch a fit about
   // "Conditional jump or move depends on uninitialised value(s)".
   //
   // Because creMy2dArray must change its first argument
   // (by allocating), the address of the 2d array must
   // be passed in, so it's a pointer to pointer to pointer
   // to int. The return value is merely a pointer to
   // pointer to int, so the contents of the first 
   // argument are passed back as a return value.
   (*pMy2dArray) = (int **)calloc((numRows), sizeof(int *));

   // Now for each int pointer, calloc (not malloc because Valgrind)
   //  each int pointer to an array of ints
   for(int row = 0; row < numRows; row++){
      (*pMy2dArray)[row] = (int *)calloc((numCols), sizeof(int));
   }

   // Now, and only now, can you fill the cells of the 2d array
   for(int row=0; row < numRows; row++){
      for(int col=0; col < numCols; col++){
         (*pMy2dArray)[row][col] = (row+1) * (col+1);
      }
   }
   return *pMy2dArray;
}


// Now, because my2dArray has been properly initialized,
// it can be treated as a 2d array with each cell
// readable and writeable as my2dArray[row][col],
// assuming row and col are within initialized limits.
// Because this function does nothing to change the 2d
// array, the 2d array itself, which is simply a pointer to
// pointer to int, can be passed in as an argument.
void showMy2dArray(int **my2dArray, int numRows, int numCols){
   for(int row=0; row < numRows; row++){
      printf("\n");
      for(int col=0; col < numCols; col++){
         printf("%4d", my2dArray[row][col]);
      }
   }
   printf("\n");
}


// Because you'll be changing my2dArray, you must pass in its
// address (pMy2dArray) instead of itself (my2dArray). Then,
// within the function, you perform the usual 2d array
// operations on (* pMy2dArray). 
// The free() operations are done in the reverse order as
// when it was created in the first place.
void freeMy2dArray(int ***pMy2dArray, int numRows, int numCols){
   int row;
   for(row = 0; row < numRows; row++){
      free((*pMy2dArray)[row]);
   }
   free((*pMy2dArray));
}

// main() calls a function to create and init
// the 2d array, a second function to show
// its contents, and a third function to free
// it, the proper way, so Valgrind doesn't gripe
// about memory leaks on the way out.
int main(int argc, char * argv[]){
   int **my2dArray;
   int rows = 10;
   int cols = 12;
   my2dArray = creMy2dArray(&my2dArray, rows, cols);
   showMy2dArray(my2dArray, rows, cols);
   freeMy2dArray(&my2dArray, rows, cols);
}
