/*
 *  File:    GetQR.cpp
 *  Author:  S Harry White
 *  Created: 2020-12-29
 * Updated: 2023-01-26
 *   Open output files in the current folder.
 * Updated: 2023-02-20
 *   Improve openInput and openOutput.
 */

/*
 *  Gets the quotient and remainder squares of magic squares.
 */

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

int **X=NULL, N, M, O, NN, squareNumber, fieldWidth; const int maxN=20000;
const bool F=false, T=true;

int getFieldWidth(int i) { int width=1; while ((i/=10)!=0) ++width; return width; }
//  -------------

void initGlobals() { M=N-1; NN=N*N; squareNumber=0; fieldWidth=getFieldWidth(N-1); }
//  ------------
//========================================= store ===============================================

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

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

void freeStore() { freeintArray(&X, N); }
//   ---------

bool allocateintArray(int ***a) {
//   ----------------
  bool ok=((*a=(int**) malloc(N*sizeof(int*)))!=NULL);
  if (ok) {
    int numAllocated=N;
    for (int i=0; i<N; i++) {
      int *p=(int*) malloc(N*sizeof(int));    
      if (p==NULL) { numAllocated=i; ok=F; break; } (*a)[i]=p;
    }
    if (!ok) freeintArray(a, numAllocated);
  }
  return ok;
} // allocateintArray

bool allocateStore() {
//   -------------
  bool ok=allocateintArray(&X); if (!ok) {reportError(storeAllocFail); freeStore(); } return ok;
} // allocateStore
//========================================= input ===============================================

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

bool getY() {
//   ----
  int c;  do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n'));
  clearLine(c);  return (c=='Y')|(c=='y');
}

bool getYorOrder(int *n) {
//   -----------
  bool result=F; int c; *n=0;

  do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') );
  if ( (c=='Y')|(c=='y') ) result=T;
  else if ( (c!='N')&(c!='n') )
    if ( ('1'<=c)&(c<='9') ) {
      int i=c-'0'; while ( ('0'<=(c=getchar())) && (c<='9') ) i=i*10+c-'0'; *n=i; result=T;
    }   
  clearLine(c); return result;
} // getYorOrder

int getInt() { int n=0; scanf_s("%d", &n); clearLine(getchar()); return n; }
//  -------  

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; }
  *s='\0'; return T;
} // getFileName

const int bufSize=1024, outSize=bufSize+50;
void check_txt(char *buf) {
//   ---------
  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");
} // check_txt

FILE *openInput(char buf[bufSize]) {
//    ---------
  char *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the squares file: ");
    if (getFileName(buf, bufSize-4)) { rFname=buf; break; }
    printf("\a\nCan't read the file name. Try again? y (yes) or n (no) "); if (!getY()) break;
  } while (T);
  if (rFname!=NULL) {
    check_txt(buf);
    if (fopen_s(&rfp, buf, "r")!=0) {
      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

void baseZero() { for (int r=0; r<N; r++) for (int c=0; c<N; c++) --X[r][c]; }
//   --------

bool readSquare(FILE *rfp) {
//   ----------
  int smallest=LONG_MAX, biggest=LONG_MIN;

  for (int r=0; r<N; r++) {
    for (int c=0; c<N; c++) {
	   int tmp, rv;
     if ( (rv=fscanf_s(rfp, "%d", &tmp))==1) {
        if (tmp<smallest) smallest=tmp; if (tmp>biggest) biggest=tmp; X[r][c]=tmp;
      } else {
        if ( (rv!=EOF)|(r!=0)|(c!=0) ) printf("\a\nError reading file.\n"); return F;
      }
    }
  }
  bool zeroBase=smallest==0; int maxv=zeroBase?NN-1:NN;
  if ((smallest<0)|(biggest>maxv)) {
    printf("Number out of range: %d\n", smallest<0?smallest:biggest); return F; }
  if (!zeroBase) baseZero(); ++squareNumber; return T;
} // readSquare
//=========================================== output ===========================================

void stripName(char *inFname, char *obuf) {
//   ---------
  char *s=inFname; while (*s++!='\0');
  while (--s!=inFname) // Remove .txt and any directory path.
    if (*s=='.') *s='\0';  else if ((*s=='\\')|(*s=='/')) { ++s; break; }
  strcpy_s(obuf, outSize, s); 
} // stripName

char outFile[outSize];
FILE *openOutput(char *inFname) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25;
  char baseName[baseSize], buf[outSize]; stripName(inFname, buf);
  sprintf_s(baseName, baseSize, "%sQR", buf); sprintf_s(buf, outSize, "%s.txt", baseName);
  do {
    if (_access_s(buf, 00)==ENOENT) break; sprintf_s(buf, outSize, "%s_%d.txt", baseName, ++sub);
  } while (T);
  if (fopen_s(&wfp, buf, "w")==0) {
    printf("..output file %s\n", buf); strcpy_s(outFile, outSize, buf);
  } else {
    char msg[outSize+50];
    sprintf_s(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  }
  return wfp;
} // openOutput

typedef bool (*t_fprintFW)(FILE *fp, int i);

bool fprintFW1(FILE *fp, int i) { return fprintf(fp, "%1d",  i) > 0; }
bool fprintFW2(FILE *fp, int i) { return fprintf(fp, "%2d",  i) > 0; }
bool fprintFW3(FILE *fp, int i) { return fprintf(fp, "%3d",  i) > 0; }
bool fprintFW4(FILE *fp, int i) { return fprintf(fp, "%4d",  i) > 0; }
bool fprintFW5(FILE *fp, int i) { return fprintf(fp, "%5d",  i) > 0; }
bool fprintFW6(FILE *fp, int i) { return fprintf(fp, "%6d",  i) > 0; }

static t_fprintFW fprintFW[] = { NULL, fprintFW1, fprintFW2, fprintFW3, fprintFW4, fprintFW5, fprintFW6 };
const int maxFieldWidth=6;

bool printQ(FILE *wfp) {
//   ------
  int fw0=fieldWidth, fw=fw0+1; if (fw>maxFieldWidth) return false;
  for (int i=0; i<N; i++) {
    if (!fprintFW[fw0](wfp, X[i][0]/N)) return false;
    for (int j=1; j<N; j++) if (!fprintFW[fw](wfp, X[i][j]/N)) return false;
    if (fputc('\n', wfp)==EOF) return false;
  }
  return fputc('\n', wfp)!=EOF;
} // printQ

bool printR(FILE *wfp) {
//   ------
  int fw0=fieldWidth, fw=fw0+1; if (fw>maxFieldWidth) return false;
  for (int i=0; i<N; i++) {
    if (!fprintFW[fw0](wfp, X[i][0]%N)) return false;
    for (int j=1; j<N; j++) if (!fprintFW[fw](wfp, X[i][j]%N)) return false;
    if (fputc('\n', wfp)==EOF) return false;
  }
  return fputc('\n', wfp)!=EOF;
} // printR

bool getQR(FILE *wfp) { return printQ(wfp)&&printR(wfp); }
//   -----

//================================================= main ================================================

//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

void printElapsedTime(time_t startTime) {
//   ---------------- 
  const int et=(int)difftime(time(NULL), startTime);
  if (et>0) printf("\nelapsed time %d:%02d:%02d\n", et/3600, et%3600/60, et%60);
} // printElapsedTime

bool validOrder() {
//   ----------
  if ((N<1)|(N>maxN)) { printf("\aERROR: Supported orders are 1 to %d.\n", maxN); return F; } return T;
} // validOrder

int main() {
//  ----
  bool inputOrder=T, ok=T; char buf[bufSize]; //outputLocalTime();
  do {
    if (inputOrder) { printf("\nOrder? "); N=getInt(); } initGlobals();
    if (validOrder()&&allocateStore()) {
      FILE *rfp=openInput(buf); time_t startTime=time(NULL);
      if (rfp!=NULL) {
        FILE *wfp=openOutput(buf);
        if (wfp!=NULL) {
          bool writeError=F; squareNumber=0;
          while (readSquare(rfp)) { writeError=!getQR(wfp); if (writeError) break; }
          fclose(wfp); if (squareNumber==0) remove(outFile);
          if (writeError) { perror("\n\aError writing file"); ok=F; break; }
          else printf("Number of squares: %d\n", squareNumber);
        }
        fclose(rfp);
      } // rfp!=NULL
      printElapsedTime(startTime); freeStore();
    } // allocateStore

    printf("\nAnother order? input y (yes), n (no) or the order: ");
    if (getYorOrder(&N)) inputOrder=(N<0); else break;
  } while (T);
  printf("\nPress a key to close the console");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main