#pragma once
#include "gsoft.h"
#include "GcDbCore2dDefs.h"
#include "drawable.h"
#include "GcHeapOpers.h"
#include "rxobject.h"
#include "gcdb.h"
#include "dbboiler.h"
#include "dbptrar.h"
#include "dbidar.h"

class GcDbIdMapping;
class GcDbAuditInfo;
class GcDbDwgFiler;
class GcDbDxfFiler;
struct resbuf;
class GcDbObjectReactor;
class GcDbImpObject;
class GcDbField;
class GcDbDictionary;
class GcGeMatrix3d;
class GcDbEntity;
class GcDbObject;

struct _GUID;
typedef struct _GUID CLSID;

GCDB_PORT Gcad::ErrorStatus gcdbOpenObject(
  GcDbObject*& pObj, GcDbObjectId id, GcDb::OpenMode mode, bool openErased, const GcRxClass* pClass);

GCDB_PORT Gcad::ErrorStatus gcdbOpenObject(
  GcDbObject*& pObj, GcDbObjectId id, GcRxClass* (*pfDesc)(), GcDb::OpenMode mode, bool openErased);

inline Gcad::ErrorStatus gcdbOpenGcDbObject(GcDbObject*& pObj,
                                            GcDbObjectId id,
                                            GcDb::OpenMode mode,
                                            bool openErasedObject = false)
{
  return ::gcdbOpenObject(pObj, id, mode, openErasedObject, nullptr);
}

template<class T_OBJECT>
inline Gcad::ErrorStatus gcdbOpenObject(T_OBJECT*& pObj,
                                        GcDbObjectId id,
                                        GcDb::OpenMode mode = GcDb::kForRead,
                                        bool openErased = false)
{
  return ::gcdbOpenObject((GcDbObject*&)pObj, id, &T_OBJECT::desc, mode, openErased);
}

inline Gcad::ErrorStatus gcdbOpenObject(GcDbObject*& pObj,
                                        GcDbObjectId id,
                                        GcDb::OpenMode mode = GcDb::kForRead,
                                        bool openErased = false)
{
  return ::gcdbOpenObject(pObj, id, mode, openErased, nullptr);
}

class GCDB_PORT GSOFT_NO_VTABLE GcDbObject : public GcGiDrawable
{
public:
  GCDB_DECLARE_MEMBERS(GcDbObject);

  ~GcDbObject();

  GcDbObjectId objectId() const;
  GcDbObjectId ownerId() const;
  virtual Gcad::ErrorStatus setOwnerId(GcDbObjectId objId);
  void getGcDbHandle(GcDbHandle& handle) const;
  GcDbDatabase* database() const;
  GcDbDatabase* databaseToUse() const;
  GcDbDatabase* intendedDatabase() const;
  Gcad::ErrorStatus setIntendedDatabase(GcDbDatabase* pDb);
  Gcad::ErrorStatus createExtensionDictionary();
  GcDbObjectId extensionDictionary() const;
  Gcad::ErrorStatus releaseExtensionDictionary();
  Gcad::ErrorStatus upgradeOpen();
  Gcad::ErrorStatus upgradeFromNotify(Gsoft::Boolean& wasWritable);
  Gcad::ErrorStatus downgradeOpen();
  Gcad::ErrorStatus downgradeToNotify(Gsoft::Boolean wasWritable);
  virtual Gcad::ErrorStatus subOpen(GcDb::OpenMode mode);
  Gcad::ErrorStatus cancel();
  virtual Gcad::ErrorStatus subCancel();
  Gcad::ErrorStatus close();
  Gcad::ErrorStatus closeAndPage(Gsoft::Boolean onlyWhenClean = true);
  virtual Gcad::ErrorStatus subClose();
  Gcad::ErrorStatus erase(Gsoft::Boolean erasing = true);
  virtual Gcad::ErrorStatus subErase(Gsoft::Boolean erasing);
  Gcad::ErrorStatus handOverTo(GcDbObject* newObject,
                               Gsoft::Boolean keepXData = true,
                               Gsoft::Boolean keepExtDict = true);
  virtual Gcad::ErrorStatus subHandOverTo(GcDbObject* newObject);
  Gcad::ErrorStatus swapIdWith(GcDbObjectId otherId,
                               Gsoft::Boolean swapXdata = false,
                               Gsoft::Boolean swapExtDict = false);
  virtual Gcad::ErrorStatus subSwapIdWith(GcDbObjectId otherId,
                                          Gsoft::Boolean swapXdata = false,
                                          Gsoft::Boolean swapExtDict = false);
  virtual Gcad::ErrorStatus swapReferences(const GcDbIdMapping& idMap);
  virtual Gcad::ErrorStatus audit(GcDbAuditInfo* pAuditInfo);
  Gcad::ErrorStatus dwgIn(GcDbDwgFiler* pFiler);
  virtual Gcad::ErrorStatus dwgInFields(GcDbDwgFiler* pFiler);
  Gcad::ErrorStatus dwgOut(GcDbDwgFiler* pFiler) const;
  virtual Gcad::ErrorStatus dwgOutFields(GcDbDwgFiler* pFiler) const;
  Gcad::ErrorStatus dxfIn(GcDbDxfFiler* pFiler);
  virtual Gcad::ErrorStatus dxfInFields(GcDbDxfFiler* pFiler);
  Gcad::ErrorStatus dxfOut(GcDbDxfFiler* pFiler, bool bAllXdata, const GcDbObjectIdArray* pRegAppIds) const;
  virtual Gcad::ErrorStatus dxfOutFields(GcDbDxfFiler* pFiler) const;
  virtual GcDb::DuplicateRecordCloning mergeStyle() const;
  virtual resbuf* xData(const GCHAR* regappName = nullptr) const;
  virtual Gcad::ErrorStatus setXData(const resbuf* xdata);
  Gcad::ErrorStatus xDataTransformBy(const GcGeMatrix3d& xform);
  Gcad::ErrorStatus setBinaryData(const GCHAR* key, Gsoft::Int32 size, const char* data);
  Gcad::ErrorStatus getBinaryData(const GCHAR* key, Gsoft::Int32& size, char*& data) const;

  Gsoft::Boolean isEraseStatusToggled() const;
  Gsoft::Boolean isErased() const;
  Gsoft::Boolean isReadEnabled() const;
  Gsoft::Boolean isWriteEnabled() const;
  Gsoft::Boolean isNotifyEnabled() const;
  Gsoft::Boolean isModified() const;
  Gsoft::Boolean isModifiedXData() const;
  Gsoft::Boolean isModifiedGraphics() const;
  Gsoft::Boolean isNewObject() const;
  Gsoft::Boolean isNotifying() const;
  Gsoft::Boolean isUndoing() const;
  Gsoft::Boolean isCancelling() const;
  Gsoft::Boolean isReallyClosing() const;
  Gsoft::Boolean isTransactionResident() const;
  Gsoft::Boolean isAProxy() const;

  void assertReadEnabled() const;
  void assertWriteEnabled(Gsoft::Boolean autoUndo = true, Gsoft::Boolean recordModified = true);
  void assertNotifyEnabled() const;

  bool isUndoRecordingDisabled() const;
  void disableUndoRecording(Gsoft::Boolean disable);
  GcDbDwgFiler* undoFiler();
  virtual Gcad::ErrorStatus applyPartialUndo(GcDbDwgFiler* undoFiler, GcRxClass* classObj);

  Gcad::ErrorStatus addReactor(GcDbObjectReactor* pReactor) const;
  Gcad::ErrorStatus removeReactor(GcDbObjectReactor* pReactor) const;
  Gcad::ErrorStatus addPersistentReactor(GcDbObjectId objId);
  Gcad::ErrorStatus removePersistentReactor(GcDbObjectId objId);

  bool hasReactor(const GcDbObjectReactor* pReactor) const;
  bool hasPersistentReactor(GcDbObjectId objId) const;

  const GcDbVoidPtrArray* reactors() const;

  virtual void recvPropagateModify(const GcDbObject* subObj);
  virtual void xmitPropagateModify() const;

  virtual Gcad::ErrorStatus deepClone(GcDbObject* pOwnerObject,
                                      GcDbObject*& pClonedObject,
                                      GcDbIdMapping& idMap,
                                      bool isPrimary = true) const final;

  template<typename CloneType>
  Gcad::ErrorStatus deepClone(GcDbObject* pOwner, CloneType*& pClone, GcDbIdMapping& idMap, bool isPrimary = true) const
  {
    GcDbObject* pNewObj = nullptr;
    Gcad::ErrorStatus es = this->deepClone(pOwner, pNewObj, idMap, isPrimary);
    if (pNewObj == nullptr || es != Gcad::eOk)
    {
      pClone = nullptr;
    }
    else
    {
      pClone = CloneType::cast(pNewObj);
      if (pClone == nullptr)
      {
        es = Gcad::eWrongObjectType;
        pNewObj->erase();
        pNewObj->close();
      }
    }
    return es;
  }
  virtual Gcad::ErrorStatus wblockClone(GcRxObject* pOwnerObject,
                                        GcDbObject*& pClonedObject,
                                        GcDbIdMapping& idMap,
                                        bool isPrimary = true) const final;

  template<typename CloneType>
  Gcad::ErrorStatus wblockClone(GcRxObject* pOwner,
                                CloneType*& pClone,
                                GcDbIdMapping& idMap,
                                bool isPrimary = true) const
  {
    GcDbObject* pNewObj = nullptr;
    Gcad::ErrorStatus es = this->wblockClone(pOwner, pNewObj, idMap, isPrimary);
    if (pNewObj == nullptr || es != Gcad::eOk)
    {
      pClone = nullptr;
    }
    else
    {
      pClone = CloneType::cast(pNewObj);
      if (pClone == nullptr)
      {
        es = Gcad::eWrongObjectType;
        pNewObj->erase();
        pNewObj->close();
      }
    }
    return es;
  }
  void setGcDbObjectIdsInFlux();
  Gsoft::Boolean isGcDbObjectIdsInFlux() const;

  virtual void cancelled(const GcDbObject* dbObj);
  virtual void copied(const GcDbObject* dbObj, const GcDbObject* newObj);
  virtual void erased(const GcDbObject* dbObj, Gsoft::Boolean bErasing);
  virtual void goodbye(const GcDbObject* dbObj);
  virtual void openedForModify(const GcDbObject* dbObj);
  virtual void modified(const GcDbObject* dbObj);
  virtual void subObjModified(const GcDbObject* dbObj, const GcDbObject* subObj);
  virtual void modifyUndone(const GcDbObject* dbObj);
  virtual void modifiedXData(const GcDbObject* dbObj);
  virtual void unappended(const GcDbObject* dbObj);
  virtual void reappended(const GcDbObject* dbObj);
  virtual void objectClosed(const GcDbObjectId objId);
  virtual void modifiedGraphics(const GcDbEntity* dbEnt);

  GcRxObject* clone() const override;
  Gcad::ErrorStatus copyFrom(const GcRxObject* pSrc) override;

  bool hasSaveVersionOverride();
  void setHasSaveVersionOverride(bool bSetIt);

  virtual Gcad::ErrorStatus getObjectSaveVersion(const GcDbDwgFiler* pFiler,
                                                 GcDb::GcDbDwgVersion& ver,
                                                 GcDb::MaintenanceReleaseVersion& maintVer);
  virtual Gcad::ErrorStatus getObjectSaveVersion(const GcDbDxfFiler* pFiler,
                                                 GcDb::GcDbDwgVersion& ver,
                                                 GcDb::MaintenanceReleaseVersion& maintVer);
  Gcad::ErrorStatus getObjectBirthVersion(GcDb::GcDbDwgVersion& ver, GcDb::MaintenanceReleaseVersion& maintVer);
  virtual Gcad::ErrorStatus decomposeForSave(GcDb::GcDbDwgVersion ver,
                                             GcDbObject*& replaceObj,
                                             GcDbObjectId& replaceId,
                                             Gsoft::Boolean& exchangeXData);
  virtual GcGiDrawable* drawable();

  Gsoft::Boolean isPersistent() const override;
  GcDbObjectId id() const override;

  virtual Gcad::ErrorStatus getClassID(CLSID* pClsid) const final;

  bool hasFields(void) const;
  Gcad::ErrorStatus getField(const GCHAR* pszPropName, GcDbObjectId& fieldId) const;
  Gcad::ErrorStatus getField(const GCHAR* pszPropName, GcDbField*& pField, GcDb::OpenMode mode = GcDb::kForRead) const;
  virtual Gcad::ErrorStatus setField(const GCHAR* pszPropName, GcDbField* pField, GcDbObjectId& fieldId);
  virtual Gcad::ErrorStatus removeField(GcDbObjectId fieldId);
  virtual Gcad::ErrorStatus removeField(const GCHAR* pszPropName, GcDbObjectId& returnId);
  virtual Gcad::ErrorStatus removeField(const GCHAR* pszPropName);
  GcDbObjectId getFieldDictionary(void) const;
  Gcad::ErrorStatus getFieldDictionary(GcDbDictionary*& pFieldDict, GcDb::OpenMode mode = GcDb::kForRead) const;

protected:
  GcDbObject();

  virtual GcRxObject* subQueryX(const GcRxClass* protocolClass) const;
  Gsoft::UInt32 subSetAttributes(GcGiDrawableTraits* pTraits) override;
  Gsoft::Boolean subWorldDraw(GcGiWorldDraw* pWd) override;
  void subViewportDraw(GcGiViewportDraw* pVd) override;
  virtual Gcad::ErrorStatus subGetClassID(CLSID* pClsid) const;
  virtual Gcad::ErrorStatus subDeepClone(GcDbObject* pOwnerObject,
                                         GcDbObject*& pClonedObject,
                                         GcDbIdMapping& idMap,
                                         Gsoft::Boolean isPrimary = true) const;
  virtual Gcad::ErrorStatus subWblockClone(GcRxObject* pOwnerObject,
                                           GcDbObject*& pClonedObject,
                                           GcDbIdMapping& idMap,
                                           Gsoft::Boolean isPrimary = true) const;

private:
  friend class GcDbPropertiesOverrule;
  friend class GcDbObjectOverrule;
  typedef GcDbImpObject impType;

  GcDbObject(const GcDbObject&) = delete;
  GcDbObject& operator=(const GcDbObject&) = delete;
};
