// This is core/vnl/io/vnl_io_sparse_matrix.hxx
#ifndef vnl_io_sparse_matrix_hxx_
#define vnl_io_sparse_matrix_hxx_
//:
// \file

#include <iostream>
#include "vnl_io_sparse_matrix.h"
#include <vnl/vnl_sparse_matrix.h>
#include <vsl/vsl_binary_io.h>
#include <cassert>
#ifdef _MSC_VER
#  include <vcl_msvc_warnings.h>
#endif

// I/O for vnl_sparse_matrix_pair
//==================================================================================
// IO Helper functions
//==================================================================================

//=================================================================================
//: Binary save self to stream.
template <class T>
void
vsl_b_write(vsl_b_ostream & os, const vnl_sparse_matrix_pair<T> & p)
{
  constexpr short io_version_no = 1;
  vsl_b_write(os, io_version_no);
  vsl_b_write(os, p.first);
  vsl_b_write(os, p.second);
}

//=================================================================================
//: Binary load self from stream.
template <class T>
void
vsl_b_read(vsl_b_istream & is, vnl_sparse_matrix_pair<T> & p)
{
  if (!is)
    return;

  short ver;
  vsl_b_read(is, ver);
  switch (ver)
  {
    case 1:
      vsl_b_read(is, p.first);
      vsl_b_read(is, p.second);
      break;

    default:
      std::cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vnl_sparse_matrix_pair<T>&)\n"
                << "           Unknown version number " << ver << '\n';
      is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
      return;
  }
}

//================================================================================
//: Output a human readable summary to the stream
template <class T>
void
vsl_print_summary(std::ostream & os, const vnl_sparse_matrix_pair<T> & p)
{
  os << "Sparse matrix pair ( " << p.first << ',' << p.second << " )\n";
}

// I/O for vnl_sparse_matrix

//=================================================================================
//: Binary save self to stream.
template <class T>
void
vsl_b_write(vsl_b_ostream & os, const vnl_sparse_matrix<T> & p)
{
  typedef vnl_sparse_matrix_pair<T> pair_t;
  typedef std::vector<pair_t> row;

  row rw;
  vnl_sparse_matrix<T> v = p;

  constexpr short io_version_no = 1;
  vsl_b_write(os, io_version_no);
  vsl_b_write(os, v.rows());
  vsl_b_write(os, v.columns());

  for (unsigned int i = 0; i < v.rows(); i++)
  {
    rw = v.get_row(i);
    vsl_b_write(os, rw.size());
    for (unsigned int j = 0; j < rw.size(); j++)
    {
      vsl_b_write(os, rw[j]);
    }
  }
}

//=================================================================================
//: Binary load self from stream.
template <class T>
void
vsl_b_read(vsl_b_istream & is, vnl_sparse_matrix<T> & p)
{
  if (!is)
    return;

  typedef vnl_sparse_matrix_pair<T> pair_t;

  short ver;
  unsigned n_rows;
  unsigned n_cols;
  unsigned row_size = 0;
  vsl_b_read(is, ver);

  std::vector<int> indexes(row_size);
  std::vector<T> values(row_size);
  switch (ver)
  {
    case 1:
      vsl_b_read(is, n_rows);
      vsl_b_read(is, n_cols);
      // As we cannot resize the matrix, check that it is the correct size.
      assert(n_rows == p.rows());
      assert(n_cols == p.columns());
      for (unsigned i = 0; i < n_rows; ++i)
      {
        vsl_b_read(is, row_size);
        indexes.resize(row_size);
        values.resize(row_size);

        for (unsigned j = 0; j < row_size; j++)
        {
          pair_t q;
          vsl_b_read(is, q);
          indexes[j] = q.first;
          values[j] = q.second;
        }
        p.set_row(i, indexes, values);
      }
      break;

    default:
      std::cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vnl_sparse_matrix<T>&)\n"
                << "           Unknown version number " << ver << '\n';
      is.is().clear(std::ios::badbit); // Set an unrecoverable IO error on stream
      return;
  }
}

//====================================================================================
//: Output a human readable summary to the stream
template <class T>
void
vsl_print_summary(std::ostream & os, const vnl_sparse_matrix<T> & p)
{
  os << "Rows x Columns: " << p.rows() << " x " << p.columns() << std::endl;
  vnl_sparse_matrix<T> v = p;
  v.reset();
  v.next();
  for (int i = 0; i < 5; i++)
  {
    os << " (" << v.getrow() << ',' << v.getcolumn() << ") value " << v.value() << '\n';
    if (!v.next())
      break;
  }
}

#define VNL_IO_SPARSE_MATRIX_INSTANTIATE(T)                                                 \
  template VNL_EXPORT void vsl_print_summary(std::ostream &, const vnl_sparse_matrix<T> &); \
  template VNL_EXPORT void vsl_b_read(vsl_b_istream &, vnl_sparse_matrix<T> &);             \
  template VNL_EXPORT void vsl_b_write(vsl_b_ostream &, const vnl_sparse_matrix<T> &)

#endif // vnl_io_sparse_matrix_hxx_
