// SPDX-FileComment: This file is part of TNL - Template Numerical Library (https://tnl-project.org/)
// SPDX-License-Identifier: MIT

#pragma once

#include <iomanip>
#include <fstream>

#include <TNL/Matrices/MatrixWriter.h>

namespace TNL::Matrices {

template< typename Matrix, typename Device >
void
MatrixWriter< Matrix, Device >::writeGnuplot( const std::string& fileName, const Matrix& matrix, bool verbose )
{
   HostMatrix hostMatrix;
   hostMatrix = matrix;
   MatrixWriter< HostMatrix >::writeGnuplot( fileName, hostMatrix, verbose );
}

template< typename Matrix, typename Device >
void
MatrixWriter< Matrix, Device >::writeGnuplot( std::ostream& str, const Matrix& matrix, bool verbose )
{
   HostMatrix hostMatrix;
   hostMatrix = matrix;
   MatrixWriter< HostMatrix >::writeGnuplot( str, hostMatrix, verbose );
}

template< typename Matrix, typename Device >
void
MatrixWriter< Matrix, Device >::writeMtx( const std::string& fileName, const Matrix& matrix, bool verbose )
{
   HostMatrix hostMatrix;
   hostMatrix = matrix;
   MatrixWriter< HostMatrix >::writeMtx( fileName, hostMatrix, verbose );
}

template< typename Matrix, typename Device >
void
MatrixWriter< Matrix, Device >::writeMtx( std::ostream& str, const Matrix& matrix, bool verbose )
{
   HostMatrix hostMatrix;
   hostMatrix = matrix;
   MatrixWriter< HostMatrix >::writeMtx( str, hostMatrix, verbose );
}

template< typename Matrix, typename Device >
void
MatrixWriter< Matrix, Device >::writeEps( const std::string& fileName, const Matrix& matrix, bool verbose )
{
   HostMatrix hostMatrix;
   hostMatrix = matrix;
   MatrixWriter< HostMatrix >::writeEps( fileName, hostMatrix, verbose );
}

template< typename Matrix, typename Device >
void
MatrixWriter< Matrix, Device >::writeEps( std::ostream& str, const Matrix& matrix, bool verbose )
{
   HostMatrix hostMatrix;
   hostMatrix = matrix;
   MatrixWriter< HostMatrix >::writeEps( str, hostMatrix, verbose );
}

// MatrixWriter specialization for TNL::Devices::Host.

// This is to prevent Doxygen warnings due to hidden class.
/// \cond
template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeGnuplot( const std::string& fileName, const Matrix& matrix, bool verbose )
{
   std::fstream str;
   str.open( fileName, std::ios::out );
   MatrixWriter< Matrix >::writeGnuplot( str, matrix, verbose );
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeGnuplot( std::ostream& str, const Matrix& matrix, bool verbose )
{
   str << "#  This file was generated by TNL (www.tnl-project.org)\n";
   for( IndexType row = 0; row < matrix.getRows(); row++ ) {
      for( IndexType column = 0; column < matrix.getColumns(); column++ ) {
         RealType elementValue = matrix.getElement( row, column );
         if( elementValue != (RealType) 0.0 )
            str << column << " " << row << " " << elementValue << "\n";
      }
      if( verbose )
         std::cout << "Drawing the row " << row << "      \r" << std::flush;
   }
   if( verbose )
      std::cout << '\n';
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeMtx( const std::string& fileName, const Matrix& matrix, bool verbose )
{
   std::fstream str;
   str.open( fileName, std::ios::out );
   MatrixWriter< Matrix >::writeMtx( str, matrix, verbose );
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeMtx( std::ostream& str, const Matrix& matrix, bool verbose )
{
   str << "%%MatrixMarket matrix coordinate real general\n";
   str << "%%\n";
   str << "%% This file was generated by TNL (www.tnl-project.org)\n";
   str << "%%" << std::setw( 9 ) << " ROWS " << std::setw( 9 ) << " COLUMNS " << std::setw( 12 ) << " ELEMENTS \n";
   str << std::setw( 9 ) << matrix.getRows() << " " << std::setw( 9 ) << matrix.getColumns() << " " << std::setw( 12 )
       << matrix.getNonzeroElementsCount() << '\n';
   for( IndexType rowIdx = 0; rowIdx < matrix.getRows(); rowIdx++ ) {
      const auto row = matrix.getRow( rowIdx );
      for( IndexType localIdx = 0; localIdx < row.getSize(); localIdx++ ) {
         IndexType columnIdx = row.getColumnIndex( localIdx );
         RealType value = row.getValue( localIdx );
         if( value != 0 ) {
            str << std::setw( 9 ) << rowIdx + 1 << std::setw( 9 ) << columnIdx + 1 << std::setw( 12 ) << value << '\n';
            if( verbose )
               std::cout << "Drawing the row " << rowIdx << "      \r" << std::flush;
         }
      }
   }
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeEps( const std::string& fileName, const Matrix& matrix, bool verbose )
{
   std::fstream str;
   str.open( fileName, std::ios::out );
   MatrixWriter< Matrix >::writeEps( str, matrix, verbose );
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeEps( std::ostream& str, const Matrix& matrix, bool verbose )
{
   const int elementSize = 10;
   writeEpsHeader( str, matrix, elementSize );
   writeEpsBody( str, matrix, elementSize, verbose );

   str << "showpage\n";
   str << "%%EOF\n";

   if( verbose )
      std::cout << '\n';
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeEpsHeader( std::ostream& str, const Matrix& matrix, const int elementSize )
{
   const double scale = elementSize * max( matrix.getRows(), matrix.getColumns() );
   str << "%!PS-Adobe-2.0 EPSF-2.0\n";
   str << "%%BoundingBox: 0 0 " << scale << " " << scale << '\n';
   str << "%%Creator: TNL\n";
   str << "%%LanguageLevel: 2\n";
   str << "%%EndComments\n\n";
   str << "0 " << scale << " translate\n";
}

template< typename Matrix >
void
MatrixWriter< Matrix, TNL::Devices::Host >::writeEpsBody(
   std::ostream& str,
   const Matrix& matrix,
   const int elementSize,
   bool verbose )
{
   IndexType lastRow = 0;
   IndexType lastColumn = 0;
   for( IndexType row = 0; row < matrix.getRows(); row++ ) {
      for( IndexType column = 0; column < matrix.getColumns(); column++ ) {
         RealType elementValue = matrix.getElement( row, column );
         if( elementValue != (RealType) 0.0 ) {
            str << ( column - lastColumn ) * elementSize << " " << -( row - lastRow ) * elementSize << " translate newpath 0 0 "
                << elementSize << " " << elementSize << " rectstroke\n";
            lastColumn = column;
            lastRow = row;
         }
      }
      if( verbose )
         std::cout << "Drawing the row " << row << "      \r" << std::flush;
   }
}
/// \endcond

}  // namespace TNL::Matrices
