//////////////////////////////////////////////////////////////////////////////
//
//  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 "acm.h"
#include "acmdef.h"
#include "cauxent.h"
#include "symstd.h"
#include "symbol.h"
#include "datumtgt.h"

//MCAD API
#include "mibase.h"
#include "migeom.h"
#include "miStatus.h"

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

Acad::ErrorStatus postToDb(AcDbEntity* ent, AcDbObjectId& objId);
void setDefValues(AcmDatumTarget* pDatumTgt);
int pickGeom(AmiObjectKey*& pGeom);
int setupLeader(AcmDatumTarget* pDatumTgt);
int placeAndPost(AcmDatumTarget* pDatumTgt, AcDbObjectId& objId);
int selectDatumtgtToAttach(AcmDatumTarget* pDatumTgt, AcDbObjectId& objId);

void datumNoTgt()
{
  // creates a new datum target object.
  AcmDatumTarget *pDatumTgt = new AcmDatumTarget(Acm::kNoTarget);
  if(!pDatumTgt)
    return;

  // set the default values for the datum target object.
  setDefValues(pDatumTgt);

  // place the datum target to modal space and post it to database.
  AcDbObjectId objId;
  if(RTNORM != placeAndPost(pDatumTgt,objId)){
    delete pDatumTgt;
    return;
  }

  pDatumTgt->close();
}

void datumLineTgt()
{
  AcmDatumTarget *pDatumTgt = new AcmDatumTarget(Acm::kLine);
  if(!pDatumTgt)
    return;

  // set the default values for the datum target object.
  setDefValues(pDatumTgt);

  // prompt to pick a geometry to attach
  AmiObjectKey* pKey = NULL;
  if(pickGeom(pKey) != RTNORM)
    return;

  // prompt to select start point and end point of line
  AcGePoint3dArray ptArr;
  TCHAR* strs[] = {_T("select first line point:"), _T("select second line point")};
  for( int i = 0; i<2; i++ ) {
    AcGePoint3d pt;
    if (acedGetPoint(NULL, strs[i], asDblArray(pt)) != RTNORM){
      delete pDatumTgt;
      pDatumTgt = NULL;
      return;
    }
    ptArr.append(pt);
  }
  
  // specify the start point and end point for line target.
  AcmCTarget *pTargetServer = pDatumTgt->target();
  AcmCTargetLine* pTargetLine = (AcmCTargetLine*)pTargetServer;
  if(!pTargetLine){
    delete pDatumTgt;
    return;
  }
  pTargetLine->setOrigin(ptArr[0]);
  pTargetLine->setEndPoint(ptArr[1]);

  // place the datum target to modal space and post it to database.
  if(setupLeader(pDatumTgt) != RTNORM)
    return;
  AcDbObjectId objId;
  if(Acad::eOk != postToDb(pDatumTgt, objId))
    return;
  if(pKey){
    Acm::ErrorStatus es = pDatumTgt->attachGeomRef(pKey, AcGePoint3d::kOrigin);
    pKey->release();
    if(Acm::eOk != es)
      return;
  }

  pDatumTgt->close();
}

void datumPointTgt()
{
  AcmDatumTarget *pDatumTgt = new AcmDatumTarget(Acm::kPoint);
  if(!pDatumTgt)
    return;

  // set the default values for the datum target object.
  setDefValues(pDatumTgt);

  // place the datum target to modal space and post it to database.
  AcDbObjectId objId;
  if(RTNORM != placeAndPost(pDatumTgt,objId)){
    delete pDatumTgt;
    pDatumTgt = NULL;
    return;
  }

  pDatumTgt->close();
}

void datumRectangularTgt()
{
  AcmDatumTarget* pDatumTgt = new AcmDatumTarget(Acm::kRectangular);
  if(!pDatumTgt)
    return;

  // set the default values for the datum target object.
  setDefValues(pDatumTgt);

  // get the rectangular area target pointer
  AcmCTarget *pTargetServer = pDatumTgt->target();
  AcmCTargetRectangularArea* pRectTarget = (AcmCTargetRectangularArea*)pTargetServer;
  if(!pRectTarget){
    delete pDatumTgt;
    return;
  }

  // prompt to pick a geometry to attach
  AmiObjectKey* pKey = NULL;
  if(RTNORM != pickGeom(pKey))
    return;

  // prompt to select center point
  int res;
  AcGePoint3d pt;
  res = acedGetPoint(NULL, _T("select center point:"), asDblArray(pt));
  if(res != RTNORM){
    delete pDatumTgt;
    return;
  }
  pRectTarget->setOrigin(pt);

  // prompt to select corner point
  res = acedGetPoint(NULL, _T("\nselect corner point:"), asDblArray(pt));
  if(res != RTNORM){
    delete pDatumTgt;
    return;
  }
  pRectTarget->setCornerPoint(pt);

  // place the datum target to modal space and post it to database.
  if(RTNORM != setupLeader(pDatumTgt))
    return;
  AcDbObjectId objId;
  if(Acad::eOk != postToDb(pDatumTgt, objId))
    return;
  if(pKey){
    Acm::ErrorStatus es = pDatumTgt->attachGeomRef(pKey, AcGePoint3d::kOrigin);
    pKey->release();
    if(Acm::eOk != es)
      return;
  }
  pDatumTgt->close();
}

void datumCircularTgt()
{
  AcmDatumTarget* pDatumTgt = new AcmDatumTarget(Acm::kCircular);
  if(!pDatumTgt)
    return;

  // set the default values for the datum target object.
  setDefValues(pDatumTgt);

  // get the rectangular area target pointer
  AcmCTarget *pTargetServer = pDatumTgt->target();
  AcmCTargetCircularArea* pCircularTarget = (AcmCTargetCircularArea*)pTargetServer;
  if(!pCircularTarget){
    delete pDatumTgt;
    return;
  }

  // prompt to select a geometry to attach
  AmiObjectKey* pKey = NULL;
  if(RTNORM != pickGeom(pKey))
    return;

  // prompt to select center point
  int res;
  AcGePoint3d pt;
  res = acedGetPoint(NULL, _T("select center point:"), asDblArray(pt));
  if(res != RTNORM){
    delete pDatumTgt;
    return;
  }
  pCircularTarget->setOrigin(pt);

  // prompt to select corner point
  ads_real dis = 0.0;
  res = acedGetDist(asDblArray(pt), _T("\nRadius:"), &dis);
  if(res != RTNORM){
    delete pDatumTgt;
    return;
  }
  pCircularTarget->setRadius(dis);

  // place the datum target to modal space and post it to database.
  if(setupLeader(pDatumTgt) != RTNORM)
    return;
  AcDbObjectId objId;
  if(postToDb(pDatumTgt, objId) != Acad::eOk)
    return;
  if(pKey){
    Acm::ErrorStatus es = pDatumTgt->attachGeomRef(pKey, AcGePoint3d::kOrigin);
    pKey->release();
    if(Acm::eOk != es)
      return;
  }

  pDatumTgt->close();
}

void datumAttachTgt()
{
  AcmDatumTarget* pDatumTgt = new AcmDatumTarget(Acm::kAttach);
  if(!pDatumTgt)
    return;

  // set the default values for the datum target object.
  setDefValues(pDatumTgt);

  // place the datum target to modal space and post it to database.
  AcDbObjectId objId;
  if(RTNORM != selectDatumtgtToAttach(pDatumTgt,objId))
    return;

  pDatumTgt->close();
}

void setDefValues(AcmDatumTarget* pDatumTgt)
{
  if(!pDatumTgt)
    return;

  // initialize the new datum target to default state.
  pDatumTgt->setSymbolDefaults(); // will create data id, because we didn't initialize it.

  // set default datum value and dimension value.
  pDatumTgt->balloon()->setValue(Acm::DatumTargetSize,_T(/*MSG0*/"1"));
  pDatumTgt->balloon()->setValue(Acm::DatumTargetText,_T(/*MSG0*/"A"));
}

int pickGeom(AmiObjectKey*& pGeom)
{
  // prompt to select a object to attach.
  pGeom = NULL;
  AmiPickObj pickObj;
  Ami::KeyType type;
  AmiStatus as = Ami::eOk;
  as = amiPick2( _T("Select geometry:"), Ami::curveKey, NULL, pickObj, Adesk::kFalse);
  if(!as.success())
    return RTNORM;
  as = amiGetKeyFromPick(&pickObj, type, pGeom);
  if (as.failure()) {
    TSTDSTRING strErrText = as.text();
    acutPrintf(_T("\nFail to get selected geometry. %s"), strErrText.c_str());
    return RTERROR;
  }

  return RTNORM;
}

int setupLeader(AcmDatumTarget* pDatumTgt)
{
  if(!pDatumTgt)
    return RTERROR;

  // get starting and ending point of leader
  int res;
  AcGePoint3dArray ptArr;
  for( int i = 0; i<2; i++ ) {
    AcGePoint3d pt;
    res = acedGetPoint(NULL, _T("\nSelect point :"),asDblArray(pt));
    if (res != RTNORM){
      return res;
    }
    ptArr.append(pt);
  }

  if(Acm::eOk != pDatumTgt->addLeader(ptArr))
    return RTERROR;

  return RTNORM;
}

int placeAndPost(AcmDatumTarget* pDatumTgt, AcDbObjectId& objId)
{
  if(!pDatumTgt)
    return RTERROR;

  // prompt to select a object to attach.
  AmiObjectKey* pKey = NULL;
  int res = pickGeom(pKey);
  if(res != RTNORM)
    return res;

  res = setupLeader(pDatumTgt);
  if(res != RTNORM)
    return res;

  // post the datum target object to database.
  if (postToDb(pDatumTgt, objId) != Acad::eOk) {
    return RTERROR;
  }

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

  return RTNORM;
}

int selectDatumtgtToAttach(AcmDatumTarget* pDatumTgt, AcDbObjectId& objId)
{
  if(!pDatumTgt)
    return RTERROR;

  //select a datum target object to attach
  ads_name ent;
  AcGePoint3d pickPoint;
  int res = ::acedEntSel(_ACRX_T("select datum target to attach"), ent, asDblArray(pickPoint));
  if(RTNORM != res){
    delete pDatumTgt;
    return res;
  }

  // check if the selected object is datum target, it it isn't, return error.
  AcDbObjectId id;
  AcDbEntity* pEnt = NULL;
  if(Acad::eOk != ::acdbGetObjectId(id, ent)){
    delete pDatumTgt;
    return RTERROR;
  }
  if(Acad::eOk != acdbOpenAcDbEntity(pEnt, id, AcDb::kForRead)){
    delete pDatumTgt;
    return RTERROR;
  }
  if(!pEnt->isKindOf(AcmDatumTarget::desc())){
    delete pDatumTgt;
    return RTERROR;
  }

  AcmDatumTarget* pTemp = AcmDatumTarget::cast(pEnt);
  if(!pTemp){
    delete pDatumTgt;
    return RTERROR;
  }

  // post the datum target object to database.
  if (postToDb(pDatumTgt, objId) != Acad::eOk) {
    delete pDatumTgt;
    return RTERROR;
  }

  if(!pDatumTgt->setTargetAttach(pTemp, Acm::kAttachLeft))
    return RTERROR;

  return RTNORM;
}