/*

Copyright (C) 2016-2018 Olaf Till <i7tiol@t-online.de

This program 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 3 of the License, or (at your option) any later
version.

This program 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, see <http://www.gnu.org/licenses/>.

*/

#include <octave/oct.h>

#include <octave/load-save.h>

#include <octave/byte-swap.h>

#include <octave/ls-mat4.h>

#include "config.h"

#include "error-helpers.h"

int minimal_read_header (std::istream& is, bool& swap,
                         OCTAVE__MACH_INFO::float_format& flt_fmt)
{
  /*
    slightly changed from load-save.cc
    (read_binary_file_header()) to reduce overhead
  */

  const int magic_len = 2;
  char magic[magic_len+1];
  is.read (magic, magic_len);
  magic[magic_len] = '\0';

  if (strncmp (magic, "1L", magic_len) == 0)
    swap = OCTAVE__MACH_INFO::words_big_endian ();
  else if (strncmp (magic, "1B", magic_len) == 0)
    swap = ! OCTAVE__MACH_INFO::words_big_endian ();
  else
    {
      _p_error ("could not read binary header");

      return -1;
    }

  char tmp = 0;
  is.read (&tmp, 1);

  flt_fmt = mopt_digit_to_float_format (tmp);

  if (flt_fmt == OCTAVE__MACH_INFO::flt_fmt_unknown)
    {
      _p_error ("unrecognized binary format");

      return -1;
    }

  return 0;
}

int minimal_read_data (std::istream& is, octave_value& val,
                       bool swap, OCTAVE__MACH_INFO::float_format flt_fmt)
{
  int32_t len;
  if (! is.read (reinterpret_cast<char *> (&len), 4))
    {
      _p_error ("could not load variable");

      return -1;
    }

  if (swap)
    swap_bytes<4> (&len);

  {
    OCTAVE_LOCAL_BUFFER (char, buf, len+1);
    if (! is.read (buf, len))
      {
        _p_error ("could not load variable");

        return -1;
      }
    buf[len] = '\0';
    std::string typ (buf);
    val = octave_value_typeinfo::lookup_type (typ);
  }

  if (! val.load_binary (is, swap, flt_fmt))
    {
      _p_error ("could not load variable");

      return -1;
    }

  return 0;
}

void minimal_write_header (std::ostream& os)
{
  /*
    slightly changed from load-save.cc (write_header(,LS_BINARY)) to
    reduce overhead
  */

  os << (OCTAVE__MACH_INFO::words_big_endian () ? "1B" : "1L");

  OCTAVE__MACH_INFO::float_format flt_fmt =
    OCTAVE__MACH_INFO::native_float_format ();

  char tmp = static_cast<char> (float_format_to_mopt_digit (flt_fmt));

  os.write (&tmp, 1);
}

int minimal_write_data (std::ostream& os, octave_value& val)
{
  /*
    Much here is cut-and-pasted from ls-oct-binary.cc
    (save_binary_data()) in Octave.
  */

  // Write the string corresponding to the octave_value type.
  std::string typ = val.type_name ();
  int32_t len = typ.length ();
  os.write (reinterpret_cast<char *> (&len), 4);
  const char *btmp = typ.data ();
  os.write (btmp, len);

  // Call specific save function
  bool save_as_floats = false;
  if (! val.save_binary (os, save_as_floats) || ! os)
    {
      _p_error ("could not save variable.");

      return -1;
    }

  return 0;
}
