//  This file is part of par2cmdline (a PAR 2.0 compatible file verification and
//  repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
//
//  Copyright (c) 2003 Peter Brian Clements
//  Copyright (c) 2019 Michael D. Nahas
//
//  par2cmdline is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  par2cmdline is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#ifndef __PAR2CREATOR_H__
#define __PAR2CREATOR_H__

#include "../parpar/gf16/controller_cpu.h"

class MainPacket;
class CreatorPacket;
class CriticalPacket;


class Par2Creator
{
public:
  Par2Creator(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel);
  ~Par2Creator(void);

  // Create recovery files from the source files specified on the command line
  Result Process(const size_t memorylimit,
		 const std::string &basepath,
		 const u32 nthreads,
		 const u32 filethreads,
		 const std::string &parfilename,
		 const std::vector<std::string> &extrafiles,
		 const u64 blocksize,
		 const u32 firstblock,
		 const Scheme recoveryfilescheme,
		 const u32 recoveryfilecount,
		 const u32 recoveryblockcount
		 );

protected:
  // Steps in the creation process:

  // Check permissions in the basepath
  bool CheckBasepath(const std::string &parfilename);

  // Compute block size from block count or vice versa depending on which was
  // specified on the command line
  bool ComputeBlockCount(const std::vector<std::string> &extrafiles);

  // Determine how much recovery data can be computed on one pass
  bool CalculateProcessBlockSize(size_t memorylimit);

  // Open all of the source files, compute the Hashes and CRC values, and store
  // the results in the file verification and file description packets.
  bool OpenSourceFiles(const std::vector<std::string> &extrafiles, std::string basepath);

  // Create the main packet and determine the set_id_hash to use with all packets
  bool CreateMainPacket(void);

  // Create the creator packet.
  bool CreateCreatorPacket(void);

  // Initialise all of the source blocks ready to start reading data from the source files.
  bool CreateSourceBlocks(void);

  // Create all of the output files and allocate all packets to appropriate file offsets.
  bool InitialiseOutputFiles(const std::string &par2filename);

  // Allocate memory buffers for reading and writing data to disk.
  bool AllocateBuffers(void);

  // Read source data, process it through the RS matrix and write it to disk.
  bool ProcessData(u64 blockoffset, size_t blocklength, ProgressMeter<u64> &progress);

  // Finish computation of the recovery packets and write the headers to disk.
  bool WriteRecoveryPacketHeaders(void);

  // Finish computing the full file hash values of the source files
  bool FinishFileHashComputation(void);

  // Fill in all remaining details in the critical packets.
  bool FinishCriticalPackets(void);

  // Write all other critical packets to disk.
  bool WriteCriticalPackets(void);

  // Close all files.
  bool CloseFiles(void);

  static u32                          GetFileThreads(void) {return filethreads;}

protected:
  std::ostream &sout; // stream for output (for commandline, this is cout)
  std::ostream &serr; // stream for errors (for commandline, this is cerr)
  std::mutex output_lock;

  const NoiseLevel noiselevel; // How noisy we should be

  static u32 filethreads;      // Number of threads for file processing

  u64 blocksize;      // The size of each block.
  size_t chunksize;   // How much of each block will be processed at a
                      // time (due to memory constraints).

  void *transferbuffer;  // chunksize * num_transfer_buffers

  u32 sourcefilecount;   // Number of source files for which recovery data will be computed.
  u32 sourceblockcount;  // Total number of data blocks that the source files will be
                         // virtually sliced into.

  u64 largestfilesize;   // The size of the largest source file

  Scheme recoveryfilescheme;  // What scheme will be used to select the
                                           // sizes for the recovery files.

  u32 recoveryfilecount;  // The number of recovery files that will be created
  u32 recoveryblockcount; // The number of recovery blocks that will be placed
                          // in the recovery files.

  u32 firstrecoveryblock; // The lowest exponent value to use for the recovery blocks.

  MainPacket    *mainpacket;    // The main packet
  CreatorPacket *creatorpacket; // The creator packet

  std::vector<Par2CreatorSourceFile*> sourcefiles;  // Array containing details of the source files
                                               // as well as the file verification and file
                                               // description packets for them.

  std::vector<DataBlock>          sourceblocks;     // Array with one entry for every source block.

  std::vector<DiskFile>           recoveryfiles;    // Array with one entry for every recovery file.
  std::vector<RecoveryPacket>     recoverypackets;  // Array with one entry for every recovery packet.

  std::list<CriticalPacket*>      criticalpackets;  // A list of all of the critical packets.
  std::list<CriticalPacketEntry>  criticalpacketentries; // A list of which critical packet will
                                                    // be written to which recovery file.

  PAR2Proc parpar;            // Main ParPar backend
  PAR2ProcCPU parparcpu;      // ParPar CPU sub-backend

  bool deferhashcomputation; // If we have enough memory to compute all recovery data
                             // in one pass, then we can defer the computation of
                             // the full file hash and block crc and hashes until
                             // the recovery data is computed.
};

#endif // __PAR2CREATOR_H__
