// Copyright (c) 2013-2020, SIB - Swiss Institute of Bioinformatics and
//                          Biozentrum - University of Basel
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#include <promod3/core/portable_binary_serializer.hh>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include <ost/base.hh>
#include <cstdio>
#include <fstream>

BOOST_AUTO_TEST_SUITE( core );

using namespace promod3::core;

enum MyEnum {
  ARG, ASN, ASP, 
  GLN, GLU, LYS, 
  SER, CYS, CYH, 
  CYD, MET, TRP, 
  TYR, THR, VAL, 
  ILE, LEU, PRO,
  CPR, TPR, HSD, 
  HSE, HIS, PHE, 
  GLY, ALA, XXX
};

struct MyStruct {
  // types used in binaries (as of Jan 21, 2016)
  char v_char;
  bool v_bool;
  int v_int;
  uint v_uint;
  short v_short;
  unsigned short v_ushort;
  uint32_t v_uint32_t;
  uint64_t v_uint64_t;
  MyEnum v_enum;
  Real v_Real;
  String v_str;
  // more for testing
  int32_t v_int32_t;
  double v_double;
  // will be stored less accurately
  int32_t i;
  uint32_t u;
  double d;

  void SetValues() {
    v_char = 1;
    v_bool = true;
    v_int = 1;
    v_uint = 1;
    v_short = 1;
    v_ushort = 1;
    v_uint32_t = 1;
    v_uint64_t = 1;
    v_enum = ASP;
    v_Real = 1;
    v_str = "HELLYEAH";
    v_int32_t = 2;
    v_double = 0.5;
    i = 1;
    u = 1;
    d = 1;
  }

  void CheckValues() {
    MyStruct s_ref;
    s_ref.SetValues();
    BOOST_CHECK_EQUAL(v_char, s_ref.v_char);
    BOOST_CHECK_EQUAL(v_bool, s_ref.v_bool);
    BOOST_CHECK_EQUAL(v_int, s_ref.v_int);
    BOOST_CHECK_EQUAL(v_uint, s_ref.v_uint);
    BOOST_CHECK_EQUAL(v_short, s_ref.v_short);
    BOOST_CHECK_EQUAL(v_ushort, s_ref.v_ushort);
    BOOST_CHECK_EQUAL(v_uint32_t, s_ref.v_uint32_t);
    BOOST_CHECK_EQUAL(v_uint64_t, s_ref.v_uint64_t);
    BOOST_CHECK_EQUAL(v_enum, s_ref.v_enum);
    BOOST_CHECK_EQUAL(v_Real, s_ref.v_Real);
    BOOST_CHECK_EQUAL(v_str, s_ref.v_str);
    BOOST_CHECK_EQUAL(v_int32_t, s_ref.v_int32_t);
    BOOST_CHECK_EQUAL(v_double, s_ref.v_double);
    BOOST_CHECK_EQUAL(i, s_ref.i);
    BOOST_CHECK_EQUAL(u, s_ref.u);
    BOOST_CHECK_EQUAL(d, s_ref.d);
  }

  template <typename DS>
  void Serialize(DS& ds) {
    // common use (reasonable sizes)
    ConvertBaseType<int8_t>(ds, v_char);
    ds & v_bool;
    ConvertBaseType<int32_t>(ds, v_int);
    ConvertBaseType<uint32_t>(ds, v_uint);
    ConvertBaseType<int16_t>(ds, v_short);
    ConvertBaseType<uint16_t>(ds, v_ushort);
    ConvertBaseType<uint32_t>(ds, v_uint32_t);
    ConvertBaseType<uint64_t>(ds, v_uint64_t);
    ConvertBaseType<int32_t>(ds, v_enum);
    ConvertBaseType<float>(ds, v_Real);
    ds & v_str;
    // store as is (still doing endianness)
    ds & v_int32_t;
    ds & v_double;
    // store some values with less precision
    ConvertBaseType<int8_t>(ds, i);
    ConvertBaseType<uint8_t>(ds, u);
    ConvertBaseType<float>(ds, d);
  }
};

BOOST_AUTO_TEST_CASE(test_check_portable_binary) {

  // set input struct and sanity check
  MyStruct s;
  s.SetValues();
  s.CheckValues();
  // store it
  String filename = "mytest.dat";
  std::ofstream out_stream(filename.c_str(), std::ios::binary);
  PortableBinaryDataSink sink(out_stream);
  sink & s;
  out_stream.close();
  // read it
  std::ifstream in_stream(filename.c_str(), std::ios::binary);
  PortableBinaryDataSource source(in_stream);
  MyStruct s_read;
  source & s_read;
  in_stream.close();
  // check
  s_read.CheckValues();

  // clean up
  std::remove(filename.c_str());
}

BOOST_AUTO_TEST_SUITE_END();
