/*
 *  File:    GetDuplicates.cpp
 *  Author:  S Harry White
 *  Created: 2022-04-24
 *  Updated: 2022-07-22
 *    Add support for non-square recrangles.
 *  Updated: 2023-03-28
 *    Fix store allocation. Remove allocatedR=0, allocatedC=0 from initGlobals().
 */

/*
 * Reports duplicate numbers in recrangles.
 *
 */

#include "stdafx.h"
#include <conio.h>
#include <errno.h>
#include <io.h>
#include <stdio.h>
#include <string.h>
//#include <time.h>
#include <Windows.h>

int R, C, // The order of the rectanglse.
    RC;   // R*C

const int startSize=32;
int **iRectangle=NULL, allocatedR, allocatedC, rectangleNumber;
const bool F=false, T=true; bool *used=NULL, bell;

void initGlobals(bool zeroBase) { RC=R*C; rectangleNumber=0; bell=T; }
//  -----------
//=============================================== store ===============================================

char *storeAllocFail="Storage allocation failed"; 
bool reportError(char *msg) { printf("%sError: %s.\n", bell ? "\a\n" : "",  msg); return bell=F; }
//   -----------

void freeRectangle(int*** rectangle, const int allocated) {
//   -------------
  if (*rectangle!=NULL) {
    for(int i=0; i<allocated; i++) free((*rectangle)[i]); free(*rectangle); *rectangle=NULL;
  }
} // freeRectangle

void freeStore() {
//   ---------
  if (iRectangle!=NULL) freeRectangle(&iRectangle, allocatedR);
  if (used!=NULL) free(used); used=NULL; allocatedR=0; allocatedC=0;
} // freeStore

bool allocateRectangle(int*** rectangle, const int nR, const int nC) {
//   -----------------
  bool ok; *rectangle=(int**) malloc(nR*sizeof(int*)); ok=(*rectangle!=NULL);
  if (ok) {
    int numAllocated=0;
    for (int i=0; i<nR; i++) {
      int* p=(int*) malloc(nC*sizeof(int)); (*rectangle)[i]=p; if (p==NULL) { numAllocated=i; ok=F; break; }
    }
    if (!ok) freeRectangle(rectangle, numAllocated);
  }
  return ok;
} // allocateRectangle

bool allocateBools(bool **x, const int size) { return ((*x=(bool*) malloc(size*sizeof(bool)))!=NULL); }
//   ------------

bool allocateStore() {
//   -------------
  bool ok=T; int nR=R, nC=C; if (nR<startSize) nR=startSize; if (nC<startSize) nC=startSize;
  if ((nR>allocatedR)|(nC>allocatedC)) {
    freeStore();
    if (ok=allocateRectangle(&iRectangle, nR, nC))
      if (ok=allocateBools(&used, nR*nC+1)) { allocatedR=nR; allocatedC=nC; }
    if (!ok) { freeStore(); reportError(storeAllocFail); }
  }
  return ok;
} //allocateStore
//================================================ input ==============================================

void clearLine(int c) { while (c!='\n') c=getchar(); }
//   ---------

bool getInts(int *p, int *q, int c) {
//   -------
  bool ok=F; *p=0; *q=0; if (c<0) do { c=getchar(); } while ((c==' ')|(c=='\t'));
  if ( ('1'<=c)&(c<='9') ) {
    int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *p=i;
    if ((c==' ')|(c=='\t')) {
      do { c=getchar(); } while ((c==' ')|(c=='\t'));
      if ( ('1'<=c)&(c<='9') ) {
        int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *q=i;
      }
    }
    if (*q==0) *q=*p; ok=T;
  }  
  clearLine(c); if (!ok) return reportError("Invalid input"); return T;
} // getInts

bool getYorInts(int *p, int *q) {
//   ----------
  bool ok=F; int c; *p=-1; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') );
  if ( (c=='Y')|(c=='y') ) ok=T; else if ( (c!='N')&(c!='n') ) return getInts(p, q, c);  
  clearLine(c); return ok;
} // getYorInts


bool getFileName(char *buf, const int size) {
//   -----------
  int c, i=0; char *s=buf;
  do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') ); *s=c;
  while (i++<size) if ( (*++s=getchar())=='\n') break;
  if (*s!='\n') { printf("\nFile name too long.\n"); clearLine(*s); return F; }
  while ( (*--s==' ')||(*s=='\t') ); /* strip trailing whitespace */ *++s='\0'; return T;
} // getFileName

const int bufSize=1024;
FILE *openInput(char *fin) {
//    ---------
  char buf[bufSize]; char *rFname=buf; FILE *rfp=NULL; strcpy_s(buf, bufSize, fin);
  if (rFname!=NULL) {
    char *s=buf; bool txt=F; while (*s++!='\0');
    while (--s!=buf)
      if (*s=='.') { txt=(*++s=='t')&&(*++s=='x')&&(*++s=='t')&&(*++s=='\0'); break; }
    if (!txt) strcat_s(buf, bufSize, ".txt");
    if (fopen_s(&rfp, rFname, "r")==0) printf("Input file is %s\n", buf);
    else {
      const int msgSize=bufSize + 50; char msg[msgSize];
      sprintf_s(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

bool readRectangle(FILE *rfp, bool *oor) {
//   -------------
  *oor=F;
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) {
	  int tmp, rv;
    if ( (rv=fscanf_s(rfp, "%d", &tmp))==1) {
      if ((tmp<0)|(tmp>RC)) {
        if (!*oor) {
          printf("%s %d, number %d is out of range\n",
            (R==C) ? "Square" : "Rectangle", rectangleNumber+1, tmp); *oor=T;
        }
      }
      iRectangle[r][c]=tmp;
    } else {
      if ((rv!=EOF)|(r!=0)|(c!=0)) printf("\a\nError reading from file.\n"); return F;
    }
  }
  ++rectangleNumber; return T;
} // readRectangle
//================================================= main ==================================================

bool validOrder() {
//   ----------
  if ((R<=0)|(C<=0)) { printf("\aERROR: Order must be positive.\n"); return F; } return T;
}// validOrder

//void outputLocalTime() {
////   --------------
//  time_t startTime=time(NULL);
//  struct tm local;  localtime_s(&local, &startTime); char dateTime[100];
//  size_t slen=strftime(dateTime, 100, "%A %Y-%m-%d %X %Z\n\n\0", &local); printf(dateTime);
//} // outputLocalTime

int main() {
//  ----
  bool another=T, inputOrder=T, ok=F; allocatedR=0, allocatedC=0;
  do {
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break; }
    if (validOrder()) {
      initGlobals(F);
      if (allocateStore()) {
        char buf[bufSize]; printf("File? "); getFileName(buf, bufSize);
        FILE *rfp=openInput(buf);
        if (rfp!=NULL) {
          bool oor=F, found=F; ok=T;
          while (readRectangle(rfp, &oor)) if (!oor) {
            int first=T; for (int i=0; i<=RC; ++i) used[i]=F;
            for (int r=0; r<R; r++) {
              for (int c=0; c<C; c++) {
                int v=iRectangle[r][c];
                if (used[v]) {
                  if (first) printf("%s %d duplicate:", (R==C) ? "Square" : "Rectangle", rectangleNumber);
                  first=F; printf(" %d", v);
                }
                used[v]=T;
              }
            }
            if (!first) { putchar('\n'); found=T; }
          }
          if (!found) printf("No duplicates found.\n"); fclose(rfp);
        }
      }
    }
     printf("\nAnother order? input y (yes), n (no) or the order: ");
     if (getYorInts(&R, &C)) inputOrder=(R<0); else another=F;
  }  while (another);
  freeStore(); printf("\nPress a key to close the console");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main