//////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2025 Autodesk, Inc.  All rights reserved.
//
//  Use of this software is subject to the terms of the Autodesk license 
//  agreement provided at the time of installation or download, or which 
//  otherwise accompanies this software in either electronic or hard copy form.   
//
//////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <cstringt.h>
#include <tchar.h>

//AcadM
//////////////////////////////////////////////////////////////////////////
//You must add the following lines before including the "acm.h" file
//1. import defines for AmgDim crx module 
#define ACMSYMBB_CRX_DLLSHARE
#define ACMSYMBB_CRX_DLLSHARE_DATA __declspec(dllimport)
//2. declare new DbRef define
#undef  DBREF_DECLARE
#define DBREF_DECLARE(AcDbType)

#include "acm.h"
//////////////////////////////////////////////////////////////////////////
#include "acmdef.h"
#include "cauxent.h"
#include "stdMgr.h"
#include "symstd.h"
#include "symbol.h"
#include "notesym.h"
#include "NoteStd.h"

#include "mibase.h"
#include "migeom.h"
#include "miStatus.h"

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

//MCAD API
AmiStatus amiPick2 ( TCHAR* prompt, AmiKeyType type, struct resbuf* entmask,
                    AmiPickObj& pickObj, Adesk::Boolean allowGhost );
AmiStatus amiGetKeyFromPick ( AmiPickObj* pPickObj, AmiKeyType& type, 
                             AmiObjectKey*& pObjKey );

void setNoteValues(AmdtNote*& pNote)
{
  //create note text
  //this function need to be called after adding leader and before setting content of note text
  pNote->createNote(ACRX_T("Test"));

  //get note standard
  AcDbObjectId noteStdID = pNote->serverId();
  AcDbObjectPointer<AmdtNoteStd> pNoteStd(noteStdID, AcDb::kForWrite);
  if (pNoteStd.openStatus() != Acad::eOk) {
    acutPrintf(ACRX_T("Fail to open Note Standard!"));
    return;
  }

  //setup the feature type that the note symbol is attached to
  //get the template groups that are compatible with this feature type
  AcArray<Adesk::Int32> featTypes = pNoteStd->featTypes(pNote);
  pNoteStd->setCompatibilityCondition(featTypes);
  pNote->subSymbol()->setTemplateType(featTypes);

  //get the first template group
  bool succeeded = false;
  AcArray<Adesk::Int32> localGroupType;
  CString localGroupName;
  succeeded = pNoteStd->getGroup(0, localGroupType, localGroupName, true);
  if (!succeeded) {
    acutPrintf(ACRX_T("Fail to get template group of note standard!"));
    return;
  }

  //get all templates of specified template group
  AcArray<LPCTSTR> localTemplateNames, localTemplateValues;
  succeeded = pNoteStd->getAllTemplates(localGroupType, localTemplateNames, localTemplateValues);
  if (!succeeded) {
    acutPrintf(ACRX_T("Fail to get all templates of specified template group!"));
    return;
  } 

  //set note text based on template
  if (localTemplateValues.length() != 0) {
    AcDbMText localText;
    localText.copyFrom(&(pNote->subSymbol()->getNoteText()));
    localText.setContents(localTemplateValues[0]);
    pNote->subSymbol()->setNoteText(localText);
  }

  //set leader and text justification
  pNote->setAttachPoint(AmdtNoteStd::kBottomText);
  pNote->setTextAlign(AmdtNoteStd::kLeft);

  //turn on reference line
  pNote->createLanding(true);
}

void createNoteSym()
{
  //select geometry to attach
  AmiPickObj pickObj;
  AmiObjectKey* pKey = NULL;
  Ami::KeyType type;
  AmiStatus as = Ami::eOk;
  as = amiPick2( _T("Select geometry to attach: "), Ami::curveKey, NULL, pickObj, Adesk::kFalse);
  if (as.success()) {
    as = amiGetKeyFromPick(&pickObj, type, pKey);
    if (as.failure()) {
      TSTDSTRING strErrText = as.text();
      acutPrintf(_T("\nFail to get selected geometry. %s"), strErrText.c_str());
    }
  }

  //create Note symbol on current UCS
  AmdtNote* pNote = new AmdtNote();
  if (pNote->setSymbolDefaults() != Acm::eOk) {
    acutPrintf(_T("\nFail to set default for Note symbol."));
    delete pNote;
    return;
  }

  //Post to Database
  AcDbObjectId objId;
  if (postToDb(pNote, objId) != Acad::eOk) {
    delete pNote;
    return;
  }

  //get leader points and add leader points
  AcGePoint3dArray ptArr;
  for (int i = 0; i<2; i++ ) {
    AcGePoint3d pt;
    int ret = ads_getpoint(NULL, _T("\nSelect point :"),asDblArray(pt));
    if( ret == RTCAN ) {
      ptArr.setLogicalLength(0);
      break;
    }
    if( ret == RTNONE ) break;
    ptArr.append(pt);
  }

  if( ptArr.length() == 0) {
    pNote->close();
    return;
  }

  if (ptArr.length() == 1) 
    pNote->setOrigin(ptArr[0]);

  if (ptArr.length() == 2) 
    pNote->addLeader(ptArr);

  //You can attach symbol now. attachGeomRef accept only keys derived from
  //AmiGeomKey.
  if (pKey) {
    Acm::ErrorStatus es = pNote->attachGeomRef(pKey, AcGePoint3d::kOrigin);
    if (Acm::eOk != es)
      acutPrintf(_T("\nFail to attach to geometry"));

    pKey->release();
  }

  //set note symbol properties.
  setNoteValues(pNote);

  pNote->close();
}

void editNoteStandard()
{
  //get current standard from standard manager.
  AcDbObjectId curStdId;
  acmStdMgr->getCurrent(curStdId);

  const AcDbObjectPointer<AcmStandard> pStd(curStdId, AcDb::kForRead);
  if (Acad::eOk != pStd.openStatus())
    return;

  //get note symbol standard from standard manager.
  AcDbObjectId noteStdId;
  if (Acad::eOk != pStd->getCurrentSymbolStd(ACRX_T("AmdtNoteStd"), noteStdId)) {
    acutPrintf(ACRX_T("The note symbol is not supported by %s standard!"), pStd->name());
    return;
  }

  //open note standard object for writing.
  AcDbObjectPointer<AmdtNoteStd> pNoteStd(noteStdId, AcDb::kForWrite);
  if (Acad::eOk != pNoteStd.openStatus()) {
    acutPrintf(ACRX_T("Edge standard can not be opened!\n"));
    return;
  }

  int numGroups = pNoteStd->numGroups(false);
  
  //get the template group for HOLE DRILLED
  bool succeeded = false;
  AcArray<Adesk::Int32> groupType;
  CString groupName;
  for (int i=0; i<numGroups; i++) {
    //pass in false to onlyCompatible parameter to get all template groups
    if (pNoteStd->getGroup(i, groupType, groupName)) {
      if (groupName.CompareNoCase(_T("HOLE DRILLED")) == 0) {
        succeeded = true;
        break;
      }
    }
  }

  if (!succeeded) {
    acutPrintf(ACRX_T("Fail to get template group of note standard!"));
    return;
  }

  //add a new template
  pNoteStd->addTemplate(groupType, _T("Test Template"), _T("=<HOLE:DIA><HOLE:DESC>"));

  //get all templates of specified template group
  AcArray<LPCTSTR> templateNames, templateValues;
  succeeded = pNoteStd->getAllTemplates(groupType, templateNames, templateValues);
  if (!succeeded) {
    acutPrintf(ACRX_T("Fail to get all templates of specified template group!"));
    return;
  } 

  //print the template name and values for the specified template group
  acutPrintf(_T("\nList of templates for %s: "), groupName);
  for (int i =0; i<templateNames.length(); i++) {
    acutPrintf(_T("\nTemplate name: %s   Template value: %s"), templateNames[i], templateValues[i]);   
  }
}

Acad::ErrorStatus postToDb(AcDbEntity* ent, AcDbObjectId& objId)
{
  Acad::ErrorStatus      es;
  AcDbBlockTable*        pBlockTable;
  AcDbBlockTableRecord*  pSpaceRecord;

  if ((es = acdbHostApplicationServices()->workingDatabase()->getBlockTable(pBlockTable, AcDb::kForRead)) != Acad::eOk) return es;

  //  Determine appropriate space from AutoCAD core function isPspace()
  if ((es = pBlockTable->getAt(ACDB_MODEL_SPACE,pSpaceRecord, AcDb::kForWrite)) != Acad::eOk) return es;
  if ((es = pBlockTable->close()) != Acad::eOk) return es;
  if ((es = pSpaceRecord->appendAcDbEntity(objId, ent)) != Acad::eOk) return es;
  // be carefull, entity remains opened.
  return pSpaceRecord->close();
}
