/*
 * (C) Copyright 1996- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include <cstdlib>
#include <cstdio>
#include <sys/types.h>

#include "eckit/serialisation/FileStream.h"
#include "eckit/io/AutoCloser.h"

#include "eckit/testing/Test.h"

using namespace std;
using namespace eckit;
using namespace eckit::testing;

namespace eckit {
namespace test {

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

namespace {

const char               i_char('a');
const unsigned char      i_uchar('b');
const bool               i_bool(true);
const int                i_int(-20000000);
const unsigned int       i_uint(30000000);
const short              i_short(-4000);
const unsigned short     i_ushort(5000);
const long               i_long(-60000000);
const unsigned long      i_ulong(70000000);
const long long          i_longlong(-80000000);
const unsigned long long i_ulonglong(90000000);
//const float              i_float(300000);
const double             i_double(100000000);
const string             i_string("abcdefghijklmnopqrstuvwx");
const char*              i_charp("cdefghijklmnopqrstuvwxyz");

char               v_char;
unsigned char      v_uchar;
bool               v_bool;
int                v_int;
unsigned int       v_uint;
short              v_short;
unsigned short     v_ushort;
long               v_long;
unsigned long      v_ulong;
long long          v_longlong;
unsigned long long v_ulonglong;
//float              v_float;
double             v_double;
string             v_string;
string             v_charp;

}  // anonymous namespace

struct F {
    ~F()
    {
        if (filename.exists()) filename.unlink();
    }

    static PathName filename;
};

PathName F::filename = PathName::unique( "data" );

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

CASE ( "write_data" )
{
    FileStream sout( F::filename, "w" ); auto c = closer(sout);
    sout << i_char
        << i_uchar
        << i_bool
        << i_int
        << i_uint
        << i_short
        << i_ushort
        << i_long
        << i_ulong
        << i_longlong
        << i_ulonglong
//         << i_float
        << i_double
        << i_string
        << i_charp
    ;
}

CASE( "read_data" )
{
    FileStream sin( F::filename, "r" ); auto c = closer(sin);
    sin
        >> v_char
        >> v_uchar
        >> v_bool
        >> v_int
        >> v_uint
        >> v_short
        >> v_ushort
        >> v_long
        >> v_ulong
        >> v_longlong
        >> v_ulonglong
    //  >> v_float
        >> v_double
        >> v_string
        >> v_charp
    ;
}

CASE( "check_data" )
{
    EXPECT( v_char      ==      i_char );
    EXPECT( v_uchar     ==     i_uchar );
    EXPECT( v_bool      ==      i_bool );
    EXPECT( v_int       ==       i_int );
    EXPECT( v_uint      ==      i_uint );
    EXPECT( v_short     ==     i_short );
    EXPECT( v_ushort    ==    i_ushort );
    EXPECT( v_long      ==      i_long );
    EXPECT( v_ulong     ==     i_ulong );
    EXPECT( v_longlong  ==  i_longlong );
    EXPECT( v_ulonglong == i_ulonglong );
//  EXPECT( v_float     ==     i_float );
    EXPECT( v_double    ==    i_double );
    EXPECT( v_string    ==    i_string );
    EXPECT( v_charp     ==     i_charp );
}

CASE ( "stream_object" ) {
    Log::info()  << "Stream an object" << std::endl;
    const std::string k("key");
    const std::string v("value");
    {
        FileStream sout( F::filename, "w" ); auto c = closer(sout);
        sout.startObject();
        sout << k;
        sout << v;
        sout.endObject();
    }
    {
        FileStream sin( F::filename, "r" ); auto c = closer(sin);
        EXPECT( sin.next() );
        std::string s;
        sin >> s;
        EXPECT( s == k );
        sin >> s;
        EXPECT( s == v );
        EXPECT( sin.endObjectFound() );
        EXPECT( !sin.next() );
    }
}

CASE( "stream_string" ) {
    Log::info() << "Stream a string" << std::endl;
    {
        FileStream sout( F::filename, "w" ); auto c = closer(sout);
        sout << i_string;
    }
    {
        FileStream sin( F::filename, "r" ); auto c = closer(sin);
        std::string s;
        EXPECT( sin.next(s) );
        EXPECT( s == i_string );
        EXPECT( !sin.next() );
    }
}

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

} // namespace test
} // namespace eckit

int main(int argc,char **argv)
{
    return run_tests ( argc, argv );
}
