#include "pch.h"
#include "MxHead.h"

#include "DxfLib\dl_codes.h"
#include "DxfLib\dl_dxf.h"
#include "DxfLib\dl_writer_ascii.h"
#include "MxWriteContext.h"
#include "Tool.h"

#include "MxOptEntity.h"
#include "MxReadContext.h"
#include "MxReadFilter.h"


MxOptEntity::MxOptEntity(MxWriteContext* pWriteContext)
	: m_pWriteContext(pWriteContext)
{
	m_pDxf = m_pWriteContext->GetDxf();
	m_pReadContext = NULL;
}

MxOptEntity::MxOptEntity(MxReadContext* pReadContext)
	: m_pReadContext(pReadContext)
{
	m_pDxf = m_pReadContext->GetDxf();
	m_pWriteContext = NULL;
}

MxOptEntity::~MxOptEntity(void)
{
}





McDbObjectId  MxOptEntity::SetEntityAttributes(McDbEntity* pEntity,
									  const DL_Attributes& attrib,
									  McDbBlockTableRecord* pBlkRecord
									  ) 
{
	pEntity->setColor(Tool::DxfColor2Mx(attrib.getColor(),false));
	
	MxString sLineType	= MXDXF_A2W(attrib.getLineType().c_str());

	MxString sLayer		= MXDXF_A2W(attrib.getLayer().c_str());

	pEntity->setLayer(sLayer);

	pEntity->setLineWeight(Tool::DxfLineWeight2Mx(attrib.getWidth()) );
	
	pBlkRecord->appendAcDbEntity(pEntity);

	McDbObjectId retId = pEntity->objectId();
	pEntity->close();

	ASSERT(!retId.isNull());

	m_pReadContext->AddLineType(retId,sLineType);

	return retId;
}


DL_Attributes MxOptEntity::GetEntityAttributes(McDbEntity* pEntity) 
{

	// Layer:
	string sLayerName = MXDXF_W2A(pEntity->layer());
	
	// Color:
	int color =  Tool::MxColor2Dxf(pEntity->color() );
	
	string sLineType = MXDXF_W2A(pEntity->linetype());
	
	// Width:
	int width = Tool::MxLineWeight2Dxf(pEntity->lineWeight() );

	DL_Attributes attrib(sLayerName,
		color,
		width,
		sLineType);

	return attrib;
}


void MxOptEntity::WritePoint(DL_WriterA& dw, 
							 McDbPoint* pPoint,
							 const DL_Attributes& attrib
							 )
{

	McGePoint3d pt = pPoint->position();
	m_pDxf->writePoint(
		dw,
		DL_PointData(pt.x,
		pt.y,
		0.0),
		attrib
		);
}

void MxOptEntity::WriteArc(DL_WriterA& dw, 
			  McDbArc* pArc, 
			  const DL_Attributes& attrib)
{
	McGePoint3d center = pArc->center();	
	m_pDxf->writeArc(
		dw,
		DL_ArcData(center.x,
		center.y,
		center.z,
		pArc->radius(),
		pArc->startAngle() * k180DivPI, pArc->endAngle()  * k180DivPI),
		attrib);
}

void MxOptEntity::WriteEllipse(DL_WriterA& dw, 
				  McDbEllipse* pEllipse, 
				  const DL_Attributes& attrib)
{
	McGePoint3d center = pEllipse->center();

	McGeVector3d major = pEllipse->majorAxis();

	m_pDxf->writeEllipse(
		dw,
		DL_EllipseData(center.x,
		center.y,
		center.z,
		major.x,
		major.y,
		major.z,
		pEllipse->radiusRatio(),
		pEllipse->startAngle(),
		pEllipse->endAngle()),
		attrib
		);
}

void MxOptEntity::WriteText(DL_WriterA& dw, 
			   McDbText* pText, 
			   const DL_Attributes& attrib
			   )
{
	
	int hJust	= (int)(pText->horizontalMode());
	int vJust	= (int)(pText->verticalMode());

	McGePoint3d pt				= pText->position();
	McGePoint3d alignmentPoint	= pText->alignmentPoint();


	MxString sTextNameW = _T("Standard");
	McDbObjectPointer<McDbTextStyleTableRecord> spTextStyle(pText->textStyle(),McDb::kForRead);
	if(spTextStyle.openStatus() == Mcad::eOk)
	{
		spTextStyle->getName(sTextNameW);
	}

	string sTextStyle	= MXDXF_W2A(sTextNameW);

	string sText		= MXDXF_W2A(pText->textStringConst());
	
	m_pDxf->writeText(
		dw,
		DL_TextData(pt.x,
			pt.y,
			pt.z,
		alignmentPoint.x,
		alignmentPoint.y,
		alignmentPoint.z,
		pText->height(),
		pText->widthFactor(),
		0,
		hJust, vJust,
		sText,
		sTextStyle,pText->rotation() ),
		attrib);
}

void MxOptEntity::WritePolyline(DL_WriterA& dw, 
				   McDbPolyline* pPolyline, 
				   const DL_Attributes& attrib)
{
	unsigned int count = pPolyline->numVerts();
	m_pDxf->writePolyline(
		dw,
		DL_PolylineData(count,
		0, 0,
		pPolyline->isClosed() ? 0x1 : 0),
		attrib);


	for(unsigned int i  =0; i < count;i++)
	{
		double dBulge = 0.0;
		pPolyline->getBulgeAt(i,dBulge);

		McGePoint2d pt;
		pPolyline->getPointAt(i,pt);

		/*
		double      startWidth	= 0.0;

		double      endWidth	= 0.0;
		pPolyline->getWidthsAt(i,startWidth,endWidth);
		*/

		m_pDxf->writeVertex(dw,
			DL_VertexData(pt.x,
			pt.y,
			0.0,
			dBulge));
	}

	m_pDxf->writePolylineEnd(dw);
}


void MxOptEntity::WriteLine(DL_WriterA& dw, 
							McDbLine* pLine,
							const DL_Attributes& attrib)
{
	McGePoint3d pt1;
	McGePoint3d pt2;

	pLine->getStartPoint(pt1);
	pLine->getEndPoint(pt2);

	m_pDxf->writeLine(
		dw,
		DL_LineData(pt1.x,
		pt1.y,
		pt1.z,
		pt2.x,
		pt2.y,
		pt2.z),
		attrib);
}


void MxOptEntity::WriteCircle(DL_WriterA& dw, McDbCircle* pCircle,
							  const DL_Attributes& attrib) 
{
	McGePoint3d  pt = pCircle->center();

	m_pDxf->writeCircle(
		dw,
		DL_CircleData(pt.x,
		pt.y,
		pt.z,
		pCircle->radius()),
		attrib);
}

void MxOptEntity::WriteInsert(DL_WriterA& dw, 
							  McDbBlockReference* pRef,
							  const DL_Attributes& attrib
							  )
{
	MxString sNameW;
	{
		McDbObjectPointer<McDbBlockTableRecord> spBlkRec(pRef->blockTableRecord(),
			McDb::kForRead);

		if(spBlkRec.openStatus() != Mcad::eOk)
		{
			ASSERT(0);
			return;

		}
		spBlkRec->getName(sNameW);
	}

	string sName		= MXDXF_W2A(sNameW);

	McGePoint3d pos		= pRef->position();

	McGeScale3d scale	= pRef->scaleFactors();

	m_pDxf->writeInsert(
		dw,
		DL_InsertData(sName,
		pos.x,
		pos.y,
		pos.z,

		scale.sx,
		scale.sy,
		scale.sz,

		pRef->rotation() * k180DivPI,

		1, 1,
		1.0,
		1.0),
		attrib);
}

// writeHatch

void MxOptEntity::WriteHatch(DL_WriterA& dw, 
							 McDbHatch* pHatch,
							 const DL_Attributes& attrib) 
{
	// 还完善，暂时返回。
	return;


	int iLoopsCount = pHatch->numLoops();
	if(iLoopsCount == 0)
		return;

	string sPattern = MXDXF_W2A(pHatch->patternName());
	
	DL_HatchData data(iLoopsCount,
		pHatch->isSolid(),
		pHatch->patternScale(),
		pHatch->patternAngle(),
		sPattern);

	m_pDxf->writeHatch1(dw, data, attrib);

	for(int i = 0; i < pHatch->numLoops();i++)
	{
		Mdesk::Int32 iType = pHatch->loopTypeAt(i);
		if(pHatch->loopTypeAt(i) & McDbHatch::kPolyline)
		{
			Mdesk::Int32		loopType = 1;
			McGePoint2dArray	vertices;
			McGeDoubleArray		bulges;

			if(pHatch->getLoopAt(i,loopType,vertices,bulges) != Mcad::eOk)
			{
				ASSERT(0);
				continue;
			}

			// Write hatch loops:
			DL_HatchLoopData lData(vertices.length() );
			m_pDxf->writeHatchLoop1(dw, lData);

			for(int j = 0; j < vertices.length();j++)
			{
				McGePoint2d pt(vertices[j] );
				double dBulge = bulges[j];

				McGePoint2d nextPt;
				if(j == vertices.length() - 1)
				{
					// 到最后一个点了
					nextPt = vertices[0];
				}
				else
				{
					nextPt = vertices[j + 1];
				}

				if(Tool::IsZero(dBulge) )
				{
					// 直线。
					m_pDxf->writeHatchEdge(
						dw,
						DL_HatchEdgeData(pt.x,
						pt.y,
						nextPt.x,
						nextPt.y)
						);
				}
				else
				{
					// 圆弧。
					McGeCircArc2d tmpArc(pt,nextPt,dBulge,Mdesk::kFalse);

					double dSAng	= tmpArc.startAng();
					double dEAng	= tmpArc.endAng();
					double dTmpAng	= tmpArc.refVec().angle();
					double dRadius  = tmpArc.radius();
					McGePoint2d cenPt = tmpArc.center();

					if(Tool::IsZero(dRadius ))
					{
						// 半径为零为的圆弧就当成一个点处理
						m_pDxf->writeHatchEdge(
							dw,
							DL_HatchEdgeData(pt.x,
							pt.y,
							nextPt.x,
							nextPt.y)
							);
					
					}
					else
					{
						if (tmpArc.isClockWise())
						{
							dSAng = dTmpAng - dEAng;
							dEAng = dTmpAng - tmpArc.startAng();
						}
						else
						{
							dSAng =	dTmpAng + dSAng;
							dEAng =	dTmpAng + dEAng;
						}

						m_pDxf->writeHatchEdge(
							dw,
							DL_HatchEdgeData(cenPt.x,
							cenPt.y,
							dRadius,
							dSAng,
							dEAng,
							true)
							);

					}
				}
			}

			// 
			m_pDxf->writeHatchLoop2(dw, lData);
		}
		else
		{
			Mdesk::Int32 loopType;
			McGeVoidPointerArray edgePtrs;
			McGeIntArray  edgeTypes;

			
			if(pHatch->getLoopAt(i,
				loopType,
				edgePtrs,
				edgeTypes)  != Mcad::eOk)
			{
				ASSERT(0);
				continue;
			}


			for(int j = 0; j < edgePtrs.length();j++)
			{
				int iType = edgeTypes[j];
				if(iType != McDbHatch::kLine
					&& iType != McDbHatch::kCirArc
					)
				{
					// 目前暂支持这两种边类型。
					ASSERT(0);
					continue;
				}
			}


			DL_HatchLoopData lData(edgePtrs.length() );
			m_pDxf->writeHatchLoop1(dw, lData);

			for(int j = 0; j < edgePtrs.length();j++)
			{
				int iType = edgeTypes[j];
				switch(iType)
				{
				case McDbHatch::kLine:
					{
						McGeLineSeg2d* pLine =  (McGeLineSeg2d*)(edgePtrs)[j];

						McGePoint2d pt1 = pLine->startPoint();
						McGePoint2d pt2= pLine->endPoint();

						m_pDxf->writeHatchEdge(
							dw,
							DL_HatchEdgeData(pt1.x,
							pt1.y,
							pt2.x,
							pt2.y)
							);
					}

					break;

				case McDbHatch::kCirArc:
					{
						McGeCircArc2d* pArc = (McGeCircArc2d*)(edgePtrs)[i];

						double dSAng	= pArc->startAng();
						double dEAng	= pArc->endAng();
						double dTmpAng	= pArc->refVec().angle();
						double dRadius  = pArc->radius();
						McGePoint2d cenPt = pArc->center();

						if (pArc->isClockWise())
						{
							dSAng = dTmpAng - dEAng;
							dEAng = dTmpAng - pArc->startAng();
						}
						else
						{
							dSAng =	dTmpAng + dSAng;
							dEAng =	dTmpAng + dEAng;
						}

						m_pDxf->writeHatchEdge(
							dw,
							DL_HatchEdgeData(cenPt.x,
							cenPt.y,
							dRadius,
							dSAng,
							dEAng,
							true)
							);

					}
					break;
				default:
					ASSERT(0);
				}
			}

			// 
			m_pDxf->writeHatchLoop2(dw, lData);
		}
	}
	m_pDxf->writeHatch2(dw, data, attrib);
}

//


#define READ_PROC	\
	McDbObjectId retId;	\
	McDbObjectId curId = m_pReadContext->GetCurSpaceId();	\
	if(curId.isNull() )	\
		return McDbObjectId::kNull;	\
	McDbObjectPointer<McDbBlockTableRecord> spBlkRec(curId,McDb::kForWrite);	\
		if(spBlkRec.openStatus() != Mcad::eOk)	\
	{	\
		ASSERT(0);	\
		return McDbObjectId::kNull;	\
	}	\
	MxReadFilter* pFilter = m_pReadContext->GetReadFilter();	\
	DL_Attributes attrib  = pFilter->getAttributes();	\


McDbObjectId  MxOptEntity::ReadLine(const DL_LineData& data)
{
	READ_PROC;
	////////////////

	McGePoint3d pt1(data.x1, data.y1,data.z1);
	McGePoint3d pt2(data.x2, data.y2,data.z2);

	McDbLine* pLine = new McDbLine(pt1,pt2);

	retId = SetEntityAttributes(pLine,attrib,spBlkRec.object() );

	pLine->close();


	return retId;
}

McDbObjectId MxOptEntity::ReadPoint(const DL_PointData& data)
{
	READ_PROC;
	McGePoint3d pt(data.x,data.y,data.z);

	McDbPoint* pPoint = new McDbPoint(pt);

	retId = SetEntityAttributes(pPoint,attrib,spBlkRec.object() );

	pPoint->close();

	return retId;
}

McDbObjectId MxOptEntity::ReadArc(const DL_ArcData& data)
{
	READ_PROC;
	McGePoint3d center (data.cx,data.cy,data.cz);

	McDbArc* pArc = new McDbArc(center, data.radius,
		data.angle1 * kPIDiv180, data.angle2 * kPIDiv180);

	retId = SetEntityAttributes(pArc,attrib,spBlkRec.object() );

	pArc->close();

	return retId;
}

McDbObjectId MxOptEntity::ReadEllipse(const DL_EllipseData& data)
{
	READ_PROC;
	McGePoint3d  center (data.cx,data.cy,data.cz);
	McGeVector3d major(data.mx, data.my,data.mz);

	McDbEllipse* pEllipse = new McDbEllipse(center,
		McGeVector3d::kZAxis,major,data.ratio,data.angle1 ,
		data.angle2);

	retId = SetEntityAttributes(pEllipse,attrib,spBlkRec.object() );

	pEllipse->close();

	return retId;
}


void MxOptEntity::ReadVertex(const DL_VertexData& data)
{
	if(m_curPolyline.isNull())
		return;

	McDbObjectPointer<McDbPolyline> spPolyline(m_curPolyline,McDb::kForWrite);
	if(spPolyline.openStatus() != Mcad::eOk)
		return;

	McGePoint2d pt(data.x, data.y);
	spPolyline->addVertexAt(pt,data.bulge);
}

McDbObjectId MxOptEntity::ReadPolyline(const DL_PolylineData& data)
{
	READ_PROC;

	McDbPolyline* pPolyline = new McDbPolyline;
	pPolyline->setClosed(data.flags & 0x1);


	retId = SetEntityAttributes(pPolyline,attrib,spBlkRec.object() );

	pPolyline->close();

	m_curPolyline = retId;

	return retId;
}


McDbObjectId MxOptEntity::ReadInsert(const DL_InsertData& data) 
{
	READ_PROC;

	McDbObjectId blkRecId;
	MxString sName = MXDXF_A2W(data.name.c_str() );
	{
		McDbBlockTableRecordPointer spBlkRec(sName,
			m_pReadContext->GetDatabase(),McDb::kForRead);
		if(spBlkRec.openStatus() != Mcad::eOk)
		{
			ASSERT(0);
			return McDbObjectId::kNull;
		}
		blkRecId = spBlkRec->objectId();
	}

	McDbBlockReference* pBlkRef = new McDbBlockReference;

	pBlkRef->setBlockTableRecord(blkRecId);
	McGePoint3d pos(data.ipx, data.ipy,data.ipz);
	pBlkRef->setPosition(pos);

	McGeScale3d scale(data.sx, data.sy,data.sz);
	pBlkRef->setScaleFactors(scale);

	pBlkRef->setRotation(data.angle * kPIDiv180);


	// 
	retId = SetEntityAttributes(pBlkRef,attrib,spBlkRec.object() );

	pBlkRef->close();

	return retId;

}


McDbObjectId MxOptEntity::ReadCircle(const DL_CircleData& data) 
{
	READ_PROC;
	McGePoint3d pt(data.cx, data.cy,data.cz);
	McDbCircle* pCirlce = new McDbCircle(pt,McGeVector3d::kZAxis,data.radius);

	// 
	retId = SetEntityAttributes(pCirlce,attrib,spBlkRec.object() );

	pCirlce->close();

	return retId;
}



McDbObjectId MxOptEntity::ReadText(const DL_TextData& data)
{
	READ_PROC;
	McGePoint3d pos(data.ipx,data.ipy,data.ipz);
	McGePoint3d alignmentPoint(data.apx,data.apy,data.apz);
	MxString sText	= MXDXF_A2W(data.text.c_str());
	MxString sStyle	= MXDXF_A2W(data.style.c_str());

	McDbObjectId styleId;
	{
		McDbTextStyleTableRecordPointer spTextStyle(sStyle,
			m_pReadContext->GetDatabase(),
			McDb::kForRead);

		if(spTextStyle.openStatus() == Mcad::eOk)
		{
			styleId = spTextStyle->objectId();
		}
	}

	McDbText* pText = new McDbText(pos,sText,styleId,data.height,data.angle);
	pText->setHorizontalMode((McDb::TextHorzMode)(data.hJustification) );
	pText->setVerticalMode((McDb::TextVertMode)(data.vJustification) );
	pText->setAlignmentPoint(alignmentPoint);
	pText->setWidthFactor(data.xScaleFactor);


	retId = SetEntityAttributes(pText,attrib,spBlkRec.object() );

	pText->close();

	return retId;
}


bool MxOptEntity::Mx2Dxf(McDbObjectId entId,DL_WriterA* dw)
{
	if(m_pWriteContext == NULL)
	{
		ASSERT(0);
		return false;
	}

	McDbObjectPointer<McDbEntity> spEntity(entId,McDb::kForRead);

	if(spEntity.openStatus() != Mcad::eOk)
		return false;

	DL_Attributes attrib = GetEntityAttributes(spEntity.object() );

	const McRxClass* pType = spEntity->isA();
	if(McDbLine::desc() ==  pType)
	{
		// 是个直线
		McDbLine* pLine = McDbLine::cast(spEntity.object() );
		ASSERT(pLine != NULL);

		WriteLine(*dw,pLine,attrib);
	}
	else if(McDbPoint::desc() == pType)
	{
		McDbPoint* pPoint = McDbPoint::cast(spEntity.object() );
		ASSERT(pPoint != NULL);
		WritePoint(*dw,pPoint,attrib);
	}
	else if(McDbArc::desc() == pType)
	{
		McDbArc* pArc = McDbArc::cast(spEntity.object() );
		ASSERT(pArc != NULL);
		WriteArc(*dw,pArc,attrib);
	}
	else if(McDbEllipse::desc() == pType)
	{
		McDbEllipse* pEllipse = McDbEllipse::cast(spEntity.object() );
		ASSERT(pEllipse != NULL);
		WriteEllipse(*dw,pEllipse,attrib);
	}
	else if(McDbPolyline::desc() == pType)
	{
		McDbPolyline* pPolyline = McDbPolyline::cast(spEntity.object() );
		ASSERT(pPolyline != NULL);
		WritePolyline(*dw,pPolyline,attrib);
	}
	else if(McDbText::desc() == pType)
	{
		McDbText* pText = McDbText::cast(spEntity.object() );
		ASSERT(pText != NULL);
		WriteText(*dw,pText,attrib);
	}

	else if(McDbBlockReference::desc() == pType)
	{
		McDbBlockReference* pRef = McDbBlockReference::cast(spEntity.object() );
		ASSERT(pRef != NULL);
		WriteInsert(*dw,pRef,attrib);
	}

	else if(McDbCircle::desc() == pType)
	{
		McDbCircle* pCircle = McDbCircle::cast(spEntity.object() );
		ASSERT(pCircle != NULL);
		WriteCircle(*dw,pCircle,attrib);
	}

	else if(McDbHatch::desc() == pType)
	{
		McDbHatch* pHatch = McDbHatch::cast(spEntity.object() );
		ASSERT(pHatch != NULL);
		WriteHatch(*dw,pHatch,attrib);
	}

	return true;
}

