#include "bob_dom_cut.h"
#include "../common/indian.h"

static int g_i;
#define BOB_WRITE(fd, i) (g_i=be2le((unsigned int)i), write(fd, &g_i, sizeof(i)))

#define max(a, b) (a > b ? a : b)
//---------------------------------------------------------------------------------
bool bob_dom_name::toFile(bob_dom_otextfilestream& os)
{
	if(m_text && *m_text!=0) {
		int oldf=os.flags();
		os << noSemicolons << "N " << autoSemicolons << m_text;
		os.flags(oldf);
	}
	return os.good();
}
//---------------------------------------------------------------------------------
// STAT
#ifdef X2BC_USE_INI_FORMATS
bool bob_dom_stat::load(bob_dom_ibinaryfilestream& is, const Settings *settings)
{
	is >> frameId;
	m_format=settings->findStatFormat(frameId);
	if(m_format==NULL){
		error(s_error, e_format_noStatFormat, "Cannot find STAT format 0x%X", frameId);
		setstate(bob_dom_stat::noFormat);
	}
	else{
		int v;
		for(Settings::StatFormat::const_iterator &it=m_format->tokens.begin(); it!=m_format->tokens.end(); ++it){
			for(int i=0; i < it->count; i++){
				is >> v;
				push_back(v);
			}
			if(is.fail()){
				error(s_error, e_format_notEnoughData, "Not enough data to match format 0x%X", frameId);
				setstate(cannotMatchFormat);
				break;
			}
		}
	}
	return rdstate()==formatted;
}
//---------------------------------------------------------------------------------
bool bob_dom_stat::toFile(bob_dom_obinaryfilestream& os)
{
	os << frameId;
	for(iterator &it=begin(); it!=end(); ++it){
		os << *it;
	}
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_stat::toFile(bob_dom_otextfilestream& os, const Settings *settings)
{
	os << noSemicolons;
	if(rdstate()!=formatted && settings->outputWarnings()){
		os << "// Error: STAT frame id=" << hex << "0x" << frameId << dec;
		if(rdstate()==noFormat)	
			os << ": cannot find frame format";
		else
			os << ": unable to match frame format";
	}
	else{
		if(settings->statFormatWarnings() && settings->outputWarnings() && m_format->issueWarning)
			os << "// Warning: Format 0x" << hex << m_format->id << " was used and it's marked with warning sign" << endl;
	
		double val;
		os << "{ ";
		
		os << hex << "0x" << frameId << "; " << dec;
		
		iterator &valit=begin();
		
		for(Settings::StatFormat::const_iterator &it=m_format->tokens.begin(); it!=m_format->tokens.end(); ++it){
			for(int i=0; i < it->count; i++){
				val=*valit;
				if(settings->convert())
					val*=it->numFormat->multiplier;
				if(settings->convert() && it->numFormat->outformat==Settings::NumberFormat::Float)
					os << (float)val;
				else
					os << (int)val;
				
				os << ';';
					
				++valit;
			}
			if(it->data)
				os << it->data;
		}
		
		os << noSemicolons << " }";
	}
	return os.good();
}
//---------------------------------------------------------------------------------
#endif // defined(X2BC_USE_INI_FORMATS)

// PATH
bool bob_dom_path::load(bob_dom_ibinaryfilestream& is, int version)
{
	int hdr;
	is >> hdr;
	if(hdr!=hdr_begin){
		error(e_badHeader);
		return false;
	}
	is >> partIdx;
	if(version < 6) {
		int bid;
		is >> bid;
		m_bodyId=new char[20];
		itoa(bid, m_bodyId, 10);
	}
	else 
		is >> m_bodyId;
		
	is >> cockpitIdx >> parentIdx >> bodyFlags;
	
	if(peek(is)==bob_dom_name::hdr_begin){
		if(name.load(is)==false){
			for(ErrorIterator &it=name.errors.begin(); it!=name.errors.end(); ++it){
				error(it->code, "name: %s", it->text);
			}
			return false;
		}
	}

	if(peek(is)==bob_dom_constants::hdr_begin){
		if(constants.load(is)==false){
			for(ErrorIterator &it=name.errors.begin(); it!=name.errors.end(); ++it){
				error(it->code, "constants: %s", it->text);
			}
			return false;
		}
	}
	
	if(peek(is)==bob_dom_bob::hdr_begin){
		bob=new bob_dom_bob(m_settings);
		if(bob->load(is)==false){
			for(ErrorIterator &it=bob->errors.begin(); it!=bob->errors.end(); ++it){
				error(it->code, "bob->%s", it->text);
			}
			return false;
		}
	}
	int noteLineCount;
	is >> noteLineCount;
	if(noteLineCount > 0){
		if(m_notes.load(is, noteLineCount)==false){
			for(ErrorIterator &it=m_notes.errors.begin(); it!=m_notes.errors.end(); ++it){
				error(it->code, "notes->%s", it->text);
			}
			return false;
		}
	}
	
	int statCount;
	is >> statCount;
	if(loadStatValues(is, statCount)==false) return false;
	
	is >> hdr;
	if(is.fail())
		error(e_notEnoughData);
	else if(hdr!=hdr_end)
		error(e_badEndHeader);
	
	/////////////////
	//if(hdr==hdr_end) formatStats();
	////////////////
	return hdr==hdr_end && !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_dom_path::loadStatValues(bob_dom_ibinaryfilestream& is, int count)
{
	int hdr;
	is >> hdr;
	if(hdr!=hdr_stat_begin){
		error(e_badHeader, "Expected STAT header.");
		return false;
	}
	
	child_type *ch;
	bool bRes;
	
	for(int i=0; i < count; i++){
		ch=createChild();
#ifdef X2BC_USE_INI_FORMATS
		bRes=ch->load(is, m_settings);
#else
		bRes=ch->load(is);
#endif
		for(ErrorIterator &it=ch->errors.begin(); it!=ch->errors.end(); ++it){
			error(it->severity, it->code, "stat->frame[%d]: %s", i, it->text);
		}
		if(bRes==false) break;
	}
	
	is >> hdr;
	if(hdr!=hdr_stat_end){
		error(s_error, e_moreData, "More data in STAT section than expected - skipping");
		do{
			is >> hdr;
		}
		while(hdr!=hdr_stat_end && is.good());
	}
	
	if(hdr!=hdr_stat_end)
		error(e_notEnoughData);
	return hdr==hdr_stat_end;
}
//---------------------------------------------------------------------------------
bool bob_dom_path::toFile(bob_dom_obinaryfilestream& os, int cutVersion)
{
	os << hdr_begin;
	os << partIdx;
	if(cutVersion >= 6)
		os << bodyId();
	else
		os << atoi(bodyId());
	
	os << cockpitIdx;
	os << parentIdx;
	os << bodyFlags;
	os << name;
	constants.toFile(os);
	if(bob) bob->toFile(os);
	os << (int)m_notes.size();
	m_notes.toFile(os);
	os << (int)size();
	
	os << hdr_stat_begin;
	for(iterator &it=begin(); it!=end(); ++it){
		it->toFile(os);
	}
	os << hdr_stat_end << hdr_end;
	return true;
}
//---------------------------------------------------------------------------------
bool bob_dom_path::toFile(bob_dom_otextfilestream& os, int idx)
{
	os << noSemicolons << "P " << partIdx << "; B " << bodyId() << "; ";
	if(parentIdx!=-1)
		os << "F " << parentIdx << "; ";
	
	if(cockpitIdx > 0)
		os << "C " << cockpitIdx << "; ";
		
	os << name;
	
	constants.toFile(os);

	/*
		we've got old x2 flags and new x3 flags
		these 2 can be mixed together
		
		for example body || light
		
		only the body (64) and scene (128) can be mixed with anything else imho
	*/
	if(bodyFlags!=0) {
		if(bodyFlags & BodyFlags::fBody)
			os << "b ";
		if(bodyFlags & BodyFlags::fScene)
			os << "j ";
		int i=bodyFlags & 0x3F;
		if(i) 
			os << (char) ('b' + i) << ' ';
	}
	
	os << "// idx " << idx << ", flags: " << bodyFlags << endl;
	
	if(m_notes.size())
		m_notes.toFile(os);
	
	if(bob){
		os << noSemicolons << "L { // beginning of embedded BOB" << endl;
		os << *bob << "} // end of embedded BOB" << endl << endl;
	}
	
	int i=0;
	for(iterator &it=begin(); it!=end(); ++it, ++i){
#ifdef X2BC_USE_INI_FORMATS
		it->toFile(os, m_settings);
#else
		it->toFile(os);
#endif
		os << " // " << i << endl;
	}
	os << endl;
	
	return os.good();
}
//---------------------------------------------------------------------------------
// CUT file
bool bob_dom_cut::load(bob_dom_ibinaryfilestream& is)
{
	int hdr;
	is >> hdr;
	if(hdr!=hdr_begin){
		error(e_badHeader);
		return false;
	}
	
	if(peek(is)==bob_dom_info::hdr_begin){
		if(!info.load(is)){
			for(ErrorIterator &it=info.errors.begin(); it!=info.errors.end(); ++it){
				error(it->code, "info: %s", it->text);
			}
			return false;
		}
	}
	
	is >> version;
	
	if(version!=supported_version)
		error(s_warning, e_badVersion, "Unsupported CUT1 version, loading might fail");
	
	is >> m_storedPathCount;
	
	child_type *ch;
	for(int i=0; i < m_storedPathCount; i++){
		ch=createChild();
		bool bRes=ch->load(is, version);
		for(ErrorIterator &it=ch->errors.begin(); it!=ch->errors.end(); ++it){
			error(it->code, "path[%d]->%s", i, it->text);
		}
		if(bRes==false) return false;
	}
	
	is >> hdr;
	if(hdr!=hdr_end)
		error(e_badEndHeader);
	return hdr==hdr_end;
}
//---------------------------------------------------------------------------------
bool bob_dom_cut::convert(bob_dom_ibinaryfilestream& is, bob_dom_otextfilestream& os)
{
	int hdr;
	is >> hdr;
	if(hdr!=hdr_begin){
		error(e_badHeader);
		return false;
	}
	
	if(peek(is)==bob_dom_info::hdr_begin){
		if(!info.load(is)){
			for(ErrorIterator &it=info.errors.begin(); it!=info.errors.end(); ++it){
				error(it->code, "info: %s", it->text);
			}
			return false;
		}
	}
	
	is >> version;
	
	os << info << endl << "VER: " << autoSemicolons << version << endl << endl;
	
	is >> m_storedPathCount;
	
	child_type *ch;
	for(int i=0; i < m_storedPathCount; i++){
		ch=new bob_dom_path(m_settings);
		bool bRes=ch->load(is, version);
			
		for(ErrorIterator &it=ch->errors.begin(); it!=ch->errors.end(); ++it){
			error(it->code, "path[%d]->%s", i, it->text);
		}
		
		if(bRes)
			ch->toFile(os, i);
			
		delete ch;
		if(bRes==false) return false;
	}
	
	is >> hdr;
	if(hdr!=hdr_end)
		error(e_badEndHeader);
	return hdr==hdr_end;
}
//---------------------------------------------------------------------------------
bool bob_dom_cut::toFile(bob_dom_obinaryfilestream& os)
{
	os << hdr_begin << info << version << (int)size();

	for(iterator &it=begin(); it!=end(); ++it){
		it->toFile(os, version);
	}
	os << hdr_end;
	
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_cut::toFile(bob_dom_otextfilestream& os)
{
	os << noSemicolons << info << endl;
	os << "VER: " << autoSemicolons << version << endl << endl;
	
	int idx=0;
	for(iterator &it=begin(); it!=end(); ++it){
		it->toFile(os, idx++);
		os << endl;
	}
	
	return os.good();
}
//---------------------------------------------------------------------------------
// NOTES - section NOTE
bool bob_dom_notes::load(bob_dom_ibinaryfilestream& is, int lineCount)
{
	int hdr;
	
	is >> hdr;
	if(hdr!=hdr_begin){
		error(e_badHeader);
		return false;
	}
	child_type *ch;
	for(int i=0; i < lineCount; i++){
		ch=createChild();
		if(ch->load(is)==false){
			error(ch->errorCode, "line[%d]: %s", i, bob_traslate_error(ch->errorCode));
			return false;
		}
	}
	is >> hdr;
	if(hdr!=hdr_end)
		error(e_badEndHeader);
		
	return hdr==hdr_end && !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_dom_notes::toFile(bob_dom_obinaryfilestream& os)
{
	if(size()==0) return true;
	
	os << hdr_begin;
	for(iterator &it=begin(); it!=end(); ++it){
		it->toFile(os);
	}
	os << hdr_end;
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_notes::toFile(bob_dom_otextfilestream& os)
{
 os << noSemicolons << "T // Notes" << endl;
	for(iterator &it=begin(); it!=end(); ++it){
		it->toFile(os);
		os << endl;
	}
	os << -1 << " // end of Notes" << endl;
	return os.good();
}
//---------------------------------------------------------------------------------
// NOTE - object
bool bob_note::load(bob_dom_ibinaryfilestream& is)
{
	is >> value;
	is >> text;
	if(is.fail())
		errorCode=e_notEnoughData;
	return !is.fail();
}
//---------------------------------------------------------------------------------
bool bob_note::toFile(bob_dom_obinaryfilestream& os)
{
	os << value << text;
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_note::toFile(bob_dom_otextfilestream& os)
{
	os << autoSemicolons << value << text;
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_constants::load(bob_dom_ibinaryfilestream& is)
{
	int hdr, count;
	is >> hdr;
	if(hdr!=hdr_begin){
		error(e_badHeader);
		return false;
	}
	
	is >> count;
	
	constant c;
	for(int i=0; i < count; i++){
		c.load(is);
		values.push_back(c);
	}
	is >> hdr;
	if(hdr!=hdr_end){
		error(e_badEndHeader);
		return false;
	}
	return is.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_constants::toFile(bob_dom_obinaryfilestream& os)
{
	if(values.size()==0) return true;
	
	os << hdr_begin << (int)values.size();
	
	for(iterator &it=values.begin(); it!=values.end(); ++it){
		(*it).toFile(os);
	}
	os << hdr_end;
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_constants::toFile(bob_dom_otextfilestream& os)
{
	if(values.size()==0) return true;
	
	os << noSemicolons << "K " << autoSemicolons << (int)values.size();
	for(iterator &it=values.begin(); it!=values.end(); ++it){
		(*it).toFile(os);
	}
	os << noSemicolons;
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_constants::constant::load(bob_dom_ibinaryfilestream& is)
{
	is >> a >> b;
	return is.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_constants::constant::toFile(bob_dom_obinaryfilestream& os)
{
	os << a << b;
	return os.good();
}
//---------------------------------------------------------------------------------
bool bob_dom_constants::constant::toFile(bob_dom_otextfilestream& os)
{
	os << a << b;
	return os.good();
}
//---------------------------------------------------------------------------------