//////////////////////////////////////////////////////////////////////////////
//
//  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.   
//
//////////////////////////////////////////////////////////////////////////////
#ifndef __SYMBOL_H
#define __SYMBOL_H

class AcGiWorldDraw;
class AcGiViewportDraw;
class AcDbDwgFiler;
class AcDbDxfFiler;
class AcmStandard;
class AcmCSubSymbol;
class AcmCLeader;
class AcRfGeom;
class AcRfPickObj;
class AcmRefReactor;
class AcmDwgMgrSymbolXProtocol;
class AcmSymbolImp;
class AcmWelding;
//class AcmVariant;

///<summary>This class is the base class for all symbol classes in AutoCAD/M. It is derived from AcDbEntity
///and cannotbe used to create instances. It is mainly used to handle leaders and connections to the AcmStandard. 
///It also handles the sub-entities created by each particular symbol. Thus the class will hold the Imp object, 
///hiding the data from the user. The class interface implements the basic AcDbEntity interface, which is extended 
///to provide leader functionality, an interface for geometry associations through the leader, and symbol transformations.</summary>
class ACMSYMBB_CRX_DLLSHARE AcmSymbol : public AcDbEntity
{
  friend class AcmSymbbInternals;
public:
  ACRX_DECLARE_MEMBERS(AcmSymbol);
  ///<summary>Destructure the symbol class.</summary>
  virtual ~AcmSymbol();

  ///<summary>Uses this overloaded member function to set up a symbol properly.</summary>
  ///<param name="pDb">Pointer to database. Default is NULL, and host databse will be used.</param>
  ///<returns>If successful then Acm::eOk is returned. If the symbol standard is not valid, then Acm::eNotSupported 
  ///is returned.</returns>
  virtual Acm::ErrorStatus setSymbolDefaults(AcDbDatabase* pDb = NULL);

  ///<summary>Checks whether the symbol is attached to some geometry.</summary>
  bool isAttached() const;
  ///<summary>Detaches a geometry reference and frees it from memory.</summary>
  virtual Acm::ErrorStatus detachGeomRef();

  ///<summary>Attaches a geometry reference. This function returns Adesk::kFalse if there is a geometry reference 
  ///attached to the symbol already. In that case you need to use the leader iterator and specify more precisely to 
  ///which arrowhead they want to attach the geometry.</summary>
  ///<param name="pGeomRef"></param>
  ///<param name="viewId">Optional. If without the view Id, this function is used to create the geometry association
  ///in model space. The void pointer is the pointer to the MCAD API geometry key.</param>
  ///<param name="arrowheadLocation"></param>
  virtual Acm::ErrorStatus attachGeomRef(const void* pGeomRef,
                                         const AcDbObjectId &viewId,
                                         const AcGePoint3d &arrowheadLocation);

  ///<summary>Attaches a geometry reference. This function returns Adesk::kFalse if there is a geometry reference
  ///attached to the symbol already. In that case you need to use the leader iterator and specify more precisely 
  ///to which arrowhead they want to attach the geometry.</summary>
  virtual Acm::ErrorStatus attachGeomRef(const void* pGeomRef,
                                         const AcGePoint3d &arrowheadLocation);
  virtual Acad::ErrorStatus eraseGeomRefs();
  ///<summary>Returns the upper bound of GS Selection Markers (number of them) used by all leaders in this class.</summary>
  virtual INT_PTR numOfGsMarkers() const;
  ///<summary>Returns the number of grip points used by this class.</summary>
  virtual int numGripPoints() const;
  virtual Adesk::Boolean isKnownGrip(int indice) const;
  ///<summary>Resets the coordinate system used by the symbol.</summary>
  virtual void setCoordSystem(const AcGePoint3d&  origin,
                              const AcGeVector3d& xDir,
                              const AcGeVector3d& normal);
  virtual void getCoordSystem(AcGePoint3d&  origin,
                              AcGeVector3d& xDir,
                              AcGeVector3d& normal) const;
  ///<summary>Returns the origin of the defining plane.</summary>
  virtual AcGePoint3d origin() const;
  ///<summary>Sets the origin of the defining plane.</summary>
  virtual void setOrigin(const AcGePoint3d& origin);
  ///<summary>Returns the direction of the X-axis of the symbol's ECS in WCS.</summary>
  AcGeVector3d xDir() const;
  ///<summary>Returns the direction of the Y-axis of the symbol's ECS in WCS.</summary>
  AcGeVector3d yDir() const;
  ///<summary>Returns the normal of the defining plane.</summary>
  AcGeVector3d normal() const;
  ///<summary>Sets the scale factor for all sub-symbols (excluding leader).</summary>
  virtual void setScale(double scale);
  ///<summary>Returns the scale factor for all sub-symbols (excluding leader).</summary>
  virtual double scale() const;

  ///<summary>Returns the Id of the standard used to create the symbol.</summary>
  AcDbObjectId standardId() const;
  ///<summary>Sets up the standard Id for the symbol.</summary>
  Acad::ErrorStatus setStandardId(const AcDbObjectId& standardId);

  ///<summary>Establishes the connection to the server for a particular symbol.</summary>
  AcDbObjectId serverId() const;
  Acad::ErrorStatus setServerId(const AcDbObjectId& serverId);

  ///<summary>Establishes the connection to the standard agent for a particular symbol.</summary>
  AcDbObjectId agentId() const;
  Acad::ErrorStatus setAgentId(const AcDbObjectId& agentId);

  ///<summary>You should call this method only for the downgrade save progress 
  /// if you want to remove a reference to a standard agent. It reads the original symbol standard ID 
  /// from the agent and sets it to temporary member. Then the reference for the agent will be removed </summary>
  Acad::ErrorStatus removeAgentId();

  ///<summary>Creates the general leader interface.</summary>
  ///<param name="index">Specifies the index in the leader array. If the leader with the specified index doesn't exist, 
  ///this function returns a NULL.</param>
  AcmCLeader* getLeader(Adesk::UInt16 index) const;

  ///<summary>Return Surface indication leader if the symbol has it and laso supported it.</summary>
  virtual const AcmCLeader* getSurfIndLeader() const;
  virtual AcmCLeader* getSurfIndLeader();

  ///<summary>Creates a new leader using supplied points.</summary>
  virtual Acm::ErrorStatus addLeader(const AcGePoint3dArray &ptrArray);
  ///<summary>Provides the method for removing the nth- leader in the list.</summary>
  ///<param name="index">Specifies the index in the leader array. If the leader doesn't exist, the function returns
  ///eInvalidInput.</param>
  virtual Acm::ErrorStatus removeLeader(Adesk::UInt16 index);
  ///<summary>Provides the number of leaders present in the symbol.</summary>
  int numOfLeaders() const;
  ///<summary>Sends a notification if the tip point of the leader has changed.</summary>
  virtual void leaderTipPointChanged(const AcGeVector3d& offset,
                                     const AcmCLeader* leader);
  ///<summary>Sends a notification if the second tip point of the leader has changed.</summary>
  virtual void leaderSecondTipPointChanged(const AcGeVector3d& offset,
                                            const AcmCLeader* leader);
  ///<summary>Sends a notification if the landing of the leader has changed.</summary>
  virtual void leaderLandingChanged(const AcGeVector3d& offset,
                                    const AcmCLeader* leader);
  ///<summary>Returns a WCS vector projected into XY plane of the symbol's ECS.</summary>
  AcGeVector3d projectedVector(const AcGeVector3d& vec) const;
  ///<summary>Returns a WCS point projected into XY plane of the symbol's ECS.</summary>
  AcGePoint3d  projectedPoint(const AcGePoint3d& pt) const;
  ///<summary>This function is called during the updating of symbols that takes place when the standard is changed.</summary>
  virtual void regen();

  ///<summary>This function internally calls the regen function but some symbols can also do specific updates of symbol parameters.</summary>
  virtual void regenProperties();

  using AcDbEntity::transformBy;
  ///<summary>Transforms all leaders. Default implementation doesn't cover any smart behavior.</summary>
  virtual Acad::ErrorStatus transformBy(const AcGeMatrix3d& xform,
												 double symbolScale);

  ///symbol is attached to has been transformed.</summary>
  virtual Acad::ErrorStatus transformAssociatedSymbol(const AcGeMatrix3d& xform,  bool MDTViewMoveCxt); 
                                                                                  
  Acad::ErrorStatus convertToBlock(AcDbObject*& replaceObj,
                                   AcDbObjectId& replaceId);
  virtual Acad::ErrorStatus subHandOverTo(AcDbObject* pNewObject);

  virtual Acad::ErrorStatus dwgInFields(AcDbDwgFiler* filer);
  virtual Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* filer) const;

///////////////////////////////////////////////////////////////////////////////
//   Temporary disabled  for Xenon
//   //////////////////////////////////////////////////////////////////////////
//   // Attention!!!
//   // All ACM symbols that start to use the new serialization system mustn't override 
//   // the original serialization functions defined by AcDbObject. They can  override 
//   // the serialization function from the AcmSymbol object (original name + postfix _Ext)
// 
//   Acad::ErrorStatus dwgInFields(AcDbDwgFiler* pFiler) override;
//   Acad::ErrorStatus dwgOutFields(AcDbDwgFiler* pFiler) const override;
// 
//   virtual Acad::ErrorStatus dwgInFields_Ext(AcDbDwgFiler* pFiler);
//   virtual Acad::ErrorStatus dwgOutFields_Ext(AcDbDwgFiler* pFiler) const;
// 
//   virtual Acad::ErrorStatus varInFields(const AcmVariant* pVar, AcRxObject* pFiler = nullptr);
//   virtual Acad::ErrorStatus varOutFields(AcmVariant* pVar, AcRxObject* pFiler = nullptr) const;
//   virtual Adesk::Int32 acadmSchemaNumberPerSymbolType(AcDbDwgFiler* pFiler, AcDbDatabase* pDb) const;
///////////////////////////////////////////////////////////////////////////////

  Acad::ErrorStatus dxfInFields(AcDbDxfFiler* filer) override;
  Acad::ErrorStatus dxfOutFields(AcDbDxfFiler* filer) const override;

  virtual Acad::ErrorStatus subErase(Adesk::Boolean erasing);
  static void convertSymbolToBlock(AcmSymbol *pSym);
  static void convertSymbolsToBlock(AcArray<AcmSymbol*> pArray);
  static void convertSymbolsToBlock(const AcDbObjectIdArray& ids);
  virtual Acad::ErrorStatus getGeomRefArcRadius(double & radius, Adesk::Boolean & isCirArc) const;
    
  virtual void setForceLineWeight(AcDb::LineWeight& lnWgt);
  virtual AcDb::LineWeight getForceLineWeight() const;
  ///<summary>Updates the symbol when attached object is modified.</summary>
  virtual Acm::ErrorStatus updateToAttachedObject();

  ///<summary>This method mirrors AcmSymbol entity.</summary>
  ///<param name="xMirrorForm">Transformation matrix with MIRROR element</param>
  ///<param name="XAxis">Returns mirrored X-Axis of the entity coordinate system</param>
  ///<param name="YAxis">Returns mirrored Y-Axis of the entity coordinate system</param>
  ///<param name="normal">Returns mirrored Normal of the entity coordinate system</param>
	virtual void mirrorCoordSys(const AcGeMatrix3d& xMirrorForm,
                              AcGeVector3d& XAxis,
                              AcGeVector3d& YAxis,
                              AcGeVector3d& normal) const;


  virtual Acm::ErrorStatus addAutoRoutingLeader(const void* pGeomRef, const AcDbObjectId &viewId, const AcGePoint3d& arrowheadLocation, AcGePoint3dArray& pointsArray);
  
  ///<summary>Return whether we allow attaching to specified symbol(like FCF symbol). Default it will return false. Please overwrite it for 
  ///derived classes if necessary</summary>
  virtual  Adesk::Boolean isAttachToSymbolAllowed(const AcRxClass* pSymbolClass) const;

  /// <summary> Method returns all label names of the given symbol if the symbol has label. </summary>
  /// <param name="label"> output list of label names as AcStringArray. </param>
  /// <returns> Return Acme::eOk if the symbol has label. </returns>
  virtual Acm::ErrorStatus getLabelNames(AcStringArray& label) const;

  bool drawProxyGraphic() const;

protected:
  ///<summary>Constructs the symbol class. The constructor is protected.</summary>
  AcmSymbol();
  virtual Adesk::Boolean    subWorldDraw(AcGiWorldDraw* pMode);
  ///<summary>Queries the standard for sub-entities for all leaders and then the server for all sub-entities 
  ///for all sub-symbols. All sub-entities are then exploded.</summary>
  virtual Acad::ErrorStatus subExplode(AcDbVoidPtrArray& entitySet) const;
  ///<summary>Sums geometry extents for a leader.</summary>
  virtual Acad::ErrorStatus subGetGeomExtents(AcDbExtents& extents) const;
  virtual Acad::ErrorStatus subTransformBy(const AcGeMatrix3d& xform);
  virtual Acad::ErrorStatus subDeepClone(AcDbObject* pOwnerObject,
                                      AcDbObject*& pClonedObject,
                                      AcDbIdMapping& idMap,
                                      Adesk::Boolean isPrimary = Adesk::kTrue) const;
  virtual Acad::ErrorStatus subWblockClone(AcRxObject*    pOwner,
                                        AcDbObject*&   pClonedObject,
                                        AcDbIdMapping& idMap,
                                        Adesk::Boolean isPrimary = Adesk::kTrue) const;
  ///<summary>Queries all leaders for their grip points.</summary>
  virtual Acad::ErrorStatus subGetGripPoints(AcGePoint3dArray&,
                                          AcDbIntArray&,
                                          AcDbIntArray&) const;
  ///<summary>Queries all leaders for their grip points.</summary>
  virtual Acad::ErrorStatus   subGetGripPoints(AcDbGripDataPtrArray& grips,
														  const double curViewUnitSize, 
														  const int gripSize, 
														  const AcGeVector3d& curViewDir,
														  const int bitflags) const;

  ///<summary>Determines the leaders to which indices belong and apply moveGripPointsAt to them. If the tip point 
  ///is being edited, then the leaderTipPointChanged() member function of the symbol will be called. If the leader 
  ///contains a landing and it is changed, then the leaderLanding() member function of the symbol is called.</summary>
  virtual Acad::ErrorStatus subMoveGripPointsAt(const AcDbIntArray&,
                                             const AcGeVector3d&);

  ///<summary>Release some resource such as grip app data as this function is called with a status value of kGripsToBeDeleted 
  ///when the grips for the entity are about to be deleted in the host application.</summary>
  virtual void subGripStatus(const AcDb::GripStat status);

  ///<summary>Implemented by calling the getGripPoints member function.</summary>
  virtual Acad::ErrorStatus subGetStretchPoints(AcGePoint3dArray&) const;
  ///<summary>Implemented by calling the moveGripPointsAt member function.</summary>
  virtual Acad::ErrorStatus subMoveStretchPointsAt(const AcDbIntArray& indices,
                                                const AcGeVector3d& offset);
  ///<summary>Determines which sub-symbols or leaders belong to the GS marker. Then, it asks for sub-entities
  ///and applies getOsnapPoints to them.</summary>
  virtual Acad::ErrorStatus subGetOsnapPoints(AcDb::OsnapMode     osnapMode,
                                           INT_PTR             gsSelectionMark,
                                           const AcGePoint3d&  pickPoint,
                                           const AcGePoint3d&  lastPoint,
                                           const AcGeMatrix3d& viewXform,
                                           AcGePoint3dArray&   snapPoints,
                                           AcDbIntArray&       geomIds) const;

  ///<summary>Queries all leaders to get their intersections with the specified entity. If it is a user-defined
  ///entity, it will be exploded first, and created entities will be resolved using the same member function.</summary>
  virtual Acad::ErrorStatus subIntersectWith(const AcDbEntity* ent,
                                          AcDb::Intersect   intType, 
                                          AcGePoint3dArray& points,
                                          INT_PTR thisGsMarker  = 0,
                                          INT_PTR otherGsMarker = 0) const;

  ///<summary>Queries all leaders to get their intersections with the specified entity. If it is a user-defined
  ///entity, it will be exploded first, and created entities will be resolved using the same member function.</summary>
  virtual Acad::ErrorStatus subIntersectWith(const AcDbEntity* ent,
                                          AcDb::Intersect   intType,
                                          const AcGePlane&  projPlane,
                                          AcGePoint3dArray& points,
                                          INT_PTR thisGsMarker  = 0,
                                          INT_PTR otherGsMarker = 0) const;
  virtual Acad::ErrorStatus subGetGsMarkersAtSubentPath(const AcDbFullSubentPath& subPath, 
                                                     AcArray<INT_PTR>& gsMarkers) const;
  ///<summary>Lists all leaders including their vertices, standard name and original name, server name and version.</summary>
  virtual void subList() const;
  virtual Acad::ErrorStatus   subGetClassID(CLSID* pClsid) const;
  ///<summary>Detaches a geometry reference and frees it from memory.</summary>
  void releaseGeomRef();
  ///<summary>This is the object version. It is tested during opening and if the number is bigger than the
  ///version defined by ACMSYMBB.ARX, the object is turned to a proxy. All commands will then stop working.</summary>
  Adesk::UInt16 classVersion() const;

  ///<summary>This functions is used to insert leader at the index position. Currently this function is only 
  ///used to insert primary arrowhead for FCF symbol. Set this function to protected for using it internal.</summary>
  ///<param name="index">The position is which inserting leader at.</param>
  ///<param name="ptrArray">The user selected the points in the drawing.</param>
  ///<returns>If successful then Acm::eOk is returned. If fail, return Acm::eBad.</returns>
  Acm::ErrorStatus insertLeader(Adesk::UInt16 index,const AcGePoint3dArray &ptrArray);
  
  ///<summary>The function changes the state of the member variable which determines if the symbol position 
  /// update is in the progress. The update of symbol position is invoked from the notification on the attached 
  /// object that the object changed position and we need to update the symbol position. Do not use this state 
  /// for direct manipulation with a symbol where subTransformBy is called directly .</summary>
  ///<param name="value">State if the update of symbol position is in progress.</param>
  void setSymbolTransformationInProgres(bool value);

private:
  AcmSymbolImp *mpImp;

protected:
  void *operator new[](size_t nSize) { UNREFERENCED_PARAMETER(nSize);assert(0); return NULL; }
  void operator delete[](void *p) { UNREFERENCED_PARAMETER(p);assert(0); }
};

DBREF_DECLARE(AcmSymbol);

#endif
