#include "ines_header.hpp"

#include <fstream>

#include "chocolat/string/number_format.hpp"
#include "chocolat/string/string_aligner.hpp"

INesHeader::INesHeader()
{
}
std::vector<unsigned char>& INesHeader::bytecode()
{
	return byte;
}
unsigned INesHeader::mapperNumber()const
{
	return (byte[6]>>4) + (byte[7] & 0xF0);
}
/** <<query>>: returns true when header signature is not valid.*/
bool INesHeader::bad()const
{
	return !isValidSignature() || !isTailValid();
}
bool INesHeader::isValid()const
{
	return !bad();
}
void INesHeader::read(std::istream& is)
{
	for (unsigned i=0; i<BYTELENGTH; ++i){
		byte.push_back(is.get());
	}
}
bool INesHeader::isVerticalMirroring()const
{
	return flagAt(FLAG_INDEX,MIRRORING_BIT);
}
bool INesHeader::isBatteryOn()const
{
	return flagAt(FLAG_INDEX, BATTERY_BIT);
}
bool INesHeader::isTrainerOn()const
{
	return flagAt(FLAG_INDEX, TRAINER_BIT);
}
bool INesHeader::is4Screen()const
{
	return flagAt(FLAG_INDEX, FOUR_SCREEN_BIT);
}
unsigned INesHeader::sizeOfPRG()const
{
	unsigned h(byte[4] * 0x4000);
	return h;
}
unsigned INesHeader::sizeOfCHR()const
{
	unsigned h(byte[5] * 0x2000);
	return h;
}
unsigned INesHeader::startAddress()const
{
	return 0x10000 - sizeOfPRG();
}
void INesHeader::dump(std::ostream& os)const
{
	StringAligner left(14, StringAligner::LEFT);
	if (isValid()){
		os << "iNes header ";
		os << std::endl;
		os << left("PRG size") << sizeOfPRG() << std::endl;
		os << left("CHR size ") << sizeOfCHR() << std::endl;
		os << left("Mapper number ") << mapperNumber() << std::endl;
		os << left("Mirroring ")
		   << (isVerticalMirroring() ? "Vertical" : "Horizontal") << std::endl;
		os << left("Battery ") << (isBatteryOn() ? "On": "Off")<<std::endl;
		os << left("Trainer ") << (isTrainerOn() ? "On": "Off")<<std::endl;
		os << left("4 Screen VRAM ") << (is4Screen() ? "On": "Off")<<std::endl;
	} else {
		os << "File is not valid nes file." << std::endl;
	}
}
bool INesHeader::isValidSignature()const
{
	if (byte[3]!=0x1A){
		return false;
	}
	std::string id;
	for (unsigned i=0; i<3;++i){
		id+=byte[i];
	}
	return (id == "NES");
}
bool INesHeader::isTailValid()const
{
	for (unsigned i=8;i<15;++i){
		if (byte[i]!=0){
			return false;
		}
	}
	return true;
}
/** index and bit are zero base.*/
bool INesHeader::flagAt(unsigned index, unsigned bit)const
{
	return byte[index] & (1<<bit);
}
