////////////////////////////////////////
// START of File: C1_DGROU.CPP
////////////////////////////////////////

//===================================================================
//
// COPYRIGHT NOTICE:  This code is protected by Copyright!
//
// Copyright (c) 1994, 1995, 1996, 1997 by DPC Technology.
// All rights reserved.
//
// See files "cf_copyr.h" and "cf_licen.h" for details of
// copyright and license specifications, and contact information.
//
//===================================================================

// Include this source file's header file:
#include	<cf_api/bf_defs.h>
#include    <cf_api/c1_dgrou.hpp>
            // relative to the IDE's (global) include path: ~/cfrz

// Avoid "magic" numbers below...
#define     C1_DGROU_GROWBY         10
            // Dynamic Array "Grow-By" Size


//
// Use this ctor to generate a cf_DataGroup from two pict3DPoints:
//
cf_DataGroup::cf_DataGroup()
{
    // Init applicable member vars (as per the hpp spec):
    ctorInit();
    this->cf_strGroupLabel = "";
}

cf_DataGroup::cf_DataGroup
            ( const char *a_cStringForLabel )
{
    // Init applicable member vars (as per the hpp spec):
    ctorInit();
    labelNew( a_cStringForLabel );
}


void
cf_DataGroup::ctorInit()
{
    // Init applicable member vars (as per the hpp spec):
    this->bIsActive = FALSE;

    this->dDataXMax = this->dDataXMin = 0.0;
    this->dDataYMax = this->dDataYMin = 0.0;
    this->dDataZMax = this->dDataZMin = 0.0;
    this->dDataXSum = this->dDataYSum = this->dDataZSum = 0.0;

    this->da_p_cf_xyzDataItems.growBy( 10 );
    // Dynamic array is ready to accept new XYZ values...

    this->iNumAllocs  = 0;
    // These all get updated over the life of the group...

    return;
}


cf_DataGroup
    ::~cf_DataGroup()
{
    // Delete ALL the Data Items the users new()ed...
    for ( int i = 1; i <= this->iNumAllocs; i++ )
        delete ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ i ] );
}

cf_DataGroup
    cf_DataGroup
        ::operator=( const cf_DataGroup &cr_cf_dgSource )
{
    // First, copy the essential datagroup object member vars:
    this->bIsActive  = cr_cf_dgSource.bIsActive;

    this->iNumAllocs    = cr_cf_dgSource.iNumAllocs;

    this->cf_strGroupLabel = cr_cf_dgSource.cf_strGroupLabel;

    this->cf_datbGroupDispAttribs  = (cf_DispAttribs&)
                                    cr_cf_dgSource.cf_datbGroupDispAttribs;

    this->dDataXMax     = cr_cf_dgSource.dDataXMax;
    this->dDataXMin     = cr_cf_dgSource.dDataXMin;

    this->dDataYMax     = cr_cf_dgSource.dDataYMax;
    this->dDataYMin     = cr_cf_dgSource.dDataYMin;

    this->dDataZMax     = cr_cf_dgSource.dDataZMax;
    this->dDataZMin     = cr_cf_dgSource.dDataZMin;

    this->dDataXSum     = cr_cf_dgSource.dDataXSum;
    this->dDataYSum     = cr_cf_dgSource.dDataYSum;
    this->dDataZSum     = cr_cf_dgSource.dDataZSum;
                        // which should take care of the attribs.

    //----------------------------------------------------------------------
    // Ready to Content-Copy the reference's dynamic array of XYZ-triplets:
    //----------------------------------------------------------------------
    int i;
    // First, get rid of any previous local XYZ allocations:
    for ( i = 1; i <= this->iNumAllocs; i++ )
        delete ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ i ] );
    this->iNumAllocs = 0;

    // NOW we can content-copy...
    cf_XYZ  *p_cf_xyzSrc = NULL;
            //...the referenced dynamic array of XYZs to...
    cf_XYZ  *p_cf_xyzDest = NULL;
            //...our local dyna array void pointers cast into XYZs...

    for ( i = 1; i <= cr_cf_dgSource.iNumAllocs; i++ )
    {
        // src is the referenced data-group...
        p_cf_xyzSrc = ( (cf_XYZ*) ((cf_DataGroup&)cr_cf_dgSource)
                                           .da_p_cf_xyzDataItems[ i ] );
        // dest will be new-ed...
        p_cf_xyzDest = new cf_XYZ();
        this->da_p_cf_xyzDataItems[ i ] = p_cf_xyzDest;
            //...into our local dyna-array of voids cast to 3D-facets;

        // so now we can de-ref and copy each pointer.
        *p_cf_xyzDest = *p_cf_xyzSrc;
    }

    // Be sure to keep track of what we've allocated!
    this->iNumAllocs = cr_cf_dgSource.iNumAllocs;

    return *this;

}   // end of cf_DataGroup::operator=( const cf_DataGroup& );


//
// Users may want to turn sets of data groups on/off.
//
BOOL
    cf_DataGroup
        ::active()
            { return this->bIsActive; }
void
    cf_DataGroup
        ::active( BOOL bNewState )
            { this->bIsActive = bNewState; }


//
//-------------------------------------------------------
// Users will want a pointer to current items.
// This will yield a pointer to either the indicated
// index, or -- if there is no item at the index,
// this will return the xyz-item at either the 
// largest current index, or if there are no current
// values, a default xyz-item of (0,0,0,NULL).
//-------------------------------------------------------
//
cf_XYZ* 
    cf_DataGroup
        ::activateXYZValAt( int iPrefIndexIfAvail )
{
    cf_XYZ  *p_cf_xyzReturnVal = NULL;  // For now...

    // If someone asks for a silly negative index -- correct it!
    if ( iPrefIndexIfAvail < 1 ) iPrefIndexIfAvail = 1;
    
    // Either they made a legitimate request, which is cool...
    if ( iPrefIndexIfAvail <= this->iNumAllocs )
    {
        p_cf_xyzReturnVal 
         = ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ iPrefIndexIfAvail ] );
    }
    else    //...or they screwed up,
    {       //...and want something that isn't there!
    
        if ( this->iNumAllocs >= 1 )
            //...so either give 'em what we have, or... 
            p_cf_xyzReturnVal 
             = ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ this->iNumAllocs ] );
        else
            //...if we don't have anything, MAKE something!...
            p_cf_xyzReturnVal = this->addXYZVal( 0.0, 0.0, 0.0 );
    }
    return p_cf_xyzReturnVal;       
}



//
//-------------------------------------------------
// Users will want to set values in the group.
//-------------------------------------------------
//
cf_XYZ*
    cf_DataGroup
        ::addXYZVal(    double      dXVal, 
                        double      dYVal,
                        double      dZVal )
{               
    // Make the allocation and assignment:
    this->iNumAllocs++;
    this->da_p_cf_xyzDataItems[ iNumAllocs ] 
                                = new cf_XYZ( dXVal, dYVal, dZVal );

    // Update the running ranges:
    if ( this->iNumAllocs == 1 )
    {
        this->dDataXMax = this->dDataXMin = dXVal;
        this->dDataYMax = this->dDataYMin = dYVal;
        this->dDataZMax = this->dDataZMin = dZVal;
    }
    else
    {
        if ( dXVal > this->dDataXMax )
                this->dDataXMax = dXVal;
        if ( dXVal < this->dDataXMin )
                this->dDataXMin = dXVal;
                // X Min/Max now updated...

        if ( dYVal > this->dDataYMax )
                this->dDataYMax = dYVal;
        if ( dYVal < this->dDataYMin )
                this->dDataYMin = dYVal;
                // Y Min/Max now updated...

        if ( dZVal > this->dDataZMax )
                this->dDataZMax = dZVal;
        if ( dZVal < this->dDataZMin ) 
                this->dDataZMin = dZVal;
                // Z Min/Max now updated...
    }
        
    // Update the running tally:
    this->dDataXSum += dXVal;
    this->dDataYSum += dYVal;
    this->dDataZSum += dZVal;
    
    // return a pointer to the address of the new XYZ object:   
    return ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ iNumAllocs ] );
}

cf_XYZ*
    cf_DataGroup
        ::addXYZVal(    cf_XYZ      &r_cf_xyzUserVal )
{ 
    // Make the allocation and assignment:
    this->iNumAllocs++;
    this->da_p_cf_xyzDataItems[ iNumAllocs ] = new cf_XYZ();
    
    *((cf_XYZ*) this->da_p_cf_xyzDataItems[ iNumAllocs ]) = r_cf_xyzUserVal;

    // Save multiple function call overhead with some temp vars...
    double  dXVal = r_cf_xyzUserVal.x();
    double  dYVal = r_cf_xyzUserVal.y();
    double  dZVal = r_cf_xyzUserVal.z();

    // Update the running ranges:
    if ( this->iNumAllocs == 1 )
    {
        this->dDataXMax = this->dDataXMin = dXVal;
        this->dDataYMax = this->dDataYMin = dYVal;
        this->dDataZMax = this->dDataZMin = dZVal;
    }
    else
    {
        if ( dXVal > this->dDataXMax )
                this->dDataXMax = dXVal;
        if ( dXVal < this->dDataXMin )
                this->dDataXMin = dXVal;
                // X Min/Max now updated...

        if ( dYVal > this->dDataYMax )
                this->dDataYMax = dYVal;
        if ( dYVal < this->dDataYMin )
                this->dDataYMin = dYVal;
                // Y Min/Max now updated...

        if ( dZVal > this->dDataZMax )
                this->dDataZMax = dZVal;
        if ( dZVal < this->dDataZMin )
                this->dDataZMin = dZVal;
                // Z Min/Max now updated...
    }

    // Update the running tally:
    this->dDataXSum += dXVal;
    this->dDataYSum += dYVal;
    this->dDataZSum += dZVal;
    
    // return a pointer to the address of the new XYZ object:   
    return ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ iNumAllocs ] );
}


//
// Users will want to get running group data ranges
//
double
    cf_DataGroup
        ::dataXMax()
            { return this->dDataXMax; }
double
    cf_DataGroup
        ::dataXMin()
            { return this->dDataXMin; }
double
    cf_DataGroup
        ::dataYMax()
            { return this->dDataYMax; }
double
    cf_DataGroup
        ::dataYMin()
            { return this->dDataYMin; }
double
    cf_DataGroup
        ::dataZMax()
            { return this->dDataZMax; }
double
    cf_DataGroup
        ::dataZMin()
            { return this->dDataZMin; }


//
// Users will want to get running group data sums
//
double
    cf_DataGroup
        ::dataXSum()
            { return this->dDataXSum; }
double
    cf_DataGroup
        ::dataYSum()
            { return this->dDataYSum; }

// Sum all the Y-Vals' Absolute Values
double
    cf_DataGroup
        ::dataYSumAbs()
{
    double  dSumAllYAbsVals = 0.0;
    double  dIndivYVal = 0.0;

    for ( int i = 1; i <= this->iNumAllocs; i++ )
    {
        dIndivYVal = ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ i ] )->y();
        if ( dIndivYVal < 0 )
            dSumAllYAbsVals += - dIndivYVal;
        else
            dSumAllYAbsVals += dIndivYVal;
    }
    return dSumAllYAbsVals;
}

double
    cf_DataGroup
        ::dataZSum()
            { return this->dDataZSum; }

// Get a reference to this group's display and/or font attributes.
cf_DispAttribs*
    cf_DataGroup
        ::dispAttribs()
            { return &this->cf_datbGroupDispAttribs; }

cf_TextAttribs*
    cf_DataGroup
        ::textAttribs()
            { return &this->cf_tatbGroupTextAttribs; }


//
// Users will want to know if this group has any data items.
//
BOOL
    cf_DataGroup
        ::hasData()
            { return (( this->iNumAllocs >= 1 ) ? TRUE : FALSE ); }

//
// Get & set group label string attribute.
//
cf_String* 
    cf_DataGroup
        ::labelIs()
            { return &(this->cf_strGroupLabel); }
void            
    cf_DataGroup
        ::labelNew( const char *a_cStringForLabel )
            { if (a_cStringForLabel != NULL) 
                    this->cf_strGroupLabel = a_cStringForLabel;}


// Users may want to get the number of active items.
int
    cf_DataGroup
        ::numItems()
            { return this->iNumAllocs; }

// "Set" methods which are synonyms of this->active( BOOL ).
void
    cf_DataGroup
        ::on()
            { this->bIsActive = TRUE; }

void
    cf_DataGroup
        ::off()
            { this->bIsActive = FALSE; }

void
    cf_DataGroup
        ::reset()
{
    // Delete ALL the Data Itemss the users new()ed...
    for ( int i = 1; i <= this->iNumAllocs; i++ )
        delete ( (cf_XYZ*) this->da_p_cf_xyzDataItems[ i ] );
    this->iNumAllocs = 0;

    // Re-init applicable member vars (to the c-tor's hpp spec):
    this->dDataXMax = this->dDataXMin = 0.0;
    this->dDataYMax = this->dDataYMin = 0.0;
    this->dDataZMax = this->dDataZMin = 0.0;
    this->dDataXSum = this->dDataYSum = this->dDataZSum = 0.0;

    // EXCEPT for the activation state:
    this->bIsActive = TRUE;
                    // which we KNOW (because we already
                    // may have set things like the label
                    // string, color, etc.) is already TRUE!

    this->cf_strGroupLabel = "";
}

//
// End of class cf_DataGroup Implementation
//

////////////////////////////////////////
// END of File: C1_DGROU.CPP
////////////////////////////////////////









