//////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2023 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 "resource.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 "AcmTitleStd.h"
#include "stdmgr.h"
#include "symstd.h"
#include <map>
#include <vector>

void setTitleAttribute(AcMgTitleBorder* pTitBrd, const ACHAR* key, const ACHAR* val) 
{ 
  // Get title and border which are properties of TitleBorder
  AcMgTitle& pTit = pTitBrd->title();
  AcMgBorder& pBrd = pTitBrd->border();

  // Get title data corresponding with the key
  AcMgTitleDatum *pDat = pTit.datumAt(key);
  AcMgTitleString *pStrDat = NULL;
  
  if (NULL != pDat)
  {
    if (NULL != (pStrDat = dynamic_cast<AcMgTitleString *>(pDat)))
      pStrDat->setValue(val);
  } // else look inside border data
  else if (NULL != (pDat = pBrd.datumAt(key)))
  {
    if (NULL != (pStrDat = dynamic_cast<AcMgTitleString *>(pDat)))
      pStrDat->setValue(val);
  }
  else // add to title data
  {
    pStrDat = (AcMgTitleString *)pAcMgTitleBorderMgr->NewDatum(key, val);
    if (!pTitBrd->IsTitleDeactivated())
      pTit.insertDatum(pStrDat);
    else
      pBrd.insertDatum(pStrDat);
    //delete pStrDat;   // crash here
  }
}

void createTitleBorder()
{
  // Get TitleBorderStandard to set this new TitleBorder
  AcDbObjectId stdId;
  if (acmStdMgr->getCurrent(stdId) != Acm::eOk)
    return;

  AcmStandard* pStandard;
  if (acdbOpenObject(pStandard, stdId, AcDb::kForRead) != Acad::eOk)
    return;

  AcDbObjectId titStdId;  
  if(pStandard->getCurrentSymbolStd(_T("AcAmgTitleStd"), titStdId) != Acad::eOk){
    pStandard->close();
    return;
  }

  pStandard->close();

  // Get title border standard pointer
  AcAmgTitleStd* pAcAmgTitleStd = NULL;
  if (acdbOpenObject(pAcAmgTitleStd, titStdId, AcDb::kForRead))
    return;

  // Check current standard is ANSI
  CString baseStd = pAcAmgTitleStd->BaseStd();
  if (baseStd != _T("ANSI")){
    acutPrintf(_T("CreateTitleBorder Command can only run in ANSI Standard! Please set standard as ANSI and try again"));
    pAcAmgTitleStd->close();
    return;
  }
  
  // Create new setting object to set the properties of TitleBorder
  AcMgTitleSetting* pSetting = pAcMgTitleBorderMgr->NewSetting();
  if (pSetting == NULL){
    pAcAmgTitleStd->close();
    return;
  }

  // Create new descriptor object to apply to create TitleBorder
  AcMgTitleBorder* pTitBrd = pAcMgTitleBorderMgr->NewDescriptor();
  if (pTitBrd == NULL){
    pAcAmgTitleStd->close();
    delete pSetting;
    return;
  }
  // Add reference count
  pTitBrd->addRef();

  // Create new context object to apply to create TitleBorder
  AcMgTitleOpSettings* pContext = pAcMgTitleBorderMgr->NewContext();
  if (pContext == NULL){
    pAcAmgTitleStd->close();
    pTitBrd->release();
    delete pSetting;
    return;
  }
  // Add reference count
  pContext->addRef();

  // Set drawing border to "A (8.5x11.0inch)"
  AcMgBorder& pBrd = pTitBrd->border();
  CMapStringToString *pBorderMap = NULL;
  pAcAmgTitleStd->getBorderMap(pBorderMap);
  if (NULL != pBorderMap){
    CString szBorderName;
    pBorderMap->Lookup(_T("A (8.5x11.0inch)"), szBorderName);
    pBrd.setName(szBorderName);
  }
  else
    pBrd.setName(_ACRX_T("ANSI_A"));

  pSetting->setValue(pBrd.Name());
  pContext->setBorder(*pSetting);

  // Set title block to "ANSI Title Block A"
  AcMgTitle& pTit = pTitBrd->title();
  CMapStringToString *pTitMap = NULL;
  pAcAmgTitleStd->getTitleMap(pTitMap);
  if (NULL != pTitMap){
    CString szTitName;
    pTitMap->Lookup(_T("Ansi Title Block A"), szTitName);
    pTit.setName(szTitName);
  }
  else
    pTit.setName(_ACRX_T("ANSI_TITLEA"));

  pSetting->setValue(pTit.Name());
  pContext->setTitleBlock(*pSetting);
  
  // Close TitleBorderStandard
  pAcAmgTitleStd->close();

  // Set scale
  pTitBrd->setScaleVal(0.5);
  pSetting->setValue(pTitBrd->scale());
  pContext->setScale(*pSetting);

  // Set position
  ads_point point;
  acedGetPoint(NULL, _T("\nPlease select a point as the position of titleborder:"), point);
  pTitBrd->setPosition(AcGePoint3d(point[0], point[2], point[3]));

  // Retrieve from assembly property
  pSetting->setValue(true);
  pContext->setRetrieveA(*pSetting);

  // Move circle
  Acad::ErrorStatus es ;
  AcDbObjectIdArray moveObjIds;
  AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase();

  // Get block table  
  AcDbBlockTable *pBtbl;
  if ((es = pDb->getSymbolTable(pBtbl, AcDb::kForRead)) != Acad::eOk){
    pTitBrd->release();
    pContext->release();
    delete pSetting;
    return;
  }

  // Get block table record
  AcDbBlockTableRecord *pBtblRcd;
  if ((es = pBtbl->getAt(ACDB_MODEL_SPACE, pBtblRcd, AcDb::kForWrite)) != Acad::eOk){
    pBtbl->close();
    pTitBrd->release();
    pContext->release();
    delete pSetting;
    return;
  }
  
  pBtbl->close();
  
  // Append circle1
  AcDbCircle *pCir1 = new AcDbCircle(AcGePoint3d(2,2,2),AcGeVector3d(0,0,1),0.5);
  if ((es = pBtblRcd->appendAcDbEntity(pCir1)) != Acad::eOk){
    pBtblRcd->close();
    pTitBrd->release();
    pContext->release();
    delete pSetting;
    delete pCir1;
    return;
  }
  
  AcDbObjectId cir1ObjId = pCir1->objectId();
  moveObjIds.append(cir1ObjId);
  pCir1->close();
  
  // Append circle2
  AcDbCircle *pCir2 = new AcDbCircle(AcGePoint3d(4,4,4),AcGeVector3d(0,0,1),0.5);
  if ((es = pBtblRcd->appendAcDbEntity(pCir2)) != Acad::eOk){
    // release resource
    pBtblRcd->close();
    pTitBrd->release();
    pContext->release();
    delete pSetting;
    delete pCir1;
    delete pCir2;
    return;
  }

  AcDbObjectId cir2ObjId = pCir2->objectId();
  moveObjIds.append(cir2ObjId);
  pCir2->close();
  pBtblRcd->close();

 // Set move and automatic placement
  pSetting->setValue(true);
  pContext->setMove(*pSetting/*, moveObjIds, AcGePoint3d(150,250,0)*/);
  pContext->setMoveObjects(moveObjIds, AcGePoint3d(150,250,0));
  pSetting->setValue(true);
  pContext->setAutomaticPlacement(*pSetting);

  // Specify no user input
  pContext->enableUserInput(false);

  // Create title border
  AcDbObjectId titleBorderId = pAcMgTitleBorderMgr->CreateTitleBorder(pTitBrd, pContext);
 
  // Release resource
  pTitBrd->release();
  pContext->release();
  delete pSetting;
}


bool getTitleBorders(AcDbObjectId*& pObjIds, int& size)
{
// Step1 get all TitleBorders by Iterator

  // Create new TitleBorder iterator to get TitleBorders
  AcMgTitleSpaceIterator *pIt = pAcMgTitleBorderMgr->newAmgTitleSpaceIterator();
  if (pIt ==NULL){
    return false;
  }

  AcDbObjectId brdObjId, curBrdObjId;
  int titBrdCount = pIt->count();

  // There are no TitleBorder in the current drawing
  if (titBrdCount == 0){
    delete pIt;
    pObjIds = NULL;
    size = 0;
    return true;
  }

  // There are only one TitleBorder in the current drawing
  if (titBrdCount == 1){
    if (Acad::eOk != pIt->current(curBrdObjId)){
      delete pIt;
      return false;
    }

    pObjIds = new AcDbObjectId[1];
    pObjIds[0] = curBrdObjId;
    size = 1;
    delete pIt;
    return true;
  }

// When more than one TitleBorders in the current drawing, get TitleBorders by Selection Set
  typedef std::pair <AcDbObjectId, AcDbObjectId> AcDbObjectId_Pair;
  std::multimap<AcDbObjectId, AcDbObjectId> titBrdMap;
  std::multimap<AcDbObjectId, AcDbObjectId>::iterator titBrdMap_pIter;
  for (int i=0; i<titBrdCount; i++)
  {
    // get objectId of TitleBorder
    if (Acad::eOk != pIt->item(i)){
      delete pIt;
      return false;
    }

    if (Acad::eOk != pIt->current(curBrdObjId)){
      delete pIt;
      return false;
    }

    // get TitleBorder pointer by its id
    AcMgTitleBorder *pTitBrd = pAcMgTitleBorderMgr->GetTitleBorderFromObj(curBrdObjId);
    if (NULL == pTitBrd) {
      delete pIt;
      return false;
    }

    //add reference count with one
    pTitBrd->addRef();

    // put title and border id into a array
    titBrdMap.insert(AcDbObjectId_Pair(curBrdObjId, pTitBrd->title().ObjectId()));
    titBrdMap.insert(AcDbObjectId_Pair(curBrdObjId, pTitBrd->border().ObjectId()));
    
    // release resource
    pTitBrd->release();
  }

  //delete resource when no longer use
  delete pIt;

  // Allow user to select one or more TitleBorders by Selection Set
  acutPrintf(_T("There are %d titleborders in current drawing.\n"), titBrdCount);

  // Get Selection Sets
  Adesk::Int32 len;
  ads_name ssname, ent; 
  acedSSGet(NULL,NULL,NULL,NULL,ssname); 	
  if (acedSSLength(ssname, &len) != RTNORM){
    return FALSE;
  }

  std::vector<AcDbObjectId> objIds;
  std::vector<AcDbObjectId>::iterator  objIds_PIter;

  // Iterate Selection Sets 
  for(long i=0; i<len; i++) 
  {  
    // Get Selection Sets name
    acedSSName(ssname, i, ent);
    acdbGetObjectId(brdObjId, ent); 
    if (!brdObjId.isValid())
      continue;

    // Get TitleBorder objectId by title or border which are selected
    for ( titBrdMap_pIter=titBrdMap.begin(); titBrdMap_pIter!=titBrdMap.end(); titBrdMap_pIter++ ){
      if (titBrdMap_pIter->second == brdObjId) {
         bool hasPush = false;

         // In order to avoid insert the same TitleBorder objectId, need check it
         for (objIds_PIter=objIds.begin(); objIds_PIter!=objIds.end(); objIds_PIter++)
         {
           if (*objIds_PIter == titBrdMap_pIter->first){
             hasPush = true;
             break;
           }
         }
         
         // if objectId of the TitleBorder selected has not been pushed in to Vector, push back it.
         if (!hasPush){
           objIds.push_back(titBrdMap_pIter->first);
           size++;
         }

        break;
      }//end if
    }// end for
   }// end for Iterate Selection Sets

  // Copy title border object ids from set into a new array, don't forget to delete it after calling
  int j =0;
  pObjIds = new AcDbObjectId[size];
  for (std::vector<AcDbObjectId>::iterator it = objIds.begin(); it != objIds.end(); it++){
    pObjIds[j] = *it;
    j++;
  }

  return true;
}

void deleteTitleBorder()
{
  AcDbObjectId* brdObjIds = NULL;
  int size = 0;

  // Get title and borders
  bool ret = getTitleBorders(brdObjIds, size);
  if (!ret){
    delete brdObjIds;
    acutPrintf(_T("Selete TitleBorder Unsuccessfully \n"));
    return ;
  }

  if (size == 0){
    delete brdObjIds;
    acutPrintf(_T(" No or invalid TitleBorder selected \n"));
    return;
  }
  
  // Iterator TitleBorder Array
  AcDbObjectId brdObjId;
  for (int i=0; i< size; i++)
  {
    brdObjId = brdObjIds[i];
    if (!brdObjId.isValid())
      continue;

    // Delete TitleBorder
    bool isDel =  pAcMgTitleBorderMgr->DeleteTitleBorder(brdObjId);
    if (isDel)
      acutPrintf(_T("Delete TitleBorder Successfully \n"));
    else
      acutPrintf(_T("Delete TitleBorder Unsuccessfully \n"));
  }
  
  // At last, Don't forget delete object id array which is created inside the method getTitleBorders.
  delete brdObjIds;
}

void modifyTitleBorder()
{
  AcDbObjectId* brdObjIds = NULL;
  int size = 0;

  // Get title and borders by Selection Sets 
  bool ret = getTitleBorders(brdObjIds, size);
  if (!ret){
    delete brdObjIds;
    acutPrintf(_T("Selete TitleBorder Unsuccessfully \n"));
    return ;
  }

  if (size == 0){
    delete brdObjIds;
    acutPrintf(_T(" No or valid TitleBorder selected \n"));
    return;
  }

  AcDbObjectId brdObjId;
  AcMgTitleSetting* pSetting;
  AcMgTitleOpSettings* pContext;
  AcMgTitleBorder *pTitBrd;

  // Iterator TitleBorder Array
  for (int i=0; i< size; i++)
  {
    // Get title border to be modified
    brdObjId = brdObjIds[i];
    if (!brdObjId.isValid()) {
      delete brdObjIds;
      return;
    }

    // Create new setting object to set the properties of TitleBorder
    pSetting = pAcMgTitleBorderMgr->NewSetting();
    if (NULL == pSetting) {
      delete brdObjIds;
      return;
    }

    // Create new context object and apply it to modify the TitleBorder object
    pContext = pAcMgTitleBorderMgr->NewContext();
    if (NULL == pContext) {
      delete brdObjIds;
      delete pSetting;
      return;
    }

    //add reference count with one
    pContext->addRef();

    // Get title border pointer from its object id
    pTitBrd = pAcMgTitleBorderMgr->GetTitleBorderFromObj(brdObjId);
    if (NULL == pTitBrd) {
      delete brdObjIds;
      delete pSetting;
      pContext->release();
      return;
    }

    //add reference count with one
    pTitBrd->addRef();

    // Set attributes
    setTitleAttribute(pTitBrd, _ACRX_T("GEN-TITLE-NAME"), _ACRX_T("New Name"));
    setTitleAttribute(pTitBrd, _ACRX_T("GEN-TITLE-DES1"), _ACRX_T("New Description"));

    // Modify drawing border
    pSetting->setValue(_ACRX_T("ANSI_B"));
    pContext->setBorder(*pSetting);

    // Modify title block
    pSetting->setValue(_ACRX_T("ANSI_TITLEAC"));
    pContext->setTitleBlock(*pSetting);

    // Modify scale
    pContext->setScale(_ACRX_T("1:10"));

    // Specify no user input
    pContext->enableBorderInput(false);
    pContext->enableUserInput(false);
    //apply the context object to the TitleBorder object
    Acad::ErrorStatus es;
    if ((es = pTitBrd->modify(pContext,false)) !=  Acad::eOk){
      pContext->release();
      pTitBrd->release();
      delete brdObjIds;
      delete pSetting;
      return;
    }

    // release resource when we don't need it
    pContext->release();
    pTitBrd->release();
    delete pSetting;
  }

  // Don't forget delete object id array which is created in calling method
  delete brdObjIds;
}

void editTitleStd()
{
  // Get title standard
  AcDbObjectId stdId;
  if (acmStdMgr->getCurrent(stdId) != Acm::eOk)
    return;

  AcmStandard* pStandard;
  if (acdbOpenObject(pStandard, stdId, AcDb::kForRead) != Acad::eOk)
    return;

  AcDbObjectId titStdId;  
  if(pStandard->getCurrentSymbolStd(_T("AcAmgTitleStd"), titStdId) != Acad::eOk){
    pStandard->close();
    return;
  }

  pStandard->close();
  
  // Get titleBorderStandard pointer
  AcAmgTitleStd* pAcAmgTitleStd = NULL;
  if (acdbOpenObject(pAcAmgTitleStd, titStdId, AcDb::kForRead))
    return;

  // Get base standard name
  CString baseStd = pAcAmgTitleStd->BaseStd();
  acutPrintf(_T("Title standard base std = %s\n"), baseStd);

  // Get default title
  CString defTit = pAcAmgTitleStd->DefTitle();
  acutPrintf(_T("Title = %s\n"), defTit);

  // Get default border 
  CString defBrd = pAcAmgTitleStd->DefBorder();
  acutPrintf(_T("Border = %s\n"), defBrd);
  
  // Get default revision list
  CString defRev = pAcAmgTitleStd->DefRev();
  acutPrintf(_T("Revisionlist = %s\n"), defRev);
  
  // Get paper formats
  CMapStringToString *pBorderMap = NULL;
  pAcAmgTitleStd->getBorderMap(pBorderMap);
  if (NULL != pBorderMap)
  {
    POSITION pos = pBorderMap->GetStartPosition();
    while( pos != NULL )
    {
      CString value, key;
      pBorderMap->GetNextAssoc( pos, key,value );
      acutPrintf(_T("key = %s, Value = %s\n"), key, value);
    }
  }
  
  // Get title blocks
  CMapStringToString *pTitMap = NULL;
  pAcAmgTitleStd->getTitleMap(pTitMap);
  if (NULL != pTitMap)
  {
    POSITION pos = pTitMap->GetStartPosition();
    while( pos != NULL )
    {
      CString value, key;
      pTitMap->GetNextAssoc( pos, key,value );
      acutPrintf(_T("key = %s, Value = %s\n"), key, value);
    }
  }

  // Get revision lists
  CMapStringToString *pRevMap = NULL;
  pAcAmgTitleStd->getRevMap(pRevMap);
  if (NULL != pRevMap)
  {
    POSITION pos = pRevMap->GetStartPosition();
    while( pos != NULL )
    {
      CString value, key;
      pRevMap->GetNextAssoc( pos, key,value );
      acutPrintf(_T("key = %s, Value = %s\n"), key, value);
    }
  }
  
  pAcAmgTitleStd->close();
}