// dxf.cpp
// Copyright 2011, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.

#include "dxf.h"

using namespace std;
static const double Pi = 3.14159265358979323846264338327950288419716939937511;

CDxfWrite::CDxfWrite(const char* filepath)
{
	// start the file
	m_fail = false;
#ifdef __WXMSW__
	m_ofs = new ofstream(filepath, ios::out);
#else
	m_ofs = new ofstream(filepath, ios::out);
#endif
	if(!(*m_ofs)){
		m_fail = true;
		return;
	}
	m_ofs->imbue(std::locale("C"));

	// start
	(*m_ofs) << 0          << endl;
	(*m_ofs) << "SECTION"  << endl;
	(*m_ofs) << 2          << endl;
	(*m_ofs) << "ENTITIES" << endl;
}

CDxfWrite::~CDxfWrite()
{
	// end
	(*m_ofs) << 0          << endl;
	(*m_ofs) << "ENDSEC"   << endl;
	(*m_ofs) << 0          << endl;
	(*m_ofs) << "EOF";

	delete m_ofs;
}

void CDxfWrite::WriteLine(const double* s, const double* e, const char* layer_name)
{
	(*m_ofs) << 0			<< endl;
	(*m_ofs) << "LINE"		<< endl;
	(*m_ofs) << 8			<< endl;	// Group code for layer name
	(*m_ofs) << layer_name	<< endl;	// Layer number
	(*m_ofs) << 10			<< endl;	// Start point of line
	(*m_ofs) << s[0]		<< endl;	// X in WCS coordinates
	(*m_ofs) << 20			<< endl;
	(*m_ofs) << s[1]		<< endl;	// Y in WCS coordinates
	(*m_ofs) << 30			<< endl;
	(*m_ofs) << s[2]		<< endl;	// Z in WCS coordinates
	(*m_ofs) << 11			<< endl;	// End point of line
	(*m_ofs) << e[0]		<< endl;	// X in WCS coordinates
	(*m_ofs) << 21			<< endl;
	(*m_ofs) << e[1]		<< endl;	// Y in WCS coordinates
	(*m_ofs) << 31			<< endl;
	(*m_ofs) << e[2]		<< endl;	// Z in WCS coordinates
}

void CDxfWrite::WritePoint(const double* s, const char* layer_name)
{
	(*m_ofs) << 0			<< endl;
	(*m_ofs) << "POINT"		<< endl;
	(*m_ofs) << 8			<< endl;	// Group code for layer name
	(*m_ofs) << layer_name	<< endl;	// Layer number
	(*m_ofs) << 10			<< endl;	// Start point of line
	(*m_ofs) << s[0]		<< endl;	// X in WCS coordinates
	(*m_ofs) << 20			<< endl;
	(*m_ofs) << s[1]		<< endl;	// Y in WCS coordinates
	(*m_ofs) << 30			<< endl;
	(*m_ofs) << s[2]		<< endl;	// Z in WCS coordinates
}

void CDxfWrite::WriteArc(const double* s, const double* e, const double* c, bool dir, const char* layer_name)
{
	double ax = s[0] - c[0];
	double ay = s[1] - c[1];
	double bx = e[0] - c[0];
	double by = e[1] - c[1];

	double start_angle = atan2(ay, ax) * 180/Pi;
	double end_angle = atan2(by, bx) * 180/Pi;
	double radius = sqrt(ax*ax + ay*ay);
	if(!dir){
		double temp = start_angle;
		start_angle = end_angle;
		end_angle = temp;
	}
	(*m_ofs) << 0			<< endl;
	(*m_ofs) << "ARC"		<< endl;
	(*m_ofs) << 8			<< endl;	// Group code for layer name
	(*m_ofs) << layer_name	<< endl;	// Layer number
	(*m_ofs) << 10			<< endl;	// Centre X
	(*m_ofs) << c[0]		<< endl;	// X in WCS coordinates
	(*m_ofs) << 20			<< endl;
	(*m_ofs) << c[1]		<< endl;	// Y in WCS coordinates
	(*m_ofs) << 30			<< endl;
	(*m_ofs) << c[2]		<< endl;	// Z in WCS coordinates
	(*m_ofs) << 40			<< endl;	//
	(*m_ofs) << radius		<< endl;	// Radius
	(*m_ofs) << 50			<< endl;
	(*m_ofs) << start_angle	<< endl;	// Start angle
	(*m_ofs) << 51			<< endl;
	(*m_ofs) << end_angle	<< endl;	// End angle
}

void CDxfWrite::WriteCircle(const double* c, double radius, const char* layer_name)
{
	(*m_ofs) << 0			<< endl;
	(*m_ofs) << "CIRCLE"		<< endl;
	(*m_ofs) << 8			<< endl;	// Group code for layer name
	(*m_ofs) << layer_name	<< endl;	// Layer number
	(*m_ofs) << 10			<< endl;	// Centre X
	(*m_ofs) << c[0]		<< endl;	// X in WCS coordinates
	(*m_ofs) << 20			<< endl;
	(*m_ofs) << c[1]		<< endl;	// Y in WCS coordinates
	(*m_ofs) << 30			<< endl;
	(*m_ofs) << c[2]		<< endl;	// Z in WCS coordinates
	(*m_ofs) << 40			<< endl;	//
	(*m_ofs) << radius		<< endl;	// Radius
}

void CDxfWrite::WriteEllipse(const double* c, double major_radius, double minor_radius, double rotation, double start_angle, double end_angle, bool dir, const char* layer_name )
{
	double m[3];
	m[2]=0;
	m[0] = major_radius * sin(rotation);
	m[1] = major_radius * cos(rotation);

	double ratio = minor_radius/major_radius;

	if(!dir){
		double temp = start_angle;
		start_angle = end_angle;
		end_angle = temp;
	}
	(*m_ofs) << 0			<< endl;
	(*m_ofs) << "ELLIPSE"		<< endl;
	(*m_ofs) << 8			<< endl;	// Group code for layer name
	(*m_ofs) << layer_name	<< endl;	// Layer number
	(*m_ofs) << 10			<< endl;	// Centre X
	(*m_ofs) << c[0]		<< endl;	// X in WCS coordinates
	(*m_ofs) << 20			<< endl;
	(*m_ofs) << c[1]		<< endl;	// Y in WCS coordinates
	(*m_ofs) << 30			<< endl;
	(*m_ofs) << c[2]		<< endl;	// Z in WCS coordinates
	(*m_ofs) << 40			<< endl;	//
	(*m_ofs) << ratio		<< endl;	// Ratio
	(*m_ofs) << 11			<< endl;	//
	(*m_ofs) << m[0]		<< endl;	// Major X
	(*m_ofs) << 21			<< endl;
	(*m_ofs) << m[1]		<< endl;	// Major Y
	(*m_ofs) << 31			<< endl;
	(*m_ofs) << m[2]		<< endl;	// Major Z
	(*m_ofs) << 41		<< endl;
	(*m_ofs) << start_angle	<< endl;	// Start angle
	(*m_ofs) << 42		<< endl;
	(*m_ofs) << end_angle	<< endl;	// End angle
}

CDxfRead::CDxfRead(const char* filepath)
{
	// start the file
	memset( m_unused_line, '\0', sizeof(m_unused_line) );
	m_fail = false;
	m_eUnits = eMillimeters;
	strcpy(m_layer_name, "0");	// Default layer name
	m_ignore_errors = true;

	m_ifs = new ifstream(filepath);
	if(!(*m_ifs)){
		m_fail = true;
		return;
	}
	m_ifs->imbue(std::locale("C"));

}

CDxfRead::~CDxfRead()
{
	delete m_ifs;
}

double CDxfRead::mm( const double & value ) const
{
	switch(m_eUnits)
	{
		case eUnspecified:	return(value * 1.0);	// We don't know any better.
		case eInches:		return(value * 25.4);
		case eFeet:		return(value * 25.4 * 12);
		case eMiles:		return(value *  1609344.0);
		case eMillimeters:	return(value * 1.0);
		case eCentimeters:	return(value * 10.0);
		case eMeters:		return(value * 1000.0);
		case eKilometers:	return(value * 1000000.0);
		case eMicroinches:	return(value * 25.4 / 1000.0);
		case eMils:		return(value * 25.4 / 1000.0);
		case eYards:		return(value * 3 * 12 * 25.4);
		case eAngstroms:	return(value * 0.0000001);
		case eNanometers:	return(value * 0.000001);
		case eMicrons:		return(value * 0.001);
		case eDecimeters:	return(value * 100.0);
		case eDekameters:	return(value * 10000.0);
		case eHectometers:	return(value * 100000.0);
		case eGigameters:	return(value * 1000000000000.0);
		case eAstronomicalUnits: return(value * 149597870690000.0);
		case eLightYears:	return(value * 9454254955500000000.0);
		case eParsecs:		return(value * 30856774879000000000.0);
		default:		return(value * 1.0);	// We don't know any better.
	} // End switch
} // End mm() method


bool CDxfRead::ReadLine()
{
	double s[3] = {0, 0, 0};
	double e[3] = {0, 0, 0};

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;

		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadLine() Failed to read integer from '%s'\n", m_str );
		    return false;
		}

		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found, so finish with line
			        DerefACI();
			        OnReadLine(s, e);
				return true;

			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// start x
				get_line();
				ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false;
				break;
			case 20:
				// start y
				get_line();
				ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false;
				break;
			case 30:
				// start z
				get_line();
				ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false;
				break;
			case 11:
				// end x
				get_line();
				ss.str(m_str); ss >> e[0]; e[0] = mm(e[0]); if(ss.fail()) return false;
				break;
			case 21:
				// end y
				get_line();
				ss.str(m_str); ss >> e[1]; e[1] = mm(e[1]); if(ss.fail()) return false;
				break;
			case 31:
				// end z
				get_line();
				ss.str(m_str); ss >> e[2]; e[2] = mm(e[2]); if(ss.fail()) return false;
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;

			case 100:
			case 39:
			case 210:
			case 220:
			case 230:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}

	try {
	    DerefACI();
	    OnReadLine(s, e);
	}
	catch(...)
	{
		if (! IgnoreErrors()) throw;	// Re-throw the exception.
	}

	return false;
}

bool CDxfRead::ReadPoint()
{
	double s[3] = {0, 0, 0};

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;

		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadPoint() Failed to read integer from '%s'\n", m_str );
		    return false;
		}

		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found, so finish with line
			        DerefACI();
				OnReadPoint(s);
				return true;

			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// start x
				get_line();
				ss.str(m_str); ss >> s[0]; s[0] = mm(s[0]); if(ss.fail()) return false;
				break;
			case 20:
				// start y
				get_line();
				ss.str(m_str); ss >> s[1]; s[1] = mm(s[1]); if(ss.fail()) return false;
				break;
			case 30:
				// start z
				get_line();
				ss.str(m_str); ss >> s[2]; s[2] = mm(s[2]); if(ss.fail()) return false;
				break;

		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;

			case 100:
			case 39:
			case 210:
			case 220:
			case 230:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}

	}

	try {
	    DerefACI();
	    OnReadPoint(s);
	}
	catch(...)
	{
		if (! IgnoreErrors()) throw;	// Re-throw the exception.
	}

	return false;
}

bool CDxfRead::ReadArc()
{
	double start_angle = 0.0;// in degrees
	double end_angle = 0.0;
	double radius = 0.0;
	double c[3]; // centre

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadArc() Failed to read integer from '%s'\n", m_str);
		    return false;
		}

		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found, so finish with arc
			        DerefACI();
			        OnReadArc(start_angle, end_angle, radius, c);
			        return true;

			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// centre x
				get_line();
				ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
				break;
			case 20:
				// centre y
				get_line();
				ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
				break;
			case 30:
				// centre z
				get_line();
				ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
				break;
			case 40:
				// radius
				get_line();
				ss.str(m_str); ss >> radius; radius = mm(radius); if(ss.fail()) return false;
				break;
			case 50:
				// start angle
				get_line();
				ss.str(m_str); ss >> start_angle; if(ss.fail()) return false;
				break;
			case 51:
				// end angle
				get_line();
				ss.str(m_str); ss >> end_angle; if(ss.fail()) return false;
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;
			case 100:
			case 39:
			case 210:
			case 220:
			case 230:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}
	DerefACI();
	OnReadArc(start_angle, end_angle, radius, c);
	return false;
}

bool CDxfRead::ReadSpline()
{
	struct SplineData sd;
	sd.norm[0] = 0;
	sd.norm[1] = 0;
	sd.norm[2] = 1;
	sd.degree = 0;
	sd.knots = 0;
	sd.flag = 0;
	sd.control_points = 0;
	sd.fit_points = 0;

	double temp_double;

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadSpline() Failed to read integer from '%s'\n", m_str);
		    return false;
		}
		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found, so finish with Spline
			        DerefACI();
				OnReadSpline(sd);
				return true;
			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;
			case 210:
				// normal x
				get_line();
				ss.str(m_str); ss >> sd.norm[0]; sd.norm[0] = mm(sd.norm[0]); if(ss.fail()) return false;
				break;
			case 220:
				// normal y
				get_line();
				ss.str(m_str); ss >> sd.norm[1]; sd.norm[1] = mm(sd.norm[1]); if(ss.fail()) return false;
				break;
			case 230:
				// normal z
				get_line();
				ss.str(m_str); ss >> sd.norm[2]; sd.norm[2] = mm(sd.norm[2]); if(ss.fail()) return false;
				break;
			case 70:
				// flag
				get_line();
				ss.str(m_str); ss >> sd.flag; if(ss.fail()) return false;
				break;
			case 71:
				// degree
				get_line();
				ss.str(m_str); ss >> sd.degree; if(ss.fail()) return false;
				break;
			case 72:
				// knots
				get_line();
				ss.str(m_str); ss >> sd.knots; if(ss.fail()) return false;
				break;
			case 73:
				// control points
				get_line();
				ss.str(m_str); ss >> sd.control_points; if(ss.fail()) return false;
				break;
			case 74:
				// fit points
				get_line();
				ss.str(m_str); ss >> sd.fit_points; if(ss.fail()) return false;
				break;
			case 12:
				// starttan x
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.starttanx.push_back(temp_double);
				break;
			case 22:
				// starttan y
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.starttany.push_back(temp_double);
				break;
			case 32:
				// starttan z
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.starttanz.push_back(temp_double);
				break;
			case 13:
				// endtan x
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.endtanx.push_back(temp_double);
				break;
			case 23:
				// endtan y
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.endtany.push_back(temp_double);
				break;
			case 33:
				// endtan z
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.endtanz.push_back(temp_double);
				break;
			case 40:
				// knot
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.knot.push_back(temp_double);
				break;
			case 41:
				// weight
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.weight.push_back(temp_double);
				break;
			case 10:
				// control x
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.controlx.push_back(temp_double);
				break;
			case 20:
				// control y
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.controly.push_back(temp_double);
				break;
			case 30:
				// control z
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.controlz.push_back(temp_double);
				break;
			case 11:
				// fit x
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.fitx.push_back(temp_double);
				break;
			case 21:
				// fit y
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.fity.push_back(temp_double);
				break;
			case 31:
				// fit z
				get_line();
				ss.str(m_str); ss >> temp_double; if(ss.fail()) return false;
				sd.fitz.push_back(temp_double);
				break;
			case 42:
			case 43:
			case 44:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}
	DerefACI();
	OnReadSpline(sd);
	return false;
}


bool CDxfRead::ReadCircle()
{
	double radius = 0.0;
	double c[3]; // centre

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadCircle() Failed to read integer from '%s'\n", m_str);
		    return false;
		}
		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found, so finish with Circle
			        DerefACI();
				OnReadCircle(c, radius);
				return true;
			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// centre x
				get_line();
				ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
				break;
			case 20:
				// centre y
				get_line();
				ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
				break;
			case 30:
				// centre z
				get_line();
				ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
				break;
			case 40:
				// radius
				get_line();
				ss.str(m_str); ss >> radius; radius = mm(radius); if(ss.fail()) return false;
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;

			case 100:
			case 39:
			case 210:
			case 220:
			case 230:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}
	DerefACI();
	OnReadCircle(c, radius);
	return false;
}


bool CDxfRead::ReadText()
{
	double c[3]; // coordinate
	double height = 0.03082;

	memset( c, 0, sizeof(c) );

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadText() Failed to read integer from '%s'\n", m_str);
		    return false;
		}
		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				return false;
			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// centre x
				get_line();
				ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
				break;
			case 20:
				// centre y
				get_line();
				ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
				break;
			case 30:
				// centre z
				get_line();
				ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
				break;
		        case 40:
				// text height
				get_line();
				ss.str(m_str); ss >> height; height = mm(height); if(ss.fail()) return false;
				break;
                       case 1:
				// text
				get_line();
				DerefACI();
				OnReadText(c, height * 25.4 / 72.0, m_str);
				return(true);

		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;

			case 100:
			case 39:
			case 210:
			case 220:
			case 230:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}

	return false;
}


bool CDxfRead::ReadEllipse()
{
	double c[3]; // centre
	double m[3]; //major axis point
	double ratio=0; //ratio of major to minor axis
	double start=0; //start of arc
	double end=0;  // end of arc

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadEllipse() Failed to read integer from '%s'\n", m_str);
		    return false;
		}
		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found, so finish with Ellipse
			        DerefACI();
				OnReadEllipse(c, m, ratio, start, end);
				return true;
			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// centre x
				get_line();
				ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
				break;
			case 20:
				// centre y
				get_line();
				ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
				break;
			case 30:
				// centre z
				get_line();
				ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
				break;
			case 11:
				// major x
				get_line();
				ss.str(m_str); ss >> m[0]; m[0] = mm(m[0]); if(ss.fail()) return false;
				break;
			case 21:
				// major y
				get_line();
				ss.str(m_str); ss >> m[1]; m[1] = mm(m[1]); if(ss.fail()) return false;
				break;
			case 31:
				// major z
				get_line();
				ss.str(m_str); ss >> m[2]; m[2] = mm(m[2]); if(ss.fail()) return false;
				break;
			case 40:
				// ratio
				get_line();
				ss.str(m_str); ss >> ratio; if(ss.fail()) return false;
				break;
			case 41:
				// start
				get_line();
				ss.str(m_str); ss >> start; if(ss.fail()) return false;
				break;
			case 42:
				// end
				get_line();
				ss.str(m_str); ss >> end; if(ss.fail()) return false;
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;
			case 100:
			case 210:
			case 220:
			case 230:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}
	DerefACI();
	OnReadEllipse(c, m, ratio, start, end);
	return false;
}


static bool poly_prev_found = false;
static double poly_prev_x;
static double poly_prev_y;
static double poly_prev_z;
static double poly_prev_bulge_found;
static double poly_prev_bulge;
static bool poly_first_found = false;
static double poly_first_x;
static double poly_first_y;
static double poly_first_z;

static void AddPolyLinePoint(CDxfRead* dxf_read, double x, double y, double z, bool bulge_found, double bulge)
{

	try {
		if(poly_prev_found)
		{
			bool arc_done = false;
			if(poly_prev_bulge_found)
			{
				double cot = 0.5 * ((1.0 / poly_prev_bulge) - poly_prev_bulge);
				double cx = ((poly_prev_x + x) - ((y - poly_prev_y) * cot)) / 2.0;
				double cy = ((poly_prev_y + y) + ((x - poly_prev_x) * cot)) / 2.0;
				double ps[3] = {poly_prev_x, poly_prev_y, poly_prev_z};
				double pe[3] = {x, y, z};
				double pc[3] = {cx, cy, (poly_prev_z + z)/2.0};
				dxf_read->OnReadArc(ps, pe, pc, poly_prev_bulge >= 0);
				arc_done = true;
			}

			if(!arc_done)
			{
				double s[3] = {poly_prev_x, poly_prev_y, poly_prev_z};
				double e[3] = {x, y, z};
				dxf_read->OnReadLine(s, e);
			}
		}

		poly_prev_found = true;
		poly_prev_x = x;
		poly_prev_y = y;
		poly_prev_z = z;
		if(!poly_first_found)
		{
			poly_first_x = x;
			poly_first_y = y;
			poly_first_z = z;
			poly_first_found = true;
		}
		poly_prev_bulge_found = bulge_found;
		poly_prev_bulge = bulge;
	}
	catch(...)
	{
		if (! dxf_read->IgnoreErrors())	throw;	// Re-throw it.
	}
}

static void PolyLineStart()
{
	poly_prev_found = false;
	poly_first_found = false;
}

bool CDxfRead::ReadLwPolyLine()
{
	PolyLineStart();

	bool x_found = false;
	bool y_found = false;
	double x = 0.0;
	double y = 0.0;
	double z = 0.0;
	bool bulge_found = false;
	double bulge = 0.0;
	bool closed = false;
	int flags;
	bool next_item_found = false;

	while(!((*m_ifs).eof()) && !next_item_found)
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
			printf("CDxfRead::ReadLwPolyLine() Failed to read integer from '%s'\n", m_str);
			return false;
		}
		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found

			        DerefACI();
			        if(x_found && y_found){
					// add point
					AddPolyLinePoint(this, x, y, z, bulge_found, bulge);
					bulge_found = false;
					x_found = false;
					y_found = false;
				}
				next_item_found = true;
				break;
			case 8: // Layer name follows
				get_line();
				strcpy(m_layer_name, m_str);
				break;

			case 10:
				// x
				get_line();
				if(x_found && y_found){
					// add point
					AddPolyLinePoint(this, x, y, z, bulge_found, bulge);
					bulge_found = false;
					x_found = false;
					y_found = false;
				}
				ss.str(m_str); ss >> x; x = mm(x); if(ss.fail()) return false;
				x_found = true;
				break;
			case 20:
				// y
				get_line();
				ss.str(m_str); ss >> y; y = mm(y); if(ss.fail()) return false;
				y_found = true;
				break;
			case 42:
				// bulge
				get_line();
				ss.str(m_str); ss >> bulge; if(ss.fail()) return false;
				bulge_found = true;
				break;
			case 70:
				// flags
				get_line();
				if(sscanf(m_str, "%d", &flags) != 1)return false;
				closed = ((flags & 1) != 0);
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}

	if(next_item_found)
	{
		if(closed && poly_first_found)
		{
			// repeat the first point
		        DerefACI();
			AddPolyLinePoint(this, poly_first_x, poly_first_y, poly_first_z, false, 0.0);
		}
		return true;
	}

	return false;
}


bool CDxfRead::ReadVertex(double *pVertex, bool *bulge_found, double *bulge)
{
    bool x_found = false;
    bool y_found = false;

    double x = 0.0;
    double y = 0.0;
    double z = 0.0;
    *bulge = 0.0;
    *bulge_found = false;

    pVertex[0] = 0.0;
    pVertex[1] = 0.0;
    pVertex[2] = 0.0;

    while(!(*m_ifs).eof()) {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1) {
            printf("CDxfRead::ReadVertex() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
        case 0:
	    DerefACI();
            put_line(m_str);    // read one line too many.  put it back.
            return(x_found && y_found);
            break;

        case 8: // Layer name follows
            get_line();
            strcpy(m_layer_name, m_str);
            break;

        case 10:
            // x
            get_line();
            ss.str(m_str); ss >> x; pVertex[0] = mm(x); if(ss.fail()) return false;
            x_found = true;
            break;
        case 20:
            // y
            get_line();
            ss.str(m_str); ss >> y; pVertex[1] = mm(y); if(ss.fail()) return false;
            y_found = true;
            break;
        case 30:
            // z
            get_line();
            ss.str(m_str); ss >> z; pVertex[2] = mm(z); if(ss.fail()) return false;
            break;

        case 42:
            get_line();
            *bulge_found = true;
            ss.str(m_str); ss >> *bulge; if(ss.fail()) return false;
            break;
	case 62:
	    // color index
	    get_line();
	    ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
	    break;

        default:
            // skip the next line
            get_line();
            break;
        }
    }

    return false;
}



bool CDxfRead::ReadPolyLine()
{
	PolyLineStart();

	bool closed = false;
	int flags;
	bool first_vertex_section_found = false;
	double first_vertex[3];
	bool bulge_found;
	double bulge;

	while(!(*m_ifs).eof())
	{
		get_line();
		int n;
		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadPolyLine() Failed to read integer from '%s'\n", m_str);
		    return false;
		}
		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:
				// next item found
			        DerefACI();
				get_line();
				if (! strcmp(m_str,"VERTEX"))
				{
				    double vertex[3];
					if (CDxfRead::ReadVertex(vertex, &bulge_found, &bulge))
					{
						if(!first_vertex_section_found) {
							first_vertex_section_found = true;
							memcpy(first_vertex, vertex, 3*sizeof(double));
						}
						AddPolyLinePoint(this, vertex[0], vertex[1], vertex[2], bulge_found, bulge);
						break;
					}
				}
				if (! strcmp(m_str,"SEQEND"))
				{
                    if(closed && first_vertex_section_found) {
                        AddPolyLinePoint(this, first_vertex[0], first_vertex[1], first_vertex[2], 0, 0);
                    }
					first_vertex_section_found = false;
					PolyLineStart();
				    return(true);
				}
				break;
			case 70:
				// flags
				get_line();
				if(sscanf(m_str, "%d", &flags) != 1)return false;
				closed = ((flags & 1) != 0);
				break;
		        case 62:
				// color index
				get_line();
				ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}

	return false;
}

void CDxfRead::OnReadArc(double start_angle, double end_angle, double radius, const double* c){
	double s[3], e[3];
	s[0] = c[0] + radius * cos(start_angle * Pi/180);
	s[1] = c[1] + radius * sin(start_angle * Pi/180);
	s[2] = c[2];
	e[0] = c[0] + radius * cos(end_angle * Pi/180);
	e[1] = c[1] + radius * sin(end_angle * Pi/180);
	e[2] = c[2];

	OnReadArc(s, e, c, true);
}

void CDxfRead::OnReadCircle(const double* c, double radius){
	double s[3];
    double start_angle = 0;
	s[0] = c[0] + radius * cos(start_angle * Pi/180);
	s[1] = c[1] + radius * sin(start_angle * Pi/180);
	s[2] = c[2];

	OnReadCircle(s, c, false); //false to change direction because otherwise the arc length is zero
}

void CDxfRead::OnReadEllipse(const double* c, const double* m, double ratio, double start_angle, double end_angle){
	double major_radius = sqrt(m[0]*m[0] + m[1]*m[1] + m[2]*m[2]);
	double minor_radius = major_radius * ratio;

	//Since we only support 2d stuff, we can calculate the rotation from the major axis x and y value only,
	//since z is zero, major_radius is the vector length

	double rotation = atan2(m[1]/major_radius,m[0]/major_radius);


	OnReadEllipse(c, major_radius, minor_radius, rotation, start_angle, end_angle, true);
}

bool CDxfRead::ReadInsert()
{
    double c[3]; // coordinate
    double s[3]; // scale
    double rot = 0.0; // rotation
    char name[1024];
    s[0] = 1.0;
    s[1] = 1.0;
    s[2] = 1.0;

    while(!((*m_ifs).eof()))
    {
        get_line();
        int n;
        if(sscanf(m_str, "%d", &n) != 1)
        {
            printf("CDxfRead::ReadInsert() Failed to read integer from '%s'\n", m_str);
            return false;
        }
        std::istringstream ss;
        ss.imbue(std::locale("C"));
        switch(n){
            case 0: 
                // next item found
                DerefACI();
                OnReadInsert(c, s, name, rot * Pi/180);
                return(true);
            case 8: 
                // Layer name follows
                get_line();
                strcpy(m_layer_name, m_str);
                break;
            case 10:
                // coord x
                get_line();
                ss.str(m_str); ss >> c[0]; c[0] = mm(c[0]); if(ss.fail()) return false;
                break;
            case 20:
                // coord y
                get_line();
                ss.str(m_str); ss >> c[1]; c[1] = mm(c[1]); if(ss.fail()) return false;
                break;
            case 30:
                // coord z
                get_line();
                ss.str(m_str); ss >> c[2]; c[2] = mm(c[2]); if(ss.fail()) return false;
                break;
            case 41:
                // scale x
                get_line();
                ss.str(m_str); ss >> s[0]; if(ss.fail()) return false;
                break;
            case 42:
                // scale y
                get_line();
                ss.str(m_str); ss >> s[1]; if(ss.fail()) return false;
                break;
            case 43:
                // scale z
                get_line();
                ss.str(m_str); ss >> s[2]; if(ss.fail()) return false;
                break;
            case 50:
                // rotation
                get_line();
                ss.str(m_str); ss >> rot; if(ss.fail()) return false;
                break;
            case 2:
                // block name
                get_line();
                strcpy(name, m_str);
                break;
            case 62:
                // color index
                get_line();
                ss.str(m_str); ss >> m_aci; if(ss.fail()) return false;
                break;
            case 100:
            case 39:
            case 210:
            case 220:
            case 230:
                // skip the next line
                get_line();
                break;
            default:
                // skip the next line
                get_line();
                break;
        }
    }
    return false;
}

void CDxfRead::get_line()
{
    if (m_unused_line[0] != '\0')
    {
        strcpy(m_str, m_unused_line);
        memset( m_unused_line, '\0', sizeof(m_unused_line));
        return;
    }

	m_ifs->getline(m_str, 1024);

	char str[1024];
	int len = strlen(m_str);
	int j = 0;
	bool non_white_found = false;
	for(int i = 0; i<len; i++){
		if(non_white_found || (m_str[i] != ' ' && m_str[i] != '\t')){
#if wxUSE_UNICODE
			if(m_str[i] != '\r')
#endif
			{
				str[j] = m_str[i]; j++;
			}
			non_white_found = true;
		}
	}
	str[j] = 0;
	strcpy(m_str, str);
}

void CDxfRead::put_line(const char *value)
{
	strcpy( m_unused_line, value );
}


bool CDxfRead::ReadUnits()
{
	get_line();	// Skip to next line.
	get_line();	// Skip to next line.
	int n = 0;
	if(sscanf(m_str, "%d", &n) == 1)
	{
		m_eUnits = eDxfUnits_t( n );
		return(true);
	} // End if - then
	else
	{
	    printf("CDxfRead::ReadUnits() Failed to get integer from '%s'\n", m_str);
		return(false);
	}
}


bool CDxfRead::ReadLayer()
{
        std::string layername;
	int aci = -1;

	while(!((*m_ifs).eof()))
	{
		get_line();
		int n;

		if(sscanf(m_str, "%d", &n) != 1)
		{
		    printf("CDxfRead::ReadLayer() Failed to read integer from '%s'\n", m_str );
		    return false;
		}

		std::istringstream ss;
		ss.imbue(std::locale("C"));
		switch(n){
			case 0:	// next item found, so finish with line
			        if (layername.empty())
				{
				    printf("CDxfRead::ReadLayer() - no layer name\n");
				    return false;
				}
			        m_layer_aci[layername] = aci;
				return true;

			case 2: // Layer name follows
				get_line();
				layername = m_str;
				break;

			case 62:
				// layer color ; if negative, layer is off
				get_line();
				if(sscanf(m_str, "%d", &aci) != 1)return false;
				break;

			case 6:	// linetype name
			case 70: // layer flags
			case 100:
			case 290:
			case 370:
			case 390:
				// skip the next line
				get_line();
				break;
			default:
				// skip the next line
				get_line();
				break;
		}
	}
	return false;
}

void CDxfRead::DoRead(const bool ignore_errors /* = false */ )
{
	m_ignore_errors = ignore_errors;
	if(m_fail)return;

	get_line();

	while(!((*m_ifs).eof()))
	{
		if (!strcmp( m_str, "$INSUNITS" )){
			if (!ReadUnits())return;
			continue;
		} // End if - then

		else if (!strcmp( m_str, "AcDbBlockBegin" )){
			get_line();

			if (! strcmp(m_str,"2"))
			{
			    get_line();
			    strcpy(m_block_name, m_str);
			}
		} // End if - then
		else if(!strcmp(m_str, "0"))
		{
			get_line();
			if (!strcmp( m_str, "SECTION" )){
			  get_line();
			  get_line();
			  strcpy(m_section_name, m_str);
			  strcpy(m_block_name, "");

		} // End if - then
		else if (!strcmp( m_str, "TABLE" )){
			  get_line();
			  get_line();
		}

		else if (!strcmp( m_str, "LAYER" )){
			  get_line();
			  get_line();
			  if(!ReadLayer())
			    {
			      printf("CDxfRead::DoRead() Failed to read layer\n");
			      return;
			    }
			  continue;		}

		else if (!strcmp( m_str, "ENDSEC" )){
                    strcpy(m_section_name, "");
                    strcpy(m_block_name, "");
                } // End if - then
		else if(!strcmp(m_str, "LINE")){
				if(!ReadLine())
				{
				    printf("CDxfRead::DoRead() Failed to read line\n");
				    return;
				}
				continue;
			}
			else if(!strcmp(m_str, "ARC")){
				if(!ReadArc())
				{
				    printf("CDxfRead::DoRead() Failed to read arc\n");
				    return;
				}
				continue;
			}
			else if(!strcmp(m_str, "CIRCLE")){
				if(!ReadCircle())
				{
				    printf("CDxfRead::DoRead() Failed to read circle\n");
				    return;
				}
				continue;
			}
			else if(!strcmp(m_str, "MTEXT")){
				if(!ReadText())
				{
				    printf("CDxfRead::DoRead() Failed to read text\n");
				    return;
				}
				continue;
			}
			else if(!strcmp(m_str, "ELLIPSE")){
				if(!ReadEllipse())
				{
				    printf("CDxfRead::DoRead() Failed to read ellipse\n");
				    return;
				}
				continue;
			}
			else if(!strcmp(m_str, "SPLINE")){
				if(!ReadSpline())
				{
				    printf("CDxfRead::DoRead() Failed to read spline\n");
				    return;
				}
				continue;
			}
			else if (!strcmp(m_str, "LWPOLYLINE")) {
				if(!ReadLwPolyLine())
				{
				    printf("CDxfRead::DoRead() Failed to read LW Polyline\n");
				    return;
				}
				continue;
			}
			else if (!strcmp(m_str, "POLYLINE")) {
				if(!ReadPolyLine())
				{
				    printf("CDxfRead::DoRead() Failed to read Polyline\n");
				    return;
				}
				continue;
			}
			else if (!strcmp(m_str, "POINT")) {
				if(!ReadPoint())
				{
				    printf("CDxfRead::DoRead() Failed to read Point\n");
				    return;
				}
				continue;
			}
            else if (!strcmp(m_str, "INSERT")) {
                if(!ReadInsert())
                {
                    printf("CDxfRead::DoRead() Failed to read Insert\n");
                    return;
                }
                continue;
            }
		}

		get_line();
	}

    AddGraphics();
}


void  CDxfRead::DerefACI()
{

    if (m_aci == 256) // if color = layer color, replace by color from layer
    {
         m_aci = m_layer_aci[std::string(m_layer_name)];
    }
}

std::string CDxfRead::LayerName() const
{
    std::string result;

    if (strlen(m_section_name) > 0)
    {
		result.append(m_section_name);
    }

    if (strlen(m_block_name) > 0)
    {
        result.append(" ");
		result.append(m_block_name);
    }

    if (strlen(m_layer_name) > 0)
    {
        result.append(" ");
		result.append(m_layer_name);
    }

    return(result);
}

