/* 
* 
* Copyright (C) 2013 UMPC
* 
* This file is part of CLaRiNet.
*  
* CLaRiNet 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.
* 
* CLaRiNet 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 CLaRiNet.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <string>
#include <algorithm>
#include "InOut.h"
#include "Erreur.h"

#define TYPE_CHAR 0
#define TYPE_UNSIGNED_CHAR 1
#define TYPE_SHORT 2
#define TYPE_UNSIGNED_SHORT 3
#define TYPE_INT 4
#define TYPE_UNSIGNED_INT 5
#define TYPE_FLOAT 6
#define TYPE_LONG 7
#define TYPE_UNSIGNED_LONG 8
#define TYPE_DOUBLE 9

using namespace std;

int SYSTEM_IS_BIG_ENDIAN(void)
{
    static unsigned long x(1);
    static int result(reinterpret_cast<unsigned char*>(&x)[0] != 0);
    return result;
}

int ReadFloat(ifstream & bin, int TYPE_SRC, bool SWITCH_ENDIANNESS, float * _f)
{
    char c;
    unsigned char uc;
    short int s;
    unsigned short int us;
    int i;
    unsigned int ui;
    float f;
    long int l;
    unsigned long int ul;
    double d;

    switch(TYPE_SRC)
    {
        case TYPE_CHAR:{
            bin.read( (char *) &c , sizeof(c) );
            *_f = (float)c;
            break;
        }
        case TYPE_UNSIGNED_CHAR:{
            bin.read( (char *) &uc , sizeof(uc) );
            *_f = (float)uc;
            break;
        }
        case TYPE_SHORT:{
            bin.read( (char *) &s , sizeof(s) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&s), pbyte(&s) + sizeof(s) );
            *_f = (float)s;
            break;
        }
        case TYPE_UNSIGNED_SHORT:{
            bin.read( (char *) &us , sizeof(us) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&us), pbyte(&us) + sizeof(us) );
            *_f = (float)us;
            break;
        }
        case TYPE_INT:{
            bin.read( (char *) &i , sizeof(i) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&i), pbyte(&i) + sizeof(i) );
            *_f = (float)i;
            break;
        }
        case TYPE_UNSIGNED_INT:{
            bin.read( (char *) &ui , sizeof(ui) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ui), pbyte(&ui) + sizeof(ui) );
            *_f = (float)ui;
            break;
        }
        case TYPE_FLOAT:{
            bin.read( (char *) &f , sizeof(f) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&f), pbyte(&f) + sizeof(f) );
            *_f = (float)f;
            break;
        }
        case TYPE_LONG:{
            bin.read( (char *) &l , sizeof(l) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&l), pbyte(&l) + sizeof(l) );
            *_f = (float)l;
            break;
        }
        case TYPE_UNSIGNED_LONG:{
            bin.read( (char *) &ul , sizeof(ul) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ul), pbyte(&ul) + sizeof(ul) );
            *_f = (float)ul;
            break;
        }
        case TYPE_DOUBLE:{
            bin.read( (char *) &d , sizeof(d) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&d), pbyte(&d) + sizeof(d) );
            *_f = (float)d;
            break;
        }
    }

    if(!(_f))
    {
        SetErreur("ReadFloat : could not read record\n");
        return(-1);
    }

    return(0);
}

int ReadUnsignedChar(ifstream & bin, int TYPE_SRC, bool SWITCH_ENDIANNESS, unsigned char * _uc)
{
    char c;
    unsigned char uc;
    short int s;
    unsigned short int us;
    int i;
    unsigned int ui;
    float f;
    long int l;
    unsigned long int ul;
    double d;

    switch(TYPE_SRC)
    {
        case TYPE_CHAR:{
            bin.read( (char *) &c , sizeof(c) );
            *_uc = (unsigned char)c;
            break;
        }
        case TYPE_UNSIGNED_CHAR:{
            bin.read( (char *) &uc , sizeof(uc) );
            *_uc = (unsigned char)uc;
            break;
        }
        case TYPE_SHORT:{
            bin.read( (char *) &s , sizeof(s) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&s), pbyte(&s) + sizeof(s) );
            *_uc = (unsigned char)s;
            break;
        }
        case TYPE_UNSIGNED_SHORT:{
            bin.read( (char *) &us , sizeof(us) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&us), pbyte(&us) + sizeof(us) );
            *_uc = (unsigned char)us;
            break;
        }
        case TYPE_INT:{
            bin.read( (char *) &i , sizeof(i) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&i), pbyte(&i) + sizeof(i) );
            *_uc = (unsigned char)i;
            break;
        }
        case TYPE_UNSIGNED_INT:{
            bin.read( (char *) &ui , sizeof(ui) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ui), pbyte(&ui) + sizeof(ui) );
            *_uc = (unsigned char)ui;
            break;
        }
        case TYPE_FLOAT:{
            bin.read( (char *) &f , sizeof(f) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&f), pbyte(&f) + sizeof(f) );
            *_uc = (unsigned char)f;
            break;
        }
        case TYPE_LONG:{
            bin.read( (char *) &l , sizeof(l) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&l), pbyte(&l) + sizeof(l) );
            *_uc = (unsigned char)l;
            break;
        }
        case TYPE_UNSIGNED_LONG:{
            bin.read( (char *) &ul , sizeof(ul) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ul), pbyte(&ul) + sizeof(ul) );
            *_uc = (unsigned char)ul;
            break;
        }
        case TYPE_DOUBLE:{
            bin.read( (char *) &d , sizeof(d) );
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&d), pbyte(&d) + sizeof(d) );
            *_uc = (unsigned char)d;
            break;
        }
    }

    if(!(_uc))
    {
        SetErreur("ReadUnsignedChar : could not read record\n");
        return(-1);
    }

    return(0);
}

int WriteFloat(ofstream & bin, int TYPE_DST, bool SWITCH_ENDIANNESS, float * _f)
{
    char c;
    unsigned char uc;
    short int s;
    unsigned short int us;
    int i;
    unsigned int ui;
    float f;
    long int l;
    unsigned long int ul;
    double d;

    switch(TYPE_DST)
    {
        case TYPE_CHAR:{
            c = (char)(*_f);
            bin.write( (char *) &c , sizeof(c) );
            break;
        }
        case TYPE_UNSIGNED_CHAR:{
            uc = (unsigned char)(*_f);
            bin.write( (char *) &uc , sizeof(uc) );
            break;
        }
        case TYPE_SHORT:{
            s = (short int)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&s), pbyte(&s) + sizeof(s) );
            bin.write( (char *) &s , sizeof(s) );
            break;
        }
        case TYPE_UNSIGNED_SHORT:{
            us = (unsigned short int)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&us), pbyte(&us) + sizeof(us) );
            bin.write( (char *) &us , sizeof(us) );
            break;
        }
        case TYPE_INT:{
            i = (int)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&i), pbyte(&i) + sizeof(i) );
            bin.write( (char *) &i , sizeof(i) );
            break;
        }
        case TYPE_UNSIGNED_INT:{
            ui = (unsigned int)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ui), pbyte(&ui) + sizeof(ui) );
            bin.write( (char *) &ui , sizeof(ui) );
            break;
        }
        case TYPE_FLOAT:{
            f = (float)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&f), pbyte(&f) + sizeof(f) );
            bin.write( (char *) &f , sizeof(f) );
            break;
        }
        case TYPE_LONG:{
            l = (long int)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&l), pbyte(&l) + sizeof(l) );
            bin.write( (char *) &l , sizeof(l) );
            break;
        }
        case TYPE_UNSIGNED_LONG:{
            ul = (unsigned long int)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ul), pbyte(&ul) + sizeof(ul) );
            bin.write( (char *) &ul , sizeof(ul) );
            break;
        }
        case TYPE_DOUBLE:{
            d = (double)(*_f);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&d), pbyte(&d) + sizeof(d) );
            bin.write( (char *) &d , sizeof(d) );
            break;
        }
    }

    return(0);
}

int WriteUnsignedChar(ofstream & bin, int TYPE_DST, bool SWITCH_ENDIANNESS, unsigned char * _uc)
{
    char c;
    unsigned char uc;
    short int s;
    unsigned short int us;
    int i;
    unsigned int ui;
    float f;
    long int l;
    unsigned long int ul;
    double d;

    switch(TYPE_DST)
    {
        case TYPE_CHAR:{
            c = (char)(*_uc);
            bin.write( (char *) &c , sizeof(c) );
            break;
        }
        case TYPE_UNSIGNED_CHAR:{
            uc = (unsigned char)(*_uc);
            bin.write( (char *) &uc , sizeof(uc) );
            break;
        }
        case TYPE_SHORT:{
            s = (short int)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&s), pbyte(&s) + sizeof(s) );
            bin.write( (char *) &s , sizeof(s) );
            break;
        }
        case TYPE_UNSIGNED_SHORT:{
            us = (unsigned short int)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&us), pbyte(&us) + sizeof(us) );
            bin.write( (char *) &us , sizeof(us) );
            break;
        }
        case TYPE_INT:{
            i = (int)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&i), pbyte(&i) + sizeof(i) );
            bin.write( (char *) &i , sizeof(i) );
            break;
        }
        case TYPE_UNSIGNED_INT:{
            ui = (unsigned int)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ui), pbyte(&ui) + sizeof(ui) );
            bin.write( (char *) &ui , sizeof(ui) );
            break;
        }
        case TYPE_FLOAT:{
            f = (float)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&f), pbyte(&f) + sizeof(f) );
            bin.write( (char *) &f , sizeof(f) );
            break;
        }
        case TYPE_LONG:{
            l = (long int)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&l), pbyte(&l) + sizeof(l) );
            bin.write( (char *) &l , sizeof(l) );
            break;
        }
        case TYPE_UNSIGNED_LONG:{
            ul = (unsigned long int)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&ul), pbyte(&ul) + sizeof(ul) );
            bin.write( (char *) &ul , sizeof(ul) );
            break;
        }
        case TYPE_DOUBLE:{
            d = (double)(*_uc);
            if (SWITCH_ENDIANNESS)
                std::reverse( pbyte(&d), pbyte(&d) + sizeof(d) );
            bin.write( (char *) &d , sizeof(d) );
            break;
        }
    }

    return(0);
}
