// $Id: RwModelReport.cpp,v 1.17 2011/03/10 00:54:35 philw Exp $

// Configuration: Should generated Icon Pixmap files have a transparent
// background?  If not, they will be written with a white background.
static const bool WRITE_ICON_PIXMAPS_WITH_TRANSPARENT_BG (true);

#ifndef RwModelReportINCLUDED
#include "RwModelReport.hpp"
#endif

#ifndef RepGenSimObjHtmlINCLUDED
#include "RepGenSimObjHtml.hpp"
#endif

#ifndef RepGenSimObjTextINCLUDED
#include "RepGenSimObjText.hpp"
#endif

#ifndef RepGenUtilsINCLUDED
#include "RepGenUtils.hpp"
#endif

#include <QApplication>
#include <QClipboard>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFileDialog>
#include <QPainter>
#include <QProcess>
#include <QMessageBox>

#ifndef rwFileINCLUDED
#include "rwFile.hpp"
#endif

#ifndef SystemINCLUDED
#include "System.hpp"
#endif

#ifndef SimWorkspaceINCLUDED
#include "SimWS.hpp"
#endif

#ifndef SimObjINCLUDED
#include "SimObj.hpp"
#endif

#ifndef Date_TimeINCLUDED
#include "Date_Time.hpp"
#endif

#ifndef FileTypeAssocFormatINCLUDED
#include "FileTypeAssocFormat.hpp"
#endif

#ifndef FileTypeAssocMgrINCLUDED
#include "FileTypeAssocMgr.hpp"
#endif

#ifndef cwfstreamINCLUDED
#include "cwfstream.hpp"
#endif

#ifndef RwQPixmap16INCLUDED
#include "RwQPixmap16.hpp"   // Slot and Account Icons
#endif

#ifndef RwQtIconsINCLUDED
#include "RwQtIcons.hpp"     // SimObj Icons
#endif

#ifndef cwVersionINCLUDED
#include "cwVersion.hpp"
#endif

#ifndef rwErrorINCLUDED
#include "rwError.hpp"   //-- for rwAssert()
#endif

#include "rwStr.hpp"

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

// static
QString RwModelReport::_recentReportDir ("");

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

// constructor 1 of 1
RwModelReport::RwModelReport()
  : _reportPath (""),
    _simObj (NULL)
{
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

// destructor
RwModelReport::~RwModelReport()
{
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

QDir RwModelReport::reportDir() const
{
   const QFileInfo repFileInfo (_reportPath);

   if (repFileInfo.isDir())
   {
      return (QDir (_reportPath));
   }
      
   return (repFileInfo.absoluteDir()); 
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

bool RwModelReport::isHtmlReport() const
{
   const bool isHtml (!isTextReport());
   return (isHtml);
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

bool RwModelReport::isTextReport() const
{
   const bool isText = _reportPath.toUpper().endsWith ("TXT");
   return (isText);
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

bool RwModelReport::chooseReportPath()
{
   static const char (*mname) ("RwModelReport::chooseReportPath");

   static const QString caption ("Select Report Output File");

   static const QString filter ("HTML (*.htm *.html);;"
                                "TEXT (*.txt);;"
                                "All (*)");

   QString startDirStr = _recentReportDir;
   if (startDirStr.isEmpty())
   {
      const rwFile* modelRwFile = rwWorkspace->getModelFile();
      if (modelRwFile && !modelRwFile->Path().isEmpty())
      {
         const QString modelFilePathStr (modelRwFile->Path());
         const QFileInfo modelFileInfo (modelFilePathStr);
         startDirStr = modelFileInfo.absoluteDir().absolutePath();
      }
      else
      {
         startDirStr = QDir::currentDirPath();
      }
   }

   QString startPathStr (startDirStr); // tentative

   if (_simObj != NULL)
   {
      QDir startDir (startDirStr);
      const QString objName (_simObj->getCompleteName());
      const QString fileStr (RepGen::reduceNameString (objName) + ".html");
      startPathStr = startDir.absoluteFilePath (fileStr);
   }

   static QString selectedFilter ("");

   // ***************************
   // ***  Show File Chooser  ***
   // ***************************

   // NOTE [3-29-2009, Phil] -- Re: Confirming file overwrites.
   // With Qt 4.3.3 (and, I believe, with 4.4 and 4.5.0), there is no way to
   // make use of the QFileDialog::getSaveFileName static function's DEFAULT
   // behavior of asking the user whether or not the picked file, if already 
   // existing, should be overwritten.  It is a bug to allow that feature to 
   // remain enabled.  If the user requests that the picked file NOT be 
   // overwritten, it would be overwritten anyway.  I may be missing 
   // something, because I can't get confirmation of this problem on the 
   // QtCentre blog.  Apparenty, the check and query needs to be done by 
   // hand AFTER receiving the result from this QFileDialog method. SEE:
   // http://www.qtcentre.org/forum/showthread.php?t=19833&referrerid=6743

   // Note: This can be several options, OR'ed together.
   const QFileDialog::Options opts =
      QFileDialog::DontConfirmOverwrite;

   const QString userPickedPath =
      QFileDialog::getSaveFileName (NULL,  // parent widget
                                    caption,
                                    startPathStr,
                                    filter,
                                    &selectedFilter,
                                    opts);

   const bool userPickedEmpty (userPickedPath.isEmpty());

   // std::cout << mname << " Picked: '" << qPrintable (userPickedPath)
   //           << "' " << (userPickedEmpty ? "[EMPTY]" : "[Good to go]")
   //           << std::endl;

   if (userPickedEmpty)
   {
      // File selection canceled by user.
      return (false);
      //----------->>
   }

   setReportPath (userPickedPath);
   return (true);
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void RwModelReport::setSimObj (SimObj* obj)
{
   _simObj = obj;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void RwModelReport::generateReportFile()
{
   static const char (*mname) ("RwModelReport::generateReportFile");

   if (_simObj == NULL)
   {
      std::cout << mname << " NULL _simObj" << std::endl;
      return; //----->>
   }

   if (_reportPath.isEmpty())
   {
      const bool pathSelected = chooseReportPath();
      if (!pathSelected) return;
      //---------------------->>   
   }

   // std::cout << mname << " '" << qPrintable (_reportPath) << "'" 
   //           << std::endl;

   cwofstream os (_reportPath.ascii());

   const bool isText = isTextReport();
   if (isText)
   {
      generateTextReportFile (os);
   }
   else
   {
      generateHtmlReportFile (os);
   }

   os.close();

   // *****************************************
   // ***  Copy document path to clipboard  ***
   // *****************************************

   QClipboard *clipboard = QApplication::clipboard();
   clipboard->setText (_reportPath, QClipboard::Clipboard);
   if (clipboard->supportsSelection())
   {
      clipboard->setText (_reportPath, QClipboard::Clipboard);
   }

   // *************************
   // ***  Show in Browser  ***
   // *************************

   showInBrowser (true); // queryUserFirst
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void RwModelReport::showInBrowser (bool queryUserFirst)
{
   static const char (*mname) ("RwModelReport::showInBrowser");

   const FileTypeAssocMgr* mgr = FileTypeAssocMgr::instance();
   const FileTypeAssocFormat::Type format (FileTypeAssocFormat::Type_HTML);
   const QString progStr = mgr->viewModeExecutablePath (format);

   const QString docFullPathWithScheme = QString ("file://") + _reportPath;
   
   QStringList argList;
   argList << docFullPathWithScheme;

   if (queryUserFirst)
   {
      static const QString msgFmt (
       "Show generated report in web browser?\n\n"
       "  Browser: %1\n"
       "  Report File: %2" );

      const QString msg = msgFmt .arg (progStr) .arg (_reportPath);

      const QMessageBox::StandardButton userResp =
         QMessageBox::question (NULL, "Show in Browser?", msg, 
                                QMessageBox::Yes | QMessageBox::No,
                                QMessageBox::No); // default

      if (userResp != QMessageBox::Yes)
      {
         return;
         //======>>
      }
   }

   // Spin off the viewer as a separate process.
   QString workingDir ("");
   qint64 pid (0);
   const bool startProgramOk =
     QProcess::startDetached (progStr, argList, workingDir, &pid);
   startProgramOk;  // (avoid compilation warning)

   // std::cout << mname 
   //           << " prog='" << qPrintable (progStr) << "'"
   //           << " doc='" << qPrintable (docFullPathWithScheme) << "'"
   //           << " pid=" << (int64) pid
   //           << " START " << (startProgramOk ? "SUCCESS" : "FAILURE")
   //           << std::endl;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

// public
void RwModelReport::createReportIconFile (const QString& fileStr, 
                                          IconHandle iconHandle)
{
   static const char (*mname) ("RwModelReport::createReportIconFile");

   if (_writtenIconFiles.contains (iconHandle))
   {
      // An Icon Image File (PNG) was ALREADY WRITTEN for this 
      // IconHandle (during this instantiation of RwModelReport).
      return;  
      //------>>
   }

   // Just once during each RwModelReport instantiation,
   // write out the specified Icon QPixmap to the given file path.

   QPixmap iconPixmap = RwQPixmap16::findIconHandlePixmap (iconHandle);
   if (iconPixmap.isNull())
      iconPixmap = RwQtIcons::findIconHandlePixmap (iconHandle);

   const QDir repDir = reportDir();
   const QString pathStr = repDir.filePath (fileStr);

   if (iconPixmap.isNull())
   {
      // ERROR
      std::cout << mname << " (" << qPrintable (pathStr) << ")"
                << " (" << qPrintable(iconHandle.iconTagStr()) << ") NOT FOUND"
                << std::endl;
   }
   else // Non-Null QPixmap
   { 
      // ************************************
      // ***  Create images subdirectory  ***
      // ************************************

      // Note that this needs to be recomputed from the assembled pathStr, 
      // because the fileStr may start with a subdirectory.

      const QFileInfo pathInfo (pathStr);
      const QDir pathDir =
         pathInfo.isDir()? QDir (pathStr) : pathInfo.absoluteDir();

      if (!pathDir.exists())
      {
         const QString pathDirStr (pathDir.absolutePath());

         // std::cout << mname 
         //           << " mkpath: '" << qPrintable (pathDirStr) << "'"
         //           << std::endl;

         pathDir.mkpath (pathDirStr);
      }

      // ********************************
      // ***  Create Icon Image File  ***
      // ********************************

      // Make an "complete" copy of the Icon Pixmap. The background needs
      // to be filled with white or "transparent".
       
      QPixmap completeIconPixmap (iconPixmap.size());
      completeIconPixmap.fill (
        WRITE_ICON_PIXMAPS_WITH_TRANSPARENT_BG ? Qt::transparent : Qt::white);

      QPainter pmapPainter (&completeIconPixmap);
      pmapPainter.drawPixmap (0, 0, iconPixmap);
      (void) pmapPainter.end();

      static const int FullQuality (100);
      const bool saveSuccess = 
         completeIconPixmap.save (pathStr, "PNG", FullQuality);

      // std::cout << mname << " (" << qPrintable (pathStr) << ")"
      //           << " (" << iconHandle.iconTagStr() << ") "
      //           << (saveSuccess ? "SAVE OK" : "SAVE FAILED")
      //           << std::endl;

      if (saveSuccess)
      {
         _writtenIconFiles.append (iconHandle);
      }
   }
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

// private
void RwModelReport::setReportPath (const QString& newPath)
{
   _reportPath = newPath;

   // ********************************************
   // ***  Record Recently Accessed Directory  ***
   // ********************************************

   if (!newPath.isEmpty())
   {
      QFileInfo finfo (newPath);
      if (finfo.isDir())
      {
         _recentReportDir = finfo.absolutePath();;
      }
      else
      {
         QDir fdir (finfo.dir());
         _recentReportDir = fdir.absolutePath();
      }
   }
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void RwModelReport::generateHtmlReportFile (cwofstream& os)
{
   const QString titleStr = QString ("RiverWare SimObj Report: %1")
                            .arg (qPrintable(_simObj->getCompleteName()));

   os << "<html>\n"
      << "<head>\n"
      << " <title>" << qPrintable (titleStr) << "</title>\n"
      << "</head>\n"
      << "<body bgcolor=\"#FFFFFF\" text=\"#000000\">\n";

   Date_Time dt;
   const QString timeStr (dt.currentTime().ascii());

   const rwFile* modelRwFile = rwWorkspace->getModelFile();
   QString modelFilePathStr ("(Unsaved RiverWare Workspace)");
   if (modelRwFile && !modelRwFile->Path().isEmpty())
   {
      modelFilePathStr = qPrintable(modelRwFile->Path());
   }

   const QString rwVersInfo (cwAppVersion::instance().getVersionInfo());
   const QString versionMsg = 
           QString ("%1&nbsp; (http://cadswes.colorado.edu/)")
           .arg (qPrintable(rwVersInfo));

   os << "<p><font size=\"+1\">RiverWare Model Report Demo</font><br>\n"
      <<    "Model File:   " << qPrintable (modelFilePathStr) << "<br>\n"
      <<    "Report File:  " << qPrintable (_reportPath)      << "<br>\n"
      <<    "Generated by: " << qPrintable (versionMsg)       << "<br>\n"
      <<    "Generated at: " << timeStr                       << "</p>\n";

   RepGenSimObjHtml objRepGen (this, _simObj);
   objRepGen.writeObj (os);

   os << "<p>--- (end) ---</p>\n"
      << "</body>\n"
      << "</html>\n"
      << std::endl;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void RwModelReport::generateTextReportFile (cwofstream& os)
{
   Date_Time dt;
   const QString timeStr (dt.currentTime().ascii());

   const rwFile* modelRwFile = rwWorkspace->getModelFile();
   QString modelFilePathStr ("(Unsaved RiverWare Workspace)");
   if (modelRwFile && !modelRwFile->Path().isEmpty())
   {
      modelFilePathStr = qPrintable(modelRwFile->Path());
   }

   const QString rwVersInfo (cwAppVersion::instance().getVersionInfo());
   const QString versionMsg = 
           QString ("%1 (http://cadswes.colorado.edu/)")
           .arg (qPrintable(rwVersInfo));

   os << "RiverWare Model Report Demo\n"
      << "Model File:   " << qPrintable (modelFilePathStr) << "\n"
      << "Report File:  " << qPrintable (_reportPath)      << "\n"
      << "Generated by: " << qPrintable (versionMsg)       << "\n"
      << "Generated at: " << timeStr                       << "\n"
      << endl;

   RepGenSimObjText objRepGen (this, _simObj);
   objRepGen.writeObj (os);

   os << "--- (end) ---" << endl;
}

//--- (end RwModelReport.hpp) ---