//////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2025 by Autodesk, Inc.  All rights reserved.
//
// This computer source code and related instructions and comments 
// are the unpublished confidential and proprietary information of 
// Autodesk, Inc. and are protected under applicable copyright and 
// trade secret law.  They may not be disclosed to, copied or used 
// by any third party without the prior written consent of Autodesk, Inc.
//
//////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "editExt_crx.h"
#include "Detailres.h"
#include "Detail.h"
#include "DetailDlg.h"

Acad::ErrorStatus postToDb(AcDbEntity* ent, AcDbObjectId& objId);

/////////////////////////////////////////////////////////////////////
// base abstract class for standard support of datum and feature id
/////////////////////////////////////////////////////////////////////
//ACRX_NO_CONS_DEFINE_MEMBERS(TestDetailStd,AcmSymbolStd);
ACRX_DXF_DEFINE_MEMBERS(TestDetailStd,AcmSymbolStd,AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,0, TESTDETAILSTD, _T("TESTAPP"));

TestDetailStd::TestDetailStd()
{
}

TestDetailStd::~TestDetailStd()
{
}

ACM_DEFINE_HEAP_OPERATORS_DEFAULT(TestDetailStd);

Acm::ErrorStatus 
TestDetailStd::getRevision(CString& rev, Adesk::UInt16& year) const
{
  year = 1998;
  rev = _T("ISO 128: 1982");
  return Acm::eOk;
}

Acm::ErrorStatus 
TestDetailStd::getDescription(CString& desc,
                              CBitmap*& pBmpLoaded,
                              CBitmap*& pBmpReg,
                              Adesk::Boolean& enablePref) const
{
  assertReadEnabled();
  CTemporaryResourceOverride temp;
  desc = _T("Detail");

  pBmpLoaded = new CBitmap;
  pBmpLoaded->LoadBitmap(IDB_BITMAP_LOADED);
  
  pBmpReg = new CBitmap;
  pBmpReg->LoadBitmap(IDB_BITMAP_UNLOADED);

  enablePref = Adesk::kTrue;
  return Acm::eOk;
}

Adesk::Boolean
TestDetailStd::isCompatibleTo(const AcmStandard* pStd) const
{
  assertReadEnabled();
  assert(pStd != NULL);
  return pStd->isKindOf(AcmStandard::desc());
}

void
TestDetailStd::initToEditOriginal()
{
  assertWriteEnabled();
  // nothing special - we don't have a page yet.
}

void
TestDetailStd::initToEdit()
{
  assertWriteEnabled();
  // nothing special - we don't have a page yet.
}

Adesk::Boolean 
TestDetailStd::setFactoryDefaults()
{
  assertWriteEnabled();
  mLeaderToText = Adesk::kTrue;
  return Adesk::kTrue;
}

Acm::ErrorStatus
TestDetailStd::checkVisibility(TestDetailSub* pData) const
{
  assertReadEnabled();
  return Acm::eOk;
}

Acm::ErrorStatus
TestDetailStd::initFields(TestDetailSub* pData) const
{
  assertReadEnabled();

  CString str;
  CVarDlgInfo v;
  CTemporaryResourceOverride tmpRes;

  v.setDlgIndex(0);
  v.setTooltip(_T("Detail description"));
  v.setLabel(_T("Detail"));
  pData->setVarDlgInfo((Acm::Variable)1,v);
  return checkVisibility(pData);
}

Acad::ErrorStatus
TestDetailStd::dwgInFields(AcDbDwgFiler* filer)
{
  AcmSymbolStd::dwgInFields(filer);
  filer->readItem((Adesk::Int8*)&mLeaderToText);
  return filer->filerStatus();
}

Acad::ErrorStatus
TestDetailStd::dwgOutFields(AcDbDwgFiler* filer) const
{
  AcmSymbolStd::dwgOutFields(filer);
  filer->writeItem((Adesk::Int8)mLeaderToText);
  return filer->filerStatus();
}

void 
TestDetailStd::getSubEnts(const TestDetailSub* pSym, AcmCAuxEntArray& subents) const
{
  assertReadEnabled();

  AcmStandard *pStd = standard();
  ASSERT(pStd);


  // circle
  AcmCAuxCircle *pCircle = new AcmCAuxCircle(pSym->origin(),pSym->radius());
  pCircle->setProperties(AcDbObjectId::kNull,pStd->symbolColor());
  subents.append(pCircle);

  if( mLeaderToText ) {
    AcGeVector3d vec = pSym->origin() - pSym->textNode();
    double len = vec.length() - pSym->radius();
    vec.normalize();
    AcGePoint3d pt = pSym->textNode() + len * vec;

    AcmCAuxCircle *pCirc;
    AcmCAuxPline *pLine = new AcmCAuxPline(pt, pSym->textNode());
    pLine->setProperties(AcDbObjectId::kNull,pStd->symbolColor());
    subents.append(pLine);
    // just default text
    AcmCAuxText *pText = new AcmCAuxText;
    pStd->getMTextProperties(pText->getMText());
    AcGePoint3d orig = pSym->textNode();
    if( isDotDrawn()) {
      orig += (pStd->textHeight() * 0.4)*AcGeVector3d::kYAxis;
      pCirc = new AcmCAuxCircle(pSym->textNode(),pStd->textHeight() * 0.25);
      pCirc->setProperties(AcDbObjectId::kNull,pStd->symbolColor());
      pCirc->setFillType(kAcGiFillAlways);
      subents.append(pCirc);
    }
    pText->set(orig,pSym->text(),AcDbMText::kBottomCenter);
    subents.append(pText);
  }
  else {
    // just default text
    AcmCAuxText *pText = new AcmCAuxText;
    pStd->getMTextProperties(pText->getMText());
    pText->set(pSym->textNode(),pSym->text(),AcDbMText::kBottomCenter);
    subents.append(pText);
  }
}

/////////////////////////////////////////////////////////////////////
//AcmId
/////////////////////////////////////////////////////////////////////
ACRX_DXF_DEFINE_MEMBERS(TestDetail, AcmSymbol,AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,AcDbProxyEntity::kAllAllowedBits, TESTDETAIL, "TESTAPP");

TestDetail::TestDetail()
: mSubSymbol(NULL) 
{
  mSubSymbol = new TestDetailSub;
}                     

TestDetail::~TestDetail()  
{
  if (mSubSymbol)
    delete mSubSymbol;
}                     

ACM_DEFINE_HEAP_OPERATORS_DEFAULT(TestDetail);

Acm::ErrorStatus
TestDetail::setSymbolDefaults()
{
  Acm::ErrorStatus es = AcmSymbol::setSymbolDefaults();
  if (es != Acm::eOk) return es;

  AcmStandard *pStd;
  Acad::ErrorStatus aes = acdbOpenObject(pStd,standardId(),AcDb::kForRead);
  if (Acad::eOk != aes) return (Acm::ErrorStatus)aes;
  assert(pStd);

  AcDbObjectId sId;
  es = pStd->getCurrentSymbolStd(_T("TestDetailStd"),sId); 
  pStd->close();

  if (es != Acm::eOk) return es;
  setServerId(sId);
  return Acm::eOk;
}

TestDetailSub *
TestDetail::subSymbol() const
{
  return mSubSymbol;
}

Adesk::Boolean
TestDetail::edit(int GSSelectionMarker)
{
    assertWriteEnabled();
    return Adesk::kTrue;
}

Acad::ErrorStatus 
TestDetail::getSubEnts(AcmCAuxEntArray& subents) const
{
  TestDetailStd *pStd;
  Acad::ErrorStatus es = acdbOpenObject(pStd,serverId(),AcDb::kForRead);
  if( es != Acad::eOk) return es;
  assert(pStd);
  pStd->getSubEnts(mSubSymbol,subents);
  pStd->close();
  return Acad::eOk;
}


Adesk::Boolean
TestDetail::subWorldDraw(AcGiWorldDraw* mode)
{
    Adesk::Boolean bRet = AcmSymbol::subWorldDraw(mode);
    if (drawProxyGraphic() || !bRet)
        return bRet;

  AcmCAuxEntArray subents;
  if( getSubEnts(subents) != Acad::eOk) return Adesk::kTrue;

  return subents.worldDraw(mode);
}

Acad::ErrorStatus
TestDetail::dwgInFields(AcDbDwgFiler* filer)
{
  Acad::ErrorStatus es;
  if ((es = AcmSymbol::dwgInFields(filer)) != Acad::eOk) return es;

  mSubSymbol->dwgInFields(filer,classVersion());
  return filer->filerStatus();
}

Acad::ErrorStatus
TestDetail::dwgOutFields(AcDbDwgFiler* filer) const
{
  Acad::ErrorStatus es;
  if ((es = AcmSymbol::dwgOutFields(filer)) != Acad::eOk) return es;
  mSubSymbol->dwgOutFields(filer);

  return filer->filerStatus();
}


Acad::ErrorStatus
TestDetail::subMoveGripPointsAt(const AcDbIntArray& indices,
                        const AcGeVector3d& offset)
{
  assertWriteEnabled();

  AcGeVector3d proOff = projectedVector(offset);
  AcGePoint3d orig = mSubSymbol->origin();
  AcGePoint3d txtPt = mSubSymbol->textNode();
  double rad = mSubSymbol->radius();
  AcGeVector3d vec;

  switch(indices[0]) {
  case 0:
    mSubSymbol->setOrigin(orig+proOff);
    break;
  case 1:
    vec = rad*xDir() + proOff;
    mSubSymbol->setRadius(vec.length());
    break;
  case 2:
    vec = rad*yDir() + proOff;
    mSubSymbol->setRadius(vec.length());
    break;
  case 3:
    vec = -rad*xDir() + proOff;
    mSubSymbol->setRadius(vec.length());
    break;
  case 4:
    vec = -rad*yDir() + proOff;
    mSubSymbol->setRadius(vec.length());
    break;
  case 5:
    mSubSymbol->setTextNode(txtPt+proOff);
    break;
  }

  return Acad::eOk;

} 

Acad::ErrorStatus
TestDetail::subGetGripPoints(AcGePoint3dArray& gripPoints,
                     AcDbIntArray& osnapModes, //osnapMasks
                     AcDbIntArray&  geomIds/*geomIds*/) const
{
  assertReadEnabled();
  AcGePoint3d orig = mSubSymbol->origin();
  double rad =  mSubSymbol->radius();
  
  gripPoints.append(orig);
  gripPoints.append(orig + rad*xDir());
  gripPoints.append(orig + rad*yDir());
  gripPoints.append(orig - rad*xDir());
  gripPoints.append(orig - rad*yDir());
  gripPoints.append(mSubSymbol->textNode());

  return Acad::eOk;
}

Acad::ErrorStatus   
TestDetail::subGetGripPoints(AcDbGripDataPtrArray& grips,
								  const double curViewUnitSize, 
								  const int gripSize, 
								  const AcGeVector3d& curViewDir, 
								  const int bitflags) const
{
	return Acad::eNotImplementedYet;
}

Acad::ErrorStatus
TestDetail::subExplode(AcDbVoidPtrArray& entitySet) const
{
  assertReadEnabled();

  Acad::ErrorStatus es = AcmSymbol::subExplode(entitySet);
  if (es != Acad::eOk) return es;

  AcmCAuxEntArray subents;
  es = getSubEnts(subents);
  if( es!= Acad::eOk) return es;

  return subents.explode(entitySet,this);
}

Acad::ErrorStatus
TestDetail::subTransformBy(const AcGeMatrix3d& xform)
{
  AcmSymbol::subTransformBy(xform);
  mSubSymbol->transformBy(xform);
  return Acad::eOk;
}

/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
LPCTSTR
TestDetailSub::value(Acm::Variable var) const
{
  return (LPCTSTR)mText;
}

Acm::ErrorStatus
TestDetailSub::getValue(Acm::Variable var, CString& value) const
{
  value = mText;
  return Acm::eOk;
}

Acm::ErrorStatus
TestDetailSub::setValue(Acm::Variable var, LPCTSTR value)
{
  mText = value;
  return Acm::eOk;
}

Acm::Visibility
TestDetailSub::variableStatus(Acm::Variable var) const
{
  return Acm::kVisible;
}

Acm::ErrorStatus
TestDetailSub::setVariableStatus(Acm::Variable var, Acm::Visibility status)
{
  return Acm::eOk;
}

Acm::ErrorStatus
TestDetailSub::getVarDlgInfo(Acm::Variable var,CVarDlgInfo& v) const
{
  v.setHelpIndex(mHelpIndex);
  v.setDlgIndex(0);
  v.setTooltip(mToolTip);
  v.setLabel(mLabel);
  return Acm::eOk;
}

Acm::ErrorStatus
TestDetailSub::setVarDlgInfo(Acm::Variable var,const CVarDlgInfo& v)
{
  mHelpIndex = v.helpIndex();
  mToolTip = v.tooltip();
  mLabel = v.label();
  return Acm::eOk;
}

Acad::ErrorStatus
TestDetailSub::dwgInFields(AcDbDwgFiler* filer,Adesk::UInt16 ver)  
{
  filer->readItem(&mOrigin);
  filer->readItem(&mTextNode);
  filer->readItem(&mRadius);
  TCHAR *tmp = NULL;
  filer->readItem(&tmp);
  mText = tmp;
  return filer->filerStatus();
}                     

Acad::ErrorStatus
TestDetailSub::dwgOutFields(AcDbDwgFiler* filer)  const
{
  filer->writeItem(mOrigin);
  filer->writeItem(mTextNode);
  filer->writeItem(mRadius);
  filer->writeItem((LPCTSTR)mText);
  return filer->filerStatus();
} 

Acad::ErrorStatus
TestDetailSub::dxfInFields(AcDbDxfFiler* filer)  
{
  return Acad::eNotImplementedYet;
}                     

Acad::ErrorStatus
TestDetailSub::dxfOutFields(AcDbDxfFiler* filer)  const
{
  return Acad::eNotImplementedYet;
} 

Acad::ErrorStatus 
TestDetailSub::transformBy(const AcGeMatrix3d& xform)
{
  mOrigin.transformBy(xform);
  mTextNode.transformBy(xform);
  mRadius *= xform.scale();
  return Acad::eOk;
}


///////////////////////////////////////////////////////////////////////////////////////////////
// Commands
//////////////////////////////////////////////////////////////////////////////////////////////
void addDetailStd()
{
  AcDbObjectId curStdId;
  acmStdMgr->getCurrent(curStdId);

  actrTransactionManager->startTransaction();

  AcDbObject* pObj;
  if( actrTransactionManager->getObject(pObj,curStdId,AcDb::kForWrite) != Acad::eOk) {
     actrTransactionManager->abortTransaction();
     return;
  }
  AcmStandard* pStd = AcmStandard::cast(pObj);
  if( !pStd ) {
     actrTransactionManager->abortTransaction();
     return;
  }

  TCHAR buff[134];
  acutPrintf(_T("\nCurrent standard is %s."), pStd->name());
  acedInitGet(0, _T("Yes No"));
  int res = acedGetKword(_T("\nDo you want to add support for TestDetail <Yes>/No :"),buff);
  if( res != RTNORM && res != RTNONE) {
     actrTransactionManager->abortTransaction();
     return;
  }

  if( _tcscmp(buff,_T("No")) == 0)  {
     actrTransactionManager->abortTransaction();
     return;
  }

  AcmSymbolStd *pSymStd = new TestDetailStd();

  if (Acad::eOk != pStd->registerSymbolStd(pSymStd, _T("TestDetailStd"), Adesk::kTrue, Adesk::kFalse, Adesk::kTrue))
  {
    acutPrintf(_T("\nError during registering new symbol standard."));
    delete pSymStd;
    actrTransactionManager->abortTransaction();
    return;
  }
  else { 
    acutPrintf(_T("\nTest Detail standard has been succesfully registered."));
    actrTransactionManager->addNewlyCreatedDBRObject(pSymStd);
    pSymStd->cInit(); 
  }
  
  actrTransactionManager->endTransaction();
}

void detail()
{

  TestDetail* pDetail = new TestDetail(); 
  
  assert(pDetail);
  if( pDetail->setSymbolDefaults() != Acm::eOk) {
    acutPrintf(_T("\nCannot setup detail symbol for current standard."));
    delete pDetail;
    return;
  }

  TestDetailSub *pSubSym = pDetail->subSymbol();
  pSubSym->setText(_T("A"));

  AcGePoint3d orig;

  int ret = acedGetPoint(NULL, _T("\nSelect Origin point :"),asDblArray(orig));
  if( ret == RTCAN || ret == RTNONE) {
    delete pDetail;
    return;
  }

  double rad;
  ret = acedGetDist(asDblArray(orig), _T("\nRadius of Detail :"),&rad);
  if( ret == RTCAN ) {
    delete pDetail;
    return;
  }

  AcGePoint3d textPt = orig + 1.05 * rad * pDetail->yDir();
  pSubSym->setRadius(rad);
  pSubSym->setOrigin(orig);
  pSubSym->setTextNode(textPt);

  // open a transaction for undo operation
  actrTransactionManager->startTransaction();

  // Post to Database
  //
  AcDbObjectId objId;
  postToDb(pDetail, objId);
  actrTransactionManager->addNewlyCreatedDBRObject(pDetail);

  actrTransactionManager->endTransaction();

}

void
editDetailStd()
{
  AcDbObjectId curStdId;
  acmStdMgr->getCurrent(curStdId);

  actrTransactionManager->startTransaction();

  AcDbObject* pObj;
  if( actrTransactionManager->getObject(pObj,curStdId,AcDb::kForWrite) != Acad::eOk) {
     actrTransactionManager->abortTransaction();
     return;
  }
  AcmStandard* pStd = AcmStandard::cast(pObj);
  if( !pStd ) {
     actrTransactionManager->abortTransaction();
     return;
  }

  AcDbObjectId symStdId;
  pStd->getCurrentSymbolStd(_T("TestDetailStd"), symStdId);
  if( actrTransactionManager->getObject(pObj,symStdId,AcDb::kForWrite) != Acad::eOk) {
     actrTransactionManager->abortTransaction();
     return;
  }
  const TestDetailStd* pTestDetailStd = TestDetailStd::cast(pObj);
  if( !pTestDetailStd ) {
     actrTransactionManager->abortTransaction();
     return;
  }

  bool OK = ACRX_X_CALL(pTestDetailStd, AcmCEditExt)->edit(pTestDetailStd);
  if (OK) {
    actrTransactionManager->endTransaction();
    CWaitCursor wait;
    acmStdMgr->updateSymbols();
  } 
  else {
    actrTransactionManager->endTransaction();
  }
  
}

// use this call back to populate standard with your implemented symbol
// standard
void 
TestDetailStdReactor::onNewStandard(AcmStandard *pNewStd)
{
  AcDbObjectIdArray idArray;
  Acm::ErrorStatus aes = pNewStd->getSymbolStd(_T("TestDetailStd"),idArray);
  if( aes == Acm::eOk && idArray.length() > 0) return;

  // we can also ask for type of standard in order to create popper class.
  TestDetailStd* pTestDetailStd = new TestDetailStd;
  if (Acad::eOk != pNewStd->registerSymbolStd(pTestDetailStd, _T("TestDetailStd"), Adesk::kTrue, Adesk::kFalse, Adesk::kFalse)) {
      delete pTestDetailStd;
  }
  else {
    pTestDetailStd->cInit();
    pTestDetailStd->close(); // safe, because of the new object !
  }

  return;
}

void 
TestDetailStdReactor::onChanged(const AcmStandard* pStd, Acm::StdVariable var)
{
  acutPrintf(_T("\n%s Standard changed %d value."),pStd->name(),var);
}

void 
TestDetailStdReactor::onChanged(const AcmSymbolStd* pSymStd)
{
  acutPrintf(_T("\n'%s' Standard changed."),pSymStd->isA()->name());
}

void 
TestDetailStdReactor::onRemoved(const AcmStandard* pStd)
{
  acutPrintf(_T("\n%s Standard erased."),pStd->name());
}

