/*
 *  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-25
 *    Fix "if (buf!=NULL)" in openInput.
 */

/*
 *  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 "stdafx.h"
#include <assert.h>
#include <conio.h>
#include <direct.h>
#include <errno.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <Windows.h>

const bool F=false, T=true;

const int bufSize=1024; bool bell=T; char fmt[bufSize];
bool reportError(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 check_txt(char *buf, const int size) {
//   ---------
  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, size, ".txt");
} // check_txt

FILE *openInput() {
//    ---------
  char buf[bufSize], *fName=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the squares file: ");
    if (getFileName(buf, bufSize-4)) { 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) {
    check_txt(buf, bufSize);
    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
//================================================= 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) sprintf_s(baseName, baseSize, "Order%dCopy%d", r, num1);
    else sprintf_s(baseName, baseSize, "Order%dCopy%dto%d", r, num1, num2);
  } else {
    if (num1==num2) sprintf_s(baseName, baseSize, "Order%dx%dCopy%d", r, c, num1);
    else sprintf_s(baseName, baseSize, "Order%dx%dCopy%dto%d", r, c, num1, num2);
  }
  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(".. writing squares to file %s\n", buf);
  else {
    const int msgSize=outSize+50; char msg[msgSize];
    sprintf_s(msg, msgSize, "\a\nCan't open for write %s", buf); perror(msg);
  }
  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;
  if (localtime_s(&local, &startTime)==0) {
    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 _tmain() {
//  ------
  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("\nPress a key to close the console.");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // _tmain