/*
 *  File:    CopySquaresByNumber.cpp
 *  Author:  S Harry White
 *  Created: 2011-11-11
 *  Updated: 2021-12-28
 *    Tidy code.
 *  Updated: 2022-07-16
 *    Support non-square rectangles.
 *  Updated: 2023-01-18
 *    Change to compile with g++ for macOS.
 */

/*
 *  Copies squares from files.
 *  Console inputs:
 *    . the order of the squares
 *    . the squares file name
 *    . a square number or number range
 *  Output:
 *    to file: copy of the selected squares
 */

//#include <ctype.h>
#include <curses.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
//#include <sys/types.h>
#include <time.h>
#include <unistd.h>

const bool F=false, T=true;

const int bufSize=1024;
bool bell=T; char fmt[bufSize];
bool reportError(const char *msg) {
//   -----------
  printf("%sError: %s.\n", bell ? "\a\n" : "",  msg); return bell=F;
} // reportError
//============================================= input ==============================================

bool readError() { printf("\a\nError reading square from file.\n"); return T; } // no write error
//   ---------

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');
} // getY

void checkRange(int *p, int *q) { if (*q==0) *q=*p; else if (*q<*p) { const int x=*p; *p=*q; *q=x; } }
//   ----------

bool getRange(int *p, int *q) {
//   --------
  bool result=F; int c; *p=*q=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; }
    }
    checkRange(p, q); result=T;
  }   
  clearLine(c); return result;
} // getRange

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

void adjustName(char *buf) {
//   ----------
  char *s=buf; bool txt=F;
  if ((*s!='.')&(*s!='/')) { // Assume in current folder.
    char tmp[bufSize]; snprintf(tmp, bufSize, "./%s", buf); strcpy(buf, tmp);
  }
  if (access(buf, R_OK)!=0) {
    while (*s++!='\0')
      ; --s;  if ((strlen(buf)>6)&&(*s--=='t')&&(*s--=='x')&&(*s--=='t')&&(*s=='.')) txt=T;
    if (!txt) strcat(buf, ".txt"); // no .txt entered, add it
  }
} // adjustName

FILE *openInput() {
//    ---------
  char buf[bufSize], *fName=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the squares file: ");
    if (getFileName(buf, bufSize-6)) { fName=buf; break; }
    printf("\a\nCan't read the file name. Try again? y (yes) or n (no) "); if (!getY()) break;
  } while (T);
  if (fName!=NULL) {
    adjustName(buf);
    if ((rfp=fopen(buf, "r"))==NULL) {
      char msg[bufSize+50]; snprintf(msg, bufSize+50, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput
//================================================= output ================================================

FILE *openOutput(const int r, const int c, const int num1, const int num2) {
//    ----------
   FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25, outSize=bufSize+50;
   char baseName[baseSize], buf[outSize];
  if (r==c) {
    if (num1==num2) snprintf(baseName, baseSize, "./Order%dCopy%d", r, num1);
    else snprintf(baseName, baseSize, "./Order%dCopy%dto%d", r, num1, num2);
  } else {
    if (num1==num2) snprintf(baseName, baseSize, "Order%dx%dCopy%d", r, c, num1);
    else snprintf(baseName, baseSize, "Order%dx%dCopy%dto%d", r, c, num1, num2);
  }
  snprintf(buf, outSize, "%s.txt", baseName);
  do {
    if (access(buf, F_OK)!=0) break;
    snprintf(buf, outSize, "%s_%d.txt", baseName, ++sub);
  } while (T);
  if ((wfp=fopen(buf, "w"))==NULL) {
    const int msgSize=outSize+50; char msg[msgSize];
    snprintf(msg, msgSize, "\a\nCan't open for write %s", buf); perror(msg);
  } else printf(".. writing squares to file %s\n", buf);
  return wfp;
} // openOutput

bool copySquares(const int R, const int C, const int num1, const int num2, FILE *rfp, FILE *wfp) {
//   -----------
  const int nn=R*C; int c=fgetc(rfp), squareNum=0;

  while (++squareNum<num1) { // move to first square
    int count=0; bool white=T;
    while (count<nn) {
      if (('0'<=c)&(c<='9')) white=F;
      else { if (c==EOF) return readError(); if (!white) ++count; white=T; }
      c=fgetc(rfp);
    }
  }
  while (c=='\n') c=fgetc(rfp);
  while (squareNum++<=num2) { // copy squares
    int count=0; bool white=T;
    while (T) {
      if (('0'<=c)&(c<='9')) white=F;
      else { if (!white) if (++count==nn) break; white=T; if (c==EOF) return readError(); }
      if (fputc(c, wfp)==EOF) return F; c=fgetc(rfp); 
    }
  }
  if (fputc('\n', wfp)==EOF) return F; return T;
} // copySquares
//================================================= main ==============================================

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

void outputElapsedTime(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);
} // outputtElapsedTime

int main() {
//  ----
  bool another=T, inputOrder=T, noWriteError=T, ok=F; outputLocalTime();
  do {
    int R, C;
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break; }
    if ((R<=0)|(C<=0)) { printf("\aOrder must be positive integer(s).\n\n"); }
    else {
      FILE *rfp=openInput();
      if (rfp!=NULL) {
        int num1, num2; printf("\nInput %s number or number range: ", (R==C) ? "square" : "rectangle");
        if (getRange(&num1, &num2)) {
          time_t startTime=time(NULL); FILE *wfp=openOutput(R, C, num1, num2);
          if (wfp!=NULL) {
            noWriteError=copySquares(R, C, num1, num2, rfp, wfp);
            if (noWriteError) ok=T; fclose(wfp);
          }
          outputElapsedTime(startTime);
        } else {
          printf("\aIncorrect number range.\n");
        } // if (getInts
        fclose(rfp);
      } // if (rfp!=NULL
    } // if (n<=0) {
    if (noWriteError) {
      printf("\nAnother order? input y (yes), n (no) or the order: ");
      if (getYorInts(&R, &C)) inputOrder=(R<0); else break;
    } else { perror("\a\nError writing file"); another=F; }
  } while (another);
  printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main