/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
#ifndef _MGIfstream_HH_
#define _MGIfstream_HH_

#include <stdio.h>
#include <fstream>
#include <map>
#include "mg/Object.h"
#include "mg/Pvector.h"
#include "mg/Plist.h"

//forward declerations
class MGGel;
class MGGroup;
class MGInPtrMap;

/** @addtogroup FileInputOutput
 *  @{
 */

///MGIfstream is a class to read the serialized data generated by MGOfstream.

///All of the MGGel data serialized can be read by :
///(1) Construct: MGIfstream strm(file_name).
///(2) Then, MGGroup grp; strm>>grp.
///(3) The above grp holds all of the serialized data.
class MG_DLL_DECLR MGIfstream{
	
typedef std::map<long, MGGel*>::iterator mapitr;
typedef std::map<long, MGGel*>pmap;

public:

/// Target input data stream. m_file is a public member.
/// All of the CFile's member functions can be used.
	std::ifstream* m_file;
////////////////////////////////////////////////////

///Default constructor.
MGIfstream();

///Ordinal constructor, file name file is used to open the file.
MGIfstream(const TCHAR* file, bool map_clear=true);

///Destructor.
virtual ~MGIfstream();

////////////operator overloaded///////////////

///Read in all the objects in the file.
///The file should be generated by the following function:
///1. MGOfstream::make_file();
///2. MGOfstream& operator<< (const MGGel& gel);
///3. void operator<<(MGGroup& group);
void operator>>(MGGroup& group);

///Read in an object into gel. Returned gel is:
///(1) null if end of file or illegal file.
///(2) non null newed object pointer. User must delete it.
MGIfstream& operator>>(MGGel*& gel);

/// 基本型のファイル入力関数
MGIfstream& operator >>( char& ch){read1Byte(&ch);return *this;};
MGIfstream& operator >>( unsigned char& uch){read1Byte(&uch);return *this;};
MGIfstream& operator >>( short& s){read2Byte(&s);return *this;};
MGIfstream& operator >>( unsigned short& us){read2Byte(&us);return *this;};
MGIfstream& operator >>( int& n){read4Byte(&n);return *this;};
MGIfstream& operator >>( unsigned int& un){read4Byte(&un);return *this;};
MGIfstream& operator >>( long& l){read4Byte(&l);return *this;};
MGIfstream& operator >>( unsigned long& ul){read4Byte(&ul);return *this;};
MGIfstream& operator >>( float& f){read4Byte(&f);return *this;};
MGIfstream& operator >>( double& d){read8Byte(&d);return *this;};

/////////Member functions//////////

///Close the file. This can be used even open() was not used.
///Users need not use this close() if need not specify the file close
///before the destruction of the MGIfstream.
void close();

///Clear the map area m_map.
void mapClear();

///Find the input pid's map address.
///If found, MGGel* will be returned.
///If not found, null pointer(0) will be returned.
MGGel* find(long pid);

///Insert the ptr into the map.
///Function's return value is:
///True: if ptr did not exist in the map and insertion succeeded.
///False: if ptr did exist in the map and insertion failed.
bool insert(long pid, MGGel* ptr);

/// fileのオープン.
///Open the file. This is valid only when default constructor MGOfstream()
///is used.
///Function's return value is:
///=0: open succeeded.
///=1: file not found, or could not be opened.
///=2: file found, but, the format is not MGCL format.
int open(const TCHAR* file, bool map_clear=true);

/// Pointer base のオブジェクトファイル入力関数.
/// 戻り値はオブジェクトポインタ.
/// Null pointer(0)のときあり。
MGGel* ReadPointer();

///n char版.
MGIfstream& readnChar(char* ps4, int n);

///Set map_clear flag.
void set_map_clear(bool map_clear){m_map_clear=map_clear;};

/// 出力ポインタ位置関数.
///Tell the current input position of m_file(same as m_file.tellg).
long tellg();

///Obtain the version number of the stream.
const char* version()const{return m_version;};
char* version(){return m_version;};

private:

///Check if this is the right MGCL file.
///Function's return value is:
///true: if this is the right MGCL file.
///false: if this is not the right MGCL file.
bool MGCLHeader(std::ifstream&);

///Read in n bytes date in th buffer ps.
///This read data is row data, and the sequence will not be changed
///like read nByte.
void read(void* ps, int n);

///1Byte版.
MGIfstream& read1Byte(void* ps2);

///2Byte版.
MGIfstream& read2Byte(void* ps2);

///4Byte版.
MGIfstream& read4Byte(void* ps4);

///8Byte版.
MGIfstream& read8Byte(void* ps4);

private:

/// Object's pointer map.
MGInPtrMap* m_map;

///Input buffer's current position in MGIfstream by byte.
int m_position;

///Boolean to indicate whether map_clear() is necessary in operator>>(const MGGel&).
///When true, map_clear() will be invoked.
bool m_map_clear;

///Version number of reading stream.
char m_version[8];

};

///@cond
//Private Helper Class.
class MGInPtrMap{
public:
	std::map<long, MGGel*> pmap;
};
///@endcond

/** @} */ // end of FileInputOutput group

#endif	//#ifndef _MGIfstream_HH_