// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

#include <BJHash.h>
#include <ExpressionList.h>
#include <DataNode.h>
#include <Expression.h>

#include <algorithm>
#include <vector>

// ****************************************************************************
// Method: ExpressionList::ExpressionList
//
// Purpose:
//   Init utility for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void ExpressionList::resetMe(void) const
{
    // These members are mutable
    myHashVal = 0;
    sortedNameHashNeedsSorting = true;
}

void ExpressionList::Init()
{
    resetMe();
    ExpressionList::SelectAll();
}

// ****************************************************************************
// Method: ExpressionList::ExpressionList
//
// Purpose:
//   Copy utility for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void ExpressionList::Copy(const ExpressionList &obj)
{
    resetMe();
    AttributeGroupVector::const_iterator pos;

    // *** Copy the expressions field ***
    // Delete the AttributeGroup objects and clear the vector.
    for(pos = expressions.begin(); pos != expressions.end(); ++pos)
        delete *pos;
    expressions.clear();
    if(obj.expressions.size() > 0)
        expressions.reserve(obj.expressions.size());
    // Duplicate the expressions from obj.
    for(pos = obj.expressions.begin(); pos != obj.expressions.end(); ++pos)
    {
        Expression *oldExpression = (Expression *)(*pos);
        Expression *newExpression = new Expression(*oldExpression);
        expressions.push_back(newExpression);
    }

    ExpressionList::SelectAll();
}

// Type map format string
const char *ExpressionList::TypeMapFormatString = EXPRESSIONLIST_TMFS;
const AttributeGroup::private_tmfs_t ExpressionList::TmfsStruct = {EXPRESSIONLIST_TMFS};


// ****************************************************************************
// Method: ExpressionList::ExpressionList
//
// Purpose:
//   Default constructor for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

ExpressionList::ExpressionList() :
    AttributeSubject(ExpressionList::TypeMapFormatString)
{
    ExpressionList::Init();
}

// ****************************************************************************
// Method: ExpressionList::ExpressionList
//
// Purpose:
//   Constructor for the derived classes of ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

ExpressionList::ExpressionList(private_tmfs_t tmfs) :
    AttributeSubject(tmfs.tmfs)
{
    ExpressionList::Init();
}

// ****************************************************************************
// Method: ExpressionList::ExpressionList
//
// Purpose:
//   Copy constructor for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

ExpressionList::ExpressionList(const ExpressionList &obj) :
    AttributeSubject(ExpressionList::TypeMapFormatString)
{
    ExpressionList::Copy(obj);
}

// ****************************************************************************
// Method: ExpressionList::ExpressionList
//
// Purpose:
//   Copy constructor for derived classes of the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

ExpressionList::ExpressionList(const ExpressionList &obj, private_tmfs_t tmfs) :
    AttributeSubject(tmfs.tmfs)
{
    ExpressionList::Copy(obj);
}

// ****************************************************************************
// Method: ExpressionList::~ExpressionList
//
// Purpose:
//   Destructor for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

ExpressionList::~ExpressionList()
{
    AttributeGroupVector::iterator pos;

    // Destroy the expressions field.
    for(pos = expressions.begin(); pos != expressions.end(); ++pos)
        delete *pos;
}

// ****************************************************************************
// Method: ExpressionList::operator =
//
// Purpose:
//   Assignment operator for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

ExpressionList&
ExpressionList::operator = (const ExpressionList &obj)
{
    if (this == &obj) return *this;

    ExpressionList::Copy(obj);

    return *this;
}

// ****************************************************************************
// Method: ExpressionList::operator ==
//
// Purpose:
//   Comparison operator == for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

bool
ExpressionList::operator == (const ExpressionList &obj) const
{
    return GetHashVal() == obj.GetHashVal();
}

// ****************************************************************************
// Method: ExpressionList::operator !=
//
// Purpose:
//   Comparison operator != for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

bool
ExpressionList::operator != (const ExpressionList &obj) const
{
    return !(this->operator == (obj));
}

// ****************************************************************************
// Method: ExpressionList::TypeName
//
// Purpose:
//   Type name method for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

const std::string
ExpressionList::TypeName() const
{
    return "ExpressionList";
}

// ****************************************************************************
// Method: ExpressionList::CopyAttributes
//
// Purpose:
//   CopyAttributes method for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

bool
ExpressionList::CopyAttributes(const AttributeGroup *atts)
{
    if(TypeName() != atts->TypeName())
        return false;

    // Call assignment operator.
    const ExpressionList *tmp = (const ExpressionList *)atts;
    *this = *tmp;

    return true;
}

// ****************************************************************************
// Method: ExpressionList::CreateCompatible
//
// Purpose:
//   CreateCompatible method for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

AttributeSubject *
ExpressionList::CreateCompatible(const std::string &tname) const
{
    AttributeSubject *retval = 0;
    if(TypeName() == tname)
        retval = new ExpressionList(*this);
    // Other cases could go here too.

    return retval;
}

// ****************************************************************************
// Method: ExpressionList::NewInstance
//
// Purpose:
//   NewInstance method for the ExpressionList class.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

AttributeSubject *
ExpressionList::NewInstance(bool copy) const
{
    AttributeSubject *retval = 0;
    if(copy)
        retval = new ExpressionList(*this);
    else
        retval = new ExpressionList;

    return retval;
}

// ****************************************************************************
// Method: ExpressionList::SelectAll
//
// Purpose:
//   Selects all attributes.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void
ExpressionList::SelectAll()
{
    resetMe();
    Select(ID_expressions, (void *)&expressions);
}

// ****************************************************************************
// Method: ExpressionList::CreateSubAttributeGroup
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

AttributeGroup *
ExpressionList::CreateSubAttributeGroup(int)
{
    return new Expression;
}

///////////////////////////////////////////////////////////////////////////////
// Persistence methods
///////////////////////////////////////////////////////////////////////////////

// ****************************************************************************
// Method: ExpressionList::CreateNode
//
// Purpose:
//   This method creates a DataNode representation of the object so it can be saved to a config file.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   Thu Dec 18 11:24:07 PDT 2003
//
// Modifications:
//   Brad Whitlock, Fri Dec 14 16:04:18 PST 2007
//   Made it use ids.
//
// ****************************************************************************

bool
ExpressionList::CreateNode(DataNode *parentNode, bool completeSave, bool forceAdd)
{
    resetMe();
    if(parentNode == 0)
        return false;

    ExpressionList defaultObject;
    bool addToParent = false;
    // Create a node for ExpressionList.
    DataNode *node = new DataNode("ExpressionList");

    if(completeSave || !FieldsEqual(ID_expressions, &defaultObject))
    {
        for(size_t i = 0; i < expressions.size(); ++i)
        {
            Expression *expr = (Expression *)expressions[i];
            if(!expr->GetFromDB())
            {
                addToParent = true;
                expr->CreateNode(node, completeSave, true);
            }
        }
    }

    // Add the node to the parent node.
    if(addToParent || forceAdd)
        parentNode->AddNode(node);
    else
        delete node;

    return (addToParent || forceAdd);
}
// ****************************************************************************
// Method: ExpressionList::SetFromNode
//
// Purpose:
//   This method sets attributes in this object from values in a DataNode representation of the object.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void
ExpressionList::SetFromNode(DataNode *parentNode)
{
    resetMe();
    if(parentNode == 0)
        return;

    DataNode *searchNode = parentNode->GetNode("ExpressionList");
    if(searchNode == 0)
        return;

    DataNode **children;

    // Clear all the Expressions if we got any.
    bool clearedExpressions = false;
    // Go through all of the children and construct a new
    // Expression for each one of them.
    children = searchNode->GetChildren();
    if(children != 0)
    {
        for(int i = 0; i < searchNode->GetNumChildren(); ++i)
        {
            if(children[i]->GetKey() == std::string("Expression"))
            {
                if (!clearedExpressions)
                {
                    ClearExpressions();
                    clearedExpressions = true;
                }
                Expression temp;
                temp.SetFromNode(children[i]);
                AddExpressions(temp);
            }
        }
    }

}

///////////////////////////////////////////////////////////////////////////////
// Set property methods
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Get property methods
///////////////////////////////////////////////////////////////////////////////

const AttributeGroupVector &
ExpressionList::GetExpressions() const
{
    return expressions;
}

AttributeGroupVector &
ExpressionList::GetExpressions()
{
    resetMe();
    return expressions;
}

///////////////////////////////////////////////////////////////////////////////
// Select property methods
///////////////////////////////////////////////////////////////////////////////

void
ExpressionList::SelectExpressions()
{
    Select(ID_expressions, (void *)&expressions);
}

///////////////////////////////////////////////////////////////////////////////
// AttributeGroupVector convenience methods.
///////////////////////////////////////////////////////////////////////////////

// ****************************************************************************
// Method: ExpressionList::AddExpressions
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void
ExpressionList::AddExpressions(const Expression &obj)
{
    resetMe();
    Expression *newExpression = new Expression(obj);
    expressions.push_back(newExpression);

    // Indicate that things have changed by selecting it.
    Select(ID_expressions, (void *)&expressions);
}

// ****************************************************************************
// Method: ExpressionList::ClearExpressions
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void
ExpressionList::ClearExpressions()
{
    resetMe();
    AttributeGroupVector::iterator pos;

    for(pos = expressions.begin(); pos != expressions.end(); ++pos)
        delete *pos;
    expressions.clear();
    sortedNameHash.clear();

    // Indicate that things have changed by selecting the list.
    Select(ID_expressions, (void *)&expressions);
}

// ****************************************************************************
// Method: ExpressionList::RemoveExpressions
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

void
ExpressionList::RemoveExpressions(int index)
{
    resetMe();
    if ((size_t) index < expressions.size())
    {
        AttributeGroupVector::iterator pos = expressions.begin() + index;
        if (pos != expressions.end())
        {
            delete *pos;
            expressions.erase(pos);
        }
    }

    // Indicate that things have changed by selecting the list.
    Select(ID_expressions, (void *)&expressions);
}

// ****************************************************************************
// Method: ExpressionList::GetNumExpressions
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

int
ExpressionList::GetNumExpressions() const
{
    return (int)expressions.size();
}

// ****************************************************************************
// Method: ExpressionList::GetExpressions
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

Expression &
ExpressionList::GetExpressions(int i)
{
    resetMe();
    return *((Expression *)expressions[i]);
}

// ****************************************************************************
// Method: ExpressionList::GetExpressions
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

const Expression &
ExpressionList::GetExpressions(int i) const
{
    return *((Expression *)expressions[i]);
}

// ****************************************************************************
// Method: ExpressionList::operator []
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

Expression &
ExpressionList::operator [] (int i)
{
    resetMe();
    return *((Expression *)expressions[i]);
}

// ****************************************************************************
// Method: ExpressionList::operator []
//
// Purpose:
//   This class contains a list of expressions and some functions to manipulate them.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

const Expression &
ExpressionList::operator [] (int i) const
{
    return *((Expression *)expressions[i]);
}

///////////////////////////////////////////////////////////////////////////////
// Keyframing methods
///////////////////////////////////////////////////////////////////////////////

// ****************************************************************************
// Method: ExpressionList::GetFieldName
//
// Purpose:
//   This method returns the name of a field given its index.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

std::string
ExpressionList::GetFieldName(int index) const
{
    switch (index)
    {
    case ID_expressions: return "expressions";
    default:  return "invalid index";
    }
}

// ****************************************************************************
// Method: ExpressionList::GetFieldType
//
// Purpose:
//   This method returns the type of a field given its index.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

AttributeGroup::FieldType
ExpressionList::GetFieldType(int index) const
{
    switch (index)
    {
    case ID_expressions: return FieldType_attVector;
    default:  return FieldType_unknown;
    }
}

// ****************************************************************************
// Method: ExpressionList::GetFieldTypeName
//
// Purpose:
//   This method returns the name of a field type given its index.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

std::string
ExpressionList::GetFieldTypeName(int index) const
{
    switch (index)
    {
    case ID_expressions: return "attVector";
    default:  return "invalid index";
    }
}

// ****************************************************************************
// Method: ExpressionList::FieldsEqual
//
// Purpose:
//   This method compares two fields and return true if they are equal.
//
// Note:       Autogenerated by xml2atts.
//
// Programmer: xml2atts
// Creation:   omitted
//
// Modifications:
//
// ****************************************************************************

bool
ExpressionList::FieldsEqual(int index_, const AttributeGroup *rhs) const
{
    const ExpressionList &obj = *((const ExpressionList*)rhs);
    bool retval = false;
    switch (index_)
    {
    case ID_expressions:
        {  // new scope
        bool expressions_equal = (obj.expressions.size() == expressions.size());
        for(size_t i = 0; (i < expressions.size()) && expressions_equal; ++i)
        {
            // Make references to Expression from AttributeGroup *.
            const Expression &expressions1 = *((const Expression *)(expressions[i]));
            const Expression &expressions2 = *((const Expression *)(obj.expressions[i]));
            expressions_equal = (expressions1 == expressions2);
        }

        retval = expressions_equal;
        }
        break;
    default: retval = false;
    }

    return retval;
}

///////////////////////////////////////////////////////////////////////////////
// User-defined methods.
///////////////////////////////////////////////////////////////////////////////
static bool compNameHashPairs(std::pair<unsigned int, size_t> const &a,
                              std::pair<unsigned int, size_t> const &b)
{
    return a.first < b.first;
}


void ExpressionList::SortNameHash(void) const // only mutables changed
{
    sortedNameHash.resize(expressions.size());
    for (size_t i = 0; i < expressions.size(); i++)
    {
        Expression const *e = (Expression *) expressions[i];
        unsigned int hashval = BJHash::Hash(e->GetName());
        sortedNameHash[i] = std::pair<unsigned int, size_t>(hashval, i);
    }

    std::sort(sortedNameHash.begin(), sortedNameHash.end(), compNameHashPairs);
    sortedNameHashNeedsSorting = false;
}

// Modifications:
//   Brad Whitlock, Thu Aug 28 15:29:59 PST 2003
//   Simplified and removed dynamic_cast so it works on Windows.
//
Expression *
ExpressionList::operator[](const char *varname)
{
    std::string var(varname);
    unsigned int hashval = BJHash::Hash(var);

    if (expressions.size() != sortedNameHash.size() || sortedNameHashNeedsSorting)
        SortNameHash();

    // Binary search to match hash, then linear over entries with same hash val
    std::pair< std::vector<std::pair<unsigned int, size_t> >::iterator,
               std::vector<std::pair<unsigned int, size_t> >::iterator > p =
    std::equal_range(sortedNameHash.begin(), sortedNameHash.end(),
        std::pair<unsigned int, size_t>(hashval, 0), compNameHashPairs);
    for (std::vector<std::pair<unsigned int, size_t> >::iterator i = p.first;
         i != p.second; ++i)
    {
        int eidx = i->second;
        Expression *e = (Expression *) expressions[eidx];
        if (e->GetName() == var)
        {
            resetMe(); // only if we actually return something
            return e;
        }
    }

    return 0;
}

Expression const *
ExpressionList::operator[](const char *varname) const
{
    std::string var(varname);
    unsigned int hashval = BJHash::Hash(var);

    if (expressions.size() != sortedNameHash.size() || sortedNameHashNeedsSorting)
        SortNameHash();

    // Binary search to match hash, then linear over entries with same hash val
    std::pair< std::vector<std::pair<unsigned int, size_t> >::iterator,
               std::vector<std::pair<unsigned int, size_t> >::iterator > p =
    std::equal_range(sortedNameHash.begin(), sortedNameHash.end(),
        std::pair<unsigned int, size_t>(hashval, 0), compNameHashPairs);
    for (std::vector<std::pair<unsigned int, size_t> >::iterator i = p.first;
         i != p.second; ++i)
    {
        int eidx = i->second;
        Expression *e = (Expression *) expressions[eidx];
        if (e->GetName() == var)
            return e;
    }

    return 0;
}

int
ExpressionList::IndexOf(char const *varname) const
{
    std::string var(varname);
    unsigned int hashval = BJHash::Hash(var);

    if (expressions.size() != sortedNameHash.size() || sortedNameHashNeedsSorting)
        SortNameHash();

    // Binary search to match hash, then linear over entries with same hash val
    std::pair< std::vector<std::pair<unsigned int, size_t> >::iterator,
               std::vector<std::pair<unsigned int, size_t> >::iterator > p =
    std::equal_range(sortedNameHash.begin(), sortedNameHash.end(),
        std::pair<unsigned int, size_t>(hashval, 0), compNameHashPairs);
    for (std::vector<std::pair<unsigned int, size_t> >::iterator i = p.first;
         i != p.second; ++i)
    {
        int eidx = i->second;
        Expression *e = (Expression *) expressions[eidx];
        if (e->GetName() == var)
            return eidx;
    }

    return -1;
}

// ****************************************************************************
// Method: ExpressionList::GetAllVarNames
//
// Purpose:
//   This method retrieves all the expression var names defined for the
//   passed database name.
//
// Arguments:
//   dbN       The database name for which to return var names.
//
// Returns:
//   A list of expression var names defined for the given database.
//
// Programmer: Kathleen Bonnell
// Creation:   August 25, 2004
//
// Modifications:
//
// ****************************************************************************

const stringVector
ExpressionList::GetAllVarNames(const std::string &dbN) const
{
    stringVector vars;
    for (int i = 0; i < GetNumExpressions(); i++)
    {
        Expression *e = (Expression*)expressions[i];
        if (!e->GetHidden() && e->GetDbName() == dbN)
            vars.push_back(e->GetName());
    }
    return vars;
}

unsigned int
ExpressionList::GetHashVal() const
{
    if (myHashVal != 0) return myHashVal;

    for (size_t i = 0; i < expressions.size(); i++)
    {
        Expression *e = (Expression*)expressions[i];

        myHashVal = BJHash::Hash(e->GetName(), myHashVal);
        myHashVal = BJHash::Hash(e->GetDefinition(), myHashVal);
        myHashVal = BJHash::Hash(e->GetDbName(), myHashVal);
        myHashVal = BJHash::Hash(e->GetMeshName(), myHashVal);
        myHashVal = BJHash::Hash(e->GetOperatorName(), myHashVal);
        struct {Expression::ExprType et; bool Bools[4];} expstuff = {
            e->GetType(),
            e->GetHidden(),
            e->GetFromDB(),
            e->GetFromOperator(),
            e->GetAutoExpression()};
        myHashVal = BJHash::Hash((void*)&expstuff, sizeof(expstuff), myHashVal);
    }

    return myHashVal;
}

// ****************************************************************************
// Method: ExpressionList::SortExpressions
//
// Purpose:
//   Sort the list of expressions by name. This is mainly for use in the gui
//   Expression list.
//
// Programmer: Alister Maguire
// Creation:   Mon Mar 16 10:17:59 PDT 2020
//
// Modifications:
//
// ****************************************************************************

void
ExpressionList::SortExpressions(void)
{
    auto comp = [](const AttributeGroup *lha, const AttributeGroup *rha) -> bool
        { return ((Expression *)lha)->GetName() < ((Expression *)rha)->GetName(); };

    sort(expressions.begin(), expressions.end(), comp);
    resetMe();
}
