//********************************************************************************
//* File       : CsvView.hpp                                                     *
//* Author     : Mahlon R. Smith                                                 *
//*              Copyright (c) 2024-2026 Mahlon R. Smith, The Software Samurai   *
//*                  GNU GPL copyright notice located below                      *
//* Date       : 05-Apr-2025                                                     *
//* Version    : (see AppVersion string)                                         *
//*                                                                              *
//* Description: Definition of the CsvView class and supporting data and         *
//*              definitions for the application.                                *
//********************************************************************************

//****************
//* Header Files *
//****************
#include <fstream>         //* C++ file I/O
#include <cstring>         //* C-language string functions
#include <cuchar>          //* Convert UTF-8 to UTF-16
#include "AnsiCmd.hpp"     //* ANSI command library definition
#include "gString.hpp"     //* gString text-formatting utility
#include "CsvFile.hpp"     //* General file-access definitions

//***********************************************
//* Constant Data:                              *
//***********************************************

//* Application version string. Keep it current! *
static const wchar_t* const AppVersion = L"0.0.06" ;
static const wchar_t* const crYears    = L"2024-2026" ;
static const wchar_t* const appName1   = L"CsvView (csvv)" ;
static const char*    const titleTemplate =  
   "%S v:%S %S(c) %S  The Software Samurai   \n"
   "=======================================================================" ;

static const wchar_t* CSV_EXT = L".csv" ;     // required source-filename extension
const uint8_t UTF_MSB = 0xFE ;                // UTF-16 byte-order-mark MSB
const uint8_t UTF_LSB = 0xFF ;                // UTF-16 byte-order-mark LSB
const uint8_t UTF_BOM[] = { 0xEF, 0xBB, 0xBF } ; // UTF-8 byte-order-mark
const wchar_t COMMA = L',' ;                  // field delimiter
const int     ibBYTES = gsALLOCMAX ;          // size of input buffer

//* Master declaration for control of debugging output *
#define DEBUG_CSVV (0)

//* Valid encoding formats for CSV files. *
enum EncType : short
{ etUTF8,         // UTF-8 encoding, no byte-order-mark (BOM)
  etUTF8bom,      // UTF-8 encoding with byte-order-mark
  etUTF16le,      // UTF-16 encoding, little-endian BOM
  etUTF16be,      // UTF-16 encoding, big-endian BOM
  etUNKNOWN
} ;

//* Formatting options for data display. *
enum DispFmt : short
{
   dfHoriz,       // Horizontal - one record per display line (default)
   dfVert,        // Vertical   - One field per display line
   dfQuick        // Quick      - output directly to stdout (horizontal format)
 } ;

//* Supported user-interface languages *
enum UILang
{
   uilEN = ZERO,  // English
   uilZH,         // Zhongwen (Chinese, simplified - mainland)
   uilENV         // Taken from terminal environment (default)
} ;

//* Operation definitions for the 'userOption' member of the commArgs class.*
enum clArgs : short 
{ clQuickHelp, clHelp, clHelpless, clVersion, clOpen, clCreate, 
  clSrcStats, clTermSize, clTermInfo, clCapture, clError } ;

//*********************************************
//* Gather and process command-line arguments *
//*********************************************
class commArgs
{
   public:
   commArgs ( int argc, char** argv, char** argenv ) :
               argCount(argc), argList(argv), envList(argenv)
   {
      *this->appPath    = NULLCHAR ;
      this->userOption  = clQuickHelp ;
      this->trgEncode   = etUNKNOWN ;
      this->capA = this->capB = -1 ;
      this->trgCrlf     = false ;
      this->staFlag = this->dimFlag = this->cfgFlag = this->pakFlag = false ;
      // Note: The tConfig member is initialized by its constructor. 
      //       Specific modifications may be made via command-line arguments.
   }
   short    argCount ;           // application's 'argc'
   char**   argList ;            // application's 'argv'
   char**   envList ;            // application's 'argenv'
   char     appPath[MAX_PATH] ;  // directory of application executable
   TermConfig tConfig ;          // terminal configuration info (see AnsiCmd class)
   clArgs   userOption ;         // decoded command-line option
   EncType  trgEncode ;          // for creating a CSV file, encoding type
   short    capA, capB ;         // range of records for 'capture' option
   bool     trgCrlf ;            // for creating a CSV file, optional CR/LF
   bool     staFlag ;            // set if request for sourcefile stats
   bool     dimFlag ;            // set if request for terminal dimensions
   bool     cfgFlag ;            // set if request for terminal configuration
   bool     pakFlag ;            // set if request for pause (press-a-key)
} ;


#if 0    // CURRENTLY UNUSED
//******************
//* Define a field *
//******************
class csvField
{
   public:
   short   indx ;                // offset into text storage area (csvRecord:fldTxt[])
   short   cnt ;                 // number of characters (including nullchar)
} ;

//*******************
//* Define a record *
//*******************
class csvRecord
{
   public:
   char *fldTxt ;                // pointer to field text: fields are comma 
                                 // delimited and record is null terminated
   short txtLen ;                // number of data bytes in 'fldTxt' incl. nullchar
   short recWidth ;              // sum of columns for all fields
   csvField *fld ;               // pointer to an array of field objects
} ;
#endif   // CURRENTLY UNUSED

//********************************
//* Application class definition *
//********************************
class CsvView
{
   public:

   virtual ~CsvView ( void ) ;            //* destructor
   CsvView ( commArgs& clArgs ) ;         //* constructor

   private:
   //*******************
   //* Private Methods *
   //*******************
   void reset ( void )                    //* initialize all data members
   {
      this->ansi        = NULL ;
      *this->pwdPath    = NULLCHAR ;
      *this->orgPath    = NULLCHAR ;
      *this->srcPath    = NULLCHAR ;
      *this->cpyPath    = NULLCHAR ;
      *this->trgPath    = NULLCHAR ;
      this->txtAttr     = aesFG_DFLT ;
      this->hdrAttr     = aesFG_DFLT ;
      this->bgdAttr     = aesBG_DFLT ;
      this->termRows    = ZERO ;
      this->termCols    = ZERO ;
      this->dispfmt     = dfHoriz ;
      this->lang        = uilENV ;

      this->recCount    = ZERO ;
      this->fldCount    = ZERO ;
      this->maxRecWidth = ZERO ;
      this->maxHdrWidth = ZERO ;
      this->maxFldWidth = NULL ;
      this->srcEncode   = etUNKNOWN ;
      this->srcCsvFmt   = false ;
      this->srcCrlf     = false ;
      this->srcHeader   = false ;
      this->hdrLock     = false ;
      this->srcScanned  = false ;
      this->trgExists   = false ;

      // Note: The 'srcStats' member self-initializes when instantiated.
   }  //* reset() *

   //* Format the data for display and write it to the terminal window. *
   void  DisplayData ( void ) ;
   short ddFormatHorizontal ( const gString& gssrc, uint8_t *obuff, 
                              const uint8_t **phptr, short& olines, bool trunc = false ) ;
   void ddFormatVertical ( const wchar_t *hdr, const wchar_t *fld, 
                           gString& gsOut, bool trunc = false ) ;
   //* Perform preliminary scan on source file. Gather file statistics. *
   bool  SrcScan ( void ) ;
   //* Parse the source file header (if present). *
   bool  parseHeader ( const uint8_t *buff, short cnt, short& fldCnt ) ;
   //* Parse the record and gather statistics. *
   void  parseFields ( const uint8_t *buff, short cnt ) ;
   //* Read a single line from the source file. *
   bool  readRecord ( std::ifstream& ifs, uint8_t *ibuff, int iblen, short& cnt, bool testBom = false ) ;
   //* Pad field width so left edge of each field is vertically aligned.*
   bool alignFieldColumns ( const gString& gssrc, gString& gstrg ) ;
   //* Test whether file contains UTF-16 encode data (with or without BOM). *
   bool isUTF16 ( uint8_t *buff, short& cnt ) ;
   //* Convert data from UTF-16 to UTF-8 encoding *
   void convert2UTF8 ( uint8_t *buff, short& cnt, bool xnl = true ) ;
   //* Convert a UTF-8 string to UTF-16 (big-endian or little-endian). *
   short convert2UTF16 ( uint8_t *buff, short cnt, EncType etype ) ;
   //* Convert two-byte "\n" sequences to newline characters (or CR/LF pairs). *
   short convertNewlines ( uint8_t *buff, short cnt, bool crlf = false ) ;
   //* Convert each CR/LF pair to a single newline character. *
   short deflateNewlines ( uint8_t *buff, short cnt ) ;
   //* Test whether file contains a UTF-8 BOM, and if so, remove it. *
   bool isUTF8bom ( uint8_t *buff, short& cnt ) ;
   //* Count the number of double-quote delimeters in source data. *
   short dqDelimeterCount ( const uint8_t *buff, short cnt ) ;
   //* If double quotation marks used as field delimiters, remove them. *
   bool convertDoubleQuotes ( uint8_t *buff, short& cnt ) ;
   //* Test size of source data for potential 'less' buffer overflow. *
   bool outputThresholdTest ( void ) ;
   //* Save re-encoded data to target file. *
   void save2Target ( void ) ;

   //* Housekeeping Methods. Located in: CsvView.cpp *
   void  GetCommandLineArgs ( commArgs& ca ) ;   //* Parse command line
   clArgs gclaFilespec ( const gString& gsSrc ) ;//* Normalize filespec
   void  gclaLanguage ( commArgs& ca ) ;         //* Locale and language
   void  pause ( void ) ;                        //* Ask user to press a key
   void  ComposeTitle ( gString& gs, bool withNL = false ) ;
   void  DisplayVersion ( void ) ;
   void  DisplayHelp ( clArgs userOption ) ;

   //* Genersal File Management Methods. Located in: CsvFile.cpp *
   short DeleteFile ( const char* fspec ) ;
   short CopyFile ( const char* srcSpec, const char* trgSpec ) ;
   short GetFileStats ( tnFName& tnFile, const gString& fPath ) ;
   bool  ExtractFilename ( gString& nameBuff, const gString& pfSource ) ;
   bool  ExtractFilename ( char nameBuff[MAX_FNAME], const gString& pfSource ) ;
   void  FormatDateString ( const localTime& ft, gString& str, bool canon = false ) ;
   fmFType DecodeFileType ( uint32_t mode ) ;
   void  DecodeEpochTime ( int64_t eTime, localTime& ftTime ) ;
   bool  ReadPermission ( tnFName& sStats, const char* sPath ) ;
   bool  WritePermission ( tnFName& sStats, const char* sPath ) ;
   bool  createCsvFile ( const char *srcSpec, EncType etype, bool crlf ) ;
   bool  captureCsvRecords ( const char *srcSpec, short indxA, short indxB ) ;
   void  dumpTermSize ( void ) ;
   void  dumpTermInfo ( bool clr ) ;
   void  dumpFileStats ( bool clr ) ;
   void  dumpHexRecord ( const uint8_t *buff, short cnt, const char *head, bool nl = true, 
                         short hlcnt = ZERO, const uint8_t *hlight = NULL ) ;

   //************************
   //* Private Data Members *
   //************************
   AnsiCmd* ansi ;               // pointer to AnsiCmd class instance
   TFMan*   tfm ;                // management of temporary files
   char     pwdPath[MAX_PATH] ;  // current working directory (target for saved files)
   char     orgPath[MAX_PATH] ;  // original source filespec
   char     srcPath[MAX_PATH] ;  // full filespec for UTF-8 _working copy_ of source file
   char     cpyPath[MAX_PATH] ;  // for 'saveas' option filespec of temp file
   char     trgPath[MAX_PATH] ;  // filespec for saving the re-encoded data
   tnFName  srcStats ;           // file stats for source file
   aeSeq    txtAttr ;            // attribute for basic application text and CSV file records
   aeSeq    hdrAttr ;            // attribute for display of CSV file heading line (if any)
   aeSeq    bgdAttr ;            // attribute for display background
   short    termRows, termCols ; // dimensions of terminal window

   //** Source File Data **
   short recCount ;              // number of records in file
   short fldCount ;              // number of fields per record
   EncType srcEncode ;           // type of character encoding for source file
   DispFmt dispfmt ;             // output format (member of enum DispFmt)
   UILang lang ;                 // user interface language (Note: Used as an index)
   bool  srcCsvFmt ;             // set if file format verified as CSV format
   bool  srcCrlf ;               // set if newlines are encoded as CR/LF pairs
   bool  srcHeader ;             // set if field header row is present
   bool  hdrLock ;               // set if user specified presence/absence of header record
   bool  srcScanned ;            // set if initial scan has been completed
   bool  trgExists ;             // set if specified output target already exists
   short maxRecWidth ;           // number of columns required for display of widest record
   short *maxFldWidth ;          // pointer to an array of values for maximum columns
                                 // used for each field
   short maxHdrWidth ;           // number of columns for widest field of header record


   // Note: Because TTY-style output extends the write to the end of each row, 
   //       using a non-default background attribute is not practical for the
   //       current application design; therefore, 'bgdAttr' is currently ignored.
} ;   // CsvView
