/* Copyright (c) 2015  Gerald Knizia
 * 
 * This file is part of the IboView program (see: http://www.iboview.org)
 * 
 * IboView 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, version 3.
 * 
 * IboView 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 details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
 * 
 * Please see IboView documentation in README.txt for:
 * -- A list of included external software and their licenses. The included
 *    external software's copyright is not touched by this agreement.
 * -- Notes on re-distribution and contributions to/further development of
 *    the IboView software
 */

#ifndef CT_IO_H
#define CT_IO_H

namespace ct {

extern char const
   *pResultFmt,
   *pResultFmtAnnotated,
   *pResultFmtI,
   *pResultFmtIAnnotated,
   *pTimingFmt,
   *pTimingPerFmt;

} // namespace ct


#include <string>
#include <stdexcept>
#include "CxPodArray.h"
#include "format.h" // brings in namespace "fmt", including its format function (which makes std::strings)

namespace ct {
   extern std::ostream
      &xerr, &xout;
   extern int
      Verbosity;
}

namespace ct {

// makes a string lower case. Will break with multibyte characters.
std::string tolower( std::string const &in );
// returns string 'in' with all spaces (but not tabs) removed.
std::string stripwhitespace( std::string const &in );

// makes a string to be output into the log as the first line of a major
// program component.
void MajorProgramIntro( std::ostream &out, const std::string &Name, const std::string &Version="" );

// will load an entire file into the stringstream str. Returns false if failed.
// if 0 != FileLength, *FileLength will receive the number of loaded chars.
bool LoadFileIntoMemory( TArray<char> &pFileContent,
        std::string const &FileName, unsigned *pFileLength = 0 );


// warning: these use std::isspace, which, among other things,
// treats newline as whitespace! And of course they are slow. Use
// for small stuff only (note: I might want to get rid of boost to
// simplify compiling, that's why these things are here).
void TrimLeft(std::string &s);
void TrimRight(std::string &s);
void Trim(std::string &s);
bool StartsWith(std::string const &s, std::string const &prefix);
bool StartsWith(std::string const &s, char const *prefix);
bool StartsWith(std::string const &s, char prefix);
void StripLineComment(std::string &s, char const *prefix);

class FLogError : public std::runtime_error
{
public:
   typedef std::runtime_error
      FBase;
   explicit FLogError(std::string const &Reason);
};


// a class encapsulating output streams and associated messaging.
//
// Main point about not simply using std::cout or some other global thing
// is that computations might be done on distributed machines or in some
// other non-local/non-sequential way.
class FLog
{
protected:
public:
   fmt::MemoryWriter
      w;
   typedef char
      Char;

   FLog() {};
   virtual ~FLog();

   void Write(fmt::BasicStringRef<Char> format) {
      w << format << '\n';
      Flush();
   }
   void WriteNoNl(fmt::BasicStringRef<Char> format) {
      w << format;
      Flush();
   }
   void Write(fmt::BasicStringRef<Char> format, fmt::ArgList args) {
      fmt::BasicFormatter<Char>(w).format(format, args);
      w << '\n';
      Flush();
   }
   void WriteNoNl(fmt::BasicStringRef<Char> format, fmt::ArgList args) {
      fmt::BasicFormatter<Char>(w).format(format, args);
      Flush();
   }
   FMT_VARIADIC_VOID(Write, fmt::BasicStringRef<Char>)

   virtual void Flush() = 0;

   virtual void EmitWarning(fmt::BasicStringRef<Char> Name);
   virtual void EmitError(fmt::BasicStringRef<Char> Name);
   void WriteResult(fmt::BasicStringRef<Char> Name, double fValue);
   void WriteResult(fmt::BasicStringRef<Char> Name, double fValue, fmt::BasicStringRef<Char> Annotation);
   void WriteCount(fmt::BasicStringRef<Char> Name, ptrdiff_t iValue);
   void WriteCount(fmt::BasicStringRef<Char> Name, ptrdiff_t iValue, fmt::BasicStringRef<Char> Annotation);
   void WriteTiming(fmt::BasicStringRef<Char> Name, double fTimeInSeconds);
   // print relative timing per task.
   void WriteTiming(fmt::BasicStringRef<Char> Name, double fTimeInSeconds, size_t nTasks);

   // this is our mechanism for communicating with the outside world (the other side of the log).
   // GetStatus may return something else than STATUS_Okay, for example, if an abort request was
   // placed on the other side by the user.
   // Default implementation does nothing (returns STATUS_Okay).
   enum FRunStatus {
      STATUS_Okay = 0,
      STATUS_AbortSignalled = 1
   };
   virtual FRunStatus GetStatus();
   // call GetStatus; throw FLogError if status says something differnt from STATUS_Okay.
   void CheckStatus();
   bool StatusOkay() { return GetStatus() == STATUS_Okay; };

   void WriteLine() { w << "\n"; };

   void WriteProgramIntro(fmt::BasicStringRef<Char> Name, fmt::BasicStringRef<Char> Version="");
private:
   void operator = (FLog const &); // not implemented
   FLog(FLog const &); // not implemented.
};


class FLogStdStream : public FLog
{
public:
   explicit FLogStdStream(std::ostream &TargetStream);
   void Flush(); // override
protected:
   std::ostream
      &m_TargetStream;
};


} // namespace ct.

#endif // CT_IO_H
