// (C) Copyright 1996-2006 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//
//  referenc.cpp
//
//

#if defined(_DEBUG) && !defined(AC_FULL_DEBUG)
#error _DEBUG should not be defined except in internal Adesk debug builds
#endif

#include <adslib.h>
#include <dbelipse.h>
#include <gemat3d.h>
#include <dbsymtb.h>
#include <dbapserv.h>
#include <aced.h>
#include <actrans.h>
#include <dbidmap.h>
#include <acestext.h>
#include "tchar.h"



// THE FOLLOWING CODE APPEARS IN THE SDK DOCUMENT.

class AsdkEllipse : public AcDbEllipse

// This class extends AcDbEllipse by adding in functionality
// to store a dynamic array of hard pointer object IDs.
//
// The subErase() member function has been overridden and
// implemented, so when an object of this class is
// erased, the objects pointed to by the hard pointer IDs
// stored within the object will also be erased.
//
{
public:

    ACRX_DECLARE_MEMBERS(AsdkEllipse);
    AsdkEllipse() {};
    AsdkEllipse(const AcDbObjectIdArray& ellipses)
        : mEllipseIds(ellipses) {};
    AsdkEllipse(const AcGePoint3d&  center,
        const AcGeVector3d& unitNormal,
        const AcGeVector3d& majorAxis,
        double radiusRatio,
        double startAngle = 0.0,
        double endAngle = 6.28318530717958647692);
    AsdkEllipse(const AcDbObjectIdArray&  ellipses,
        const AcGePoint3d&  center,
        const AcGeVector3d& unitNormal,
        const AcGeVector3d& majorAxis,
        double radiusRatio,
        double startAngle = 0.0,
        double endAngle   = 6.28318530717958647692);
    AcDbObjectId ellipseId(unsigned short which);
    Acad::ErrorStatus setEllipseId(
        const AcDbObjectId& objId, unsigned short which);
    Acad::ErrorStatus setEllipseIds(
        const AcDbObjectIdArray& Ids);
    Acad::ErrorStatus appendId(const AcDbObjectId& objId);
    Acad::ErrorStatus appendIds(
        const AcDbObjectIdArray& objIds);
    inline Adesk::Boolean removeId(
        const AcDbObjectId& objId);

    // AcDbObject overrides.
    //
    virtual Acad::ErrorStatus subErase(
        Adesk::Boolean pErasing);
    virtual Acad::ErrorStatus dwgInFields(
        AcDbDwgFiler* filer);
    virtual Acad::ErrorStatus dwgOutFields(
        AcDbDwgFiler* filer) const;
    virtual Acad::ErrorStatus dxfInFields(
        AcDbDxfFiler* filer);
    virtual Acad::ErrorStatus dxfOutFields(
        AcDbDxfFiler* filer) const;
protected:
    virtual Acad::ErrorStatus subWblockClone(
        AcRxObject* pOwnerObject,
        AcDbObject*& pClonedObject,
        AcDbIdMapping& idMap,
        Adesk::Boolean isPrimary = Adesk::kTrue) const;

    // AcDbEntity overrides.
    //
    virtual void subList() const;

private:
    AcDbObjectIdArray mEllipseIds;
    static int mInFlux;  // == 1 when first object's
                         //    subErase is erasing.
};

ACRX_DXF_DEFINE_MEMBERS(AsdkEllipse, AcDbEllipse,
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,
0,\
    ASDKELLIPSE, REFERENC);

// Static class data member definition.
//
int AsdkEllipse::mInFlux = Adesk::kFalse;


AsdkEllipse::AsdkEllipse(const AcGePoint3d&  center,
    const AcGeVector3d& unitNormal,
    const AcGeVector3d& majorAxis,
    double              radiusRatio,
    double              startAngle,
    double              endAngle) :

    AcDbEllipse(center, unitNormal, majorAxis, radiusRatio,
    startAngle, endAngle)
{	}

AsdkEllipse::AsdkEllipse(const AcDbObjectIdArray&  ellipses,
    const AcGePoint3d&  center,
    const AcGeVector3d& unitNormal,
    const AcGeVector3d& majorAxis,
    double              radiusRatio,
    double              startAngle,
    double              endAngle) :

    AcDbEllipse(center, unitNormal, majorAxis, radiusRatio,
    startAngle, endAngle), mEllipseIds(ellipses)
{ 	}

AcDbObjectId
AsdkEllipse::ellipseId(unsigned short which)
{
    assertReadEnabled();
    if  (which > mEllipseIds.length())
        return AcDbObjectId::kNull;
    return mEllipseIds[which];
}

Acad::ErrorStatus
AsdkEllipse::setEllipseId(const AcDbObjectId& objId,
    unsigned short which)
{
    assertWriteEnabled();
    if  (which > mEllipseIds.length())
        return Acad::eInvalidIndex;
    mEllipseIds[which] = objId;
    return Acad::eOk;
}

Acad::ErrorStatus
AsdkEllipse::setEllipseIds(const AcDbObjectIdArray& objIds)
{
    assertWriteEnabled();
    if (objIds.length() == 0)
        return Acad::eNullObjectId;
    mEllipseIds = objIds;
    return Acad::eOk;
}

Acad::ErrorStatus
AsdkEllipse::appendId(const AcDbObjectId& objId)
{
    assertWriteEnabled();
    if  (objId == AcDbObjectId::kNull)
        return Acad::eNullObjectId;
    mEllipseIds.append(objId);
    return Acad::eOk;
}

Acad::ErrorStatus
AsdkEllipse::appendIds(const AcDbObjectIdArray& objIds)
{
    assertWriteEnabled();
    if  (objIds.length() == 0)
        return Acad::eNullObjectId;
    mEllipseIds.append(objIds);
    return Acad::eOk;
}

inline Adesk::Boolean
AsdkEllipse::removeId(const AcDbObjectId& objId)
{
    assertWriteEnabled();
    return mEllipseIds.remove(objId);
}

// This implementation of subErase opens and erases all
// objects that this entity has hard pointer references
// to.  The effect is that when one AsdkEllipse is erased,
// all the others it has hard pointers to also erase as
// a "group".
//
Acad::ErrorStatus
AsdkEllipse::subErase(Adesk::Boolean pErasing)
{
    Acad::ErrorStatus es = AcDbEllipse::subErase(pErasing);
    if (es != Acad::eOk)
        return es;

    if (mInFlux == Adesk::kFalse) {
        mInFlux = Adesk::kTrue;
        AsdkEllipse *pEllipse;
        int es;
        for (int i = 0; i < mEllipseIds.length(); i++) {
            es = acdbOpenObject(pEllipse, mEllipseIds[i],
                AcDb::kForWrite, Adesk::kTrue);
            if (es != Acad::eOk)
                continue;

			pEllipse->erase(pErasing);
            pEllipse->close();
        }
        mInFlux = Adesk::kFalse;
    }
    return Acad::eOk;
}

Acad::ErrorStatus
AsdkEllipse::dwgInFields(AcDbDwgFiler* filer)
{
    assertWriteEnabled();

    AcDbEllipse::dwgInFields(filer);

    mEllipseIds.setLogicalLength(0);

    int idCount;
    filer->readInt32((long*)&idCount);

    AcDbHardPointerId objId;
    for (int i = 0; i < idCount; i++) {
        filer->readItem(&objId);
        mEllipseIds.append(objId);
    }
    return filer->filerStatus();
}

Acad::ErrorStatus
AsdkEllipse::dwgOutFields(AcDbDwgFiler* filer) const
{
    assertReadEnabled();

    AcDbEllipse::dwgOutFields(filer);

    filer->writeInt32(mEllipseIds.length());

    for (int i = 0; i < mEllipseIds.length(); i++) {
        filer->writeHardPointerId(mEllipseIds[i]);
    }
    return filer->filerStatus();
}

Acad::ErrorStatus
AsdkEllipse::dxfInFields(AcDbDxfFiler* filer)
{
    assertWriteEnabled();

    Acad::ErrorStatus es = AcDbEllipse::dxfInFields(filer);
    if (es != Acad::eOk) {
        return es;
    }

    // Check to see if we're at the right subclass data
    // marker.
    //
    if (!filer->atSubclassData(_T("AsdkEllipse"))) {
        return Acad::eBadDxfSequence;
    }

    struct resbuf inbuf;
    AcDbObjectId objId;
    int idCount;
    filer->readItem(&inbuf);
    if (inbuf.restype == AcDb::kDxfInt32) {
        idCount = inbuf.resval.rint;
    } else {
        filer->pushBackItem();
        filer->setError(Acad::eInvalidDxfCode,
            _T("\nError: expected group code %d"),
             AcDb::kDxfInt32);
        return filer->filerStatus();
    }

    for (int i = 0; i < idCount; i++) {
        es = filer->readItem(&inbuf);
        if (es != Acad::eOk) {
            filer->setError(Acad::eMissingDxfField,
                _T("\nError: expected more group code %d's"),
                AcDb::kDxfHardPointerId);
            return filer->filerStatus();
        }
        if (inbuf.restype == AcDb::kDxfHardPointerId) {
            acdbGetObjectId(objId, inbuf.resval.rlname);
            mEllipseIds.append(objId);
        } else {
            filer->pushBackItem();
            filer->setError(Acad::eInvalidDxfCode,
                _T("\nError: expected group code %d"),
                AcDb::kDxfHardPointerId);
            return filer->filerStatus();
        }
    }
    return filer->filerStatus();
}

Acad::ErrorStatus
AsdkEllipse::dxfOutFields(AcDbDxfFiler* filer) const
{
    assertReadEnabled();

    AcDbEllipse::dxfOutFields(filer);
    filer->writeItem(AcDb::kDxfSubclass, _T("AsdkEllipse"));
    filer->writeInt32(AcDb::kDxfInt32,
        mEllipseIds.length());

    for (int i = 0; i < mEllipseIds.length(); i++) {
        filer->writeObjectId(AcDb::kDxfHardPointerId,
            mEllipseIds[i]);
    }
    return filer->filerStatus();
}


void
AsdkEllipse::subList() const
{
    assertReadEnabled();
    AcDbEllipse::subList();
    acutPrintf(_T("\nClass:\t%s"), isA()->name());
    for (int i = 0; i < mEllipseIds.length(); i++) {
        acutPrintf(_T("\nReferenceId[%d]:\t%ld"), i,
            (mEllipseIds[i]).asOldId());
    }
}


Acad::ErrorStatus
AsdkEllipse::subWblockClone(AcRxObject*    pOwnerObject,
                         AcDbObject*&   pClonedObject,
                         AcDbIdMapping& idMap,
                         Adesk::Boolean isPrimary) const
{
    assertReadEnabled();
    static AcDbObjectId btr, pspace = AcDbObjectId::kNull;
    AcTransaction *pTrans = NULL;
    pClonedObject = NULL;
    if (pspace == AcDbObjectId::kNull) {
        AcDbBlockTable *pTable;
        database()->getSymbolTable(pTable, AcDb::kForRead);
        pTable->getAt(ACDB_PAPER_SPACE, pspace);
        pTable->close();
    }

    if (   idMap.deepCloneContext() == AcDb::kDcXrefBind
        && ownerId() == pspace)
        return Acad::eOk;

    // Have we already done this entity ?
    //
    AcDbIdPair idPair(objectId(), (AcDbObjectId)NULL,
        Adesk::kTrue);
    if (idMap.compute(idPair) == TRUE && idPair.value()
        != NULL)
    {
        pClonedObject = NULL;
        return Acad::eOk;
    }

    AcDbBlockTableRecord *pBTR
        = AcDbBlockTableRecord::cast(pOwnerObject);

    if (pBTR != NULL) {
        if (isPrimary == Adesk::kTrue)
            btr = pBTR->objectId();
        else
            btr = AcDbObjectId::kNull;

    } else if (btr != AcDbObjectId::kNull) {
        pTrans = actrTransactionManager->startTransaction();
        pTrans->getObject((AcDbObject*&)pBTR, btr,
            AcDb::kForWrite);
        pOwnerObject = pBTR;
    }

    Acad::ErrorStatus es
        = AcDbEllipse::wblockClone(pOwnerObject,
        pClonedObject, idMap, btr != AcDbObjectId::kNull);

	if (pTrans)
		actrTransactionManager->endTransaction();

    acutPrintf(_T("\nWblockClone error status: %s"), acadErrorStatusText(es) );
    return Acad::eOk;
}


void
createEllipses()
{
    const int ellipseCount = 10;

    AsdkEllipse *pEllipse;
    pEllipse = new AsdkEllipse(AcGePoint3d(4.0, 4.0, 0.0),
        AcGeVector3d(0.0, 0.0, 1.0),
        AcGeVector3d(2.0, 0.0, 0.0),
        0.5);

    AcDbVoidPtrArray ellipses;
    ellipses.append(pEllipse);

    // Now use the getTransformedCopy() function with a
    // scaling matrix (in X & Y only) to create new
    // AsdkEllipses, each 0.5 units larger than the last in
    // the X & Y direction,  but identical in the Z
    // direction.  This would be similar to the
    // getOffsetCurves() function,  but that function
    // returns AcDbSpline entities instead of AcDbEllipses.
    //
    double j = 1.1;
    AcGeMatrix3d scale;
    for (int i = 0; i < ellipseCount; i++, j += 0.1) {
        scale.setToScaling(j, pEllipse->center());
        scale.entry[2][2] = 1.0;  // Z scaling == 1

        // getTransformed copy uses this->clone() to create
        // a new object, which the ent pointer is assigned
        // to point to. Therefore, ent should NOT point to an
        // existing entity or there will be a memory leak!
        //
        // Since this->clone() is used, the AsdkEllipse class
        // must override this member function to
        // be sure that an AsdkEllipse is created instead
        // of just an AcDbEllipse.
	//

        AsdkEllipse *pNextEllipse;
        ((AsdkEllipse*)ellipses[0])->getTransformedCopy(
            scale, (AcDbEntity*&)pNextEllipse);

        ellipses.append(pNextEllipse);
    }

    AcDbBlockTable *pBlockTable;
    acdbHostApplicationServices()->workingDatabase()
        ->getSymbolTable(pBlockTable, AcDb::kForRead);

    AcDbBlockTableRecord *pBlockTableRecord;
    pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
        AcDb::kForWrite);
    pBlockTable->close();

    AcDbObjectIdArray ellipseIds;
    AcDbObjectId tempId;
    for (int i = 0; i < ellipses.length(); i++) {
        pBlockTableRecord->appendAcDbEntity(tempId,
            (AsdkEllipse*)ellipses[i]);
        ellipseIds.append(tempId);
    }
    pBlockTableRecord->close();

    // Set up the hard pointers and close the ellipses.
    //
    for (int i = 0; i < ellipses.length(); i++) {
        // Add in all the IDs.
        //
        ((AsdkEllipse*)ellipses[i])
            ->setEllipseIds(ellipseIds);

        // Now remove the object ID of the "*this" ellipse
        // so it doesn't reference itself.
        //
        ((AsdkEllipse*)ellipses[i])->removeId(
            ((AsdkEllipse*)ellipses[i])->objectId());

        ((AsdkEllipse*)ellipses[i])->close();
    }
}

void
initApp()
{
    acedRegCmds->addCommand(_T("ASDK_ELLIPSES"),
        _T("ASDK_ELLIPSES"), _T("ELLIPSES"),
        ACRX_CMD_MODAL, createEllipses);

    AsdkEllipse::rxInit();
    acrxBuildClassHierarchy();
}

void
unloadApp()
{
    acedRegCmds->removeGroup(_T("ASDK_ELLIPSES"));

    // Remove the AsdkEllipse class from the ACRX runtime
    // class hierarchy. If this is done while the database is
    // still active, it should cause all objects of class
    // AsdkEllipse to be turned into proxies.
    //
    deleteAcRxClass(AsdkEllipse::desc());
}

extern "C" AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
    switch (msg) {
    case AcRx::kInitAppMsg:
        acrxDynamicLinker->unlockApplication(appId);
		acrxDynamicLinker->registerAppMDIAware(appId);
        initApp();
        break;
    case AcRx::kUnloadAppMsg:
        unloadApp();
    }
    return AcRx::kRetOK;
}

// END CODE APPEARING IN SDK DOCUMENT.
