// $Id: JGObject.cpp,v 1.7 2003/03/16 14:51:01 fukasawa Exp $

//=============================================================================
/**
 *  @file    JGObject.cpp
 *
 *  @author  Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 2001-2003 BEE Co.,Ltd. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
//=============================================================================

#define BEE_BUILD_DLL

#include "JGObject.h"
#include "JGAttribute.h"
#include "JGAttrData.h"
#include "JGErrorList.h"
#include "BS2ListItem.h"
#include "BS2DeclAtoms.h"

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
JGObject::JGObject(JGClass * cls, const BCHAR * name)
        : b_object((b_objtype *)cls, name)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGObject::JGObject");

    m_base = NULL;

    return ;
}

//-----------------------------------------------------------------------------
JGObject::JGObject(const JGObject& rhs) : b_object((b_object&)rhs)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGObject::JGObject");

    m_base = NULL;

    return ;
}

//-----------------------------------------------------------------------------
JGObject::~JGObject()
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGObject::~JGObject");
}

//-----------------------------------------------------------------------------
// Copy
//-----------------------------------------------------------------------------
JGObject& JGObject::operator=(const JGObject& rhs)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGObject::operator=");
    if (this == &rhs)
        return *this;

    this->b_object::copy((b_object&)rhs);

    m_base = rhs.m_base;
    return *this;
}

//-----------------------------------------------------------------------------
// Get object specification item
//-----------------------------------------------------------------------------
BS2Item * JGObject::makeObjSpec()
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::makeObjSpec");

    BS2Atom * atom = new BS2Ascii(this->objSpec());
    BS2Item * item = BS2Item::factory(_TX("OBJSPEC"), atom);

    return item;
}

//-----------------------------------------------------------------------------
// Get value by attribute name
//-----------------------------------------------------------------------------
BS2Item * JGObject::makeAttrList()
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::makeAttrList");
    BS2ListItem * listitem = new BS2ListItem;
    AttrDataVect::iterator iter;
    for (iter = m_values.begin(); iter != m_values.end(); iter++)
    {
        JGAttrData * data = (JGAttrData *)(*iter);
        if ((data->name() == _TX("ObjID")) ||
            (data->name() == _TX("ObjType")))
        {
            continue;
        }
        BS2Item * mbrlist = data->getItem();
        listitem->add(mbrlist);
    }

    return listitem;
}

//-----------------------------------------------------------------------------
BS2Item * JGObject::makeAttrList(vector<JGid>& attrids)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::makeAttrList");

    if (attrids.size() == 0)
    {   // Set all attributes
        return this->makeAttrList();
    }

    BS2ListItem * attrlist = new BS2ListItem;
    for (size_t i = 0; i < attrids.size(); i++)
    {
        string attrName;
        attrids[i].get(attrName);
        JGAttrData * data = (JGAttrData *)(this->findData(attrName));
        if (data == NULL)
        {
            delete attrlist;
            return NULL;
        }
        BS2Item * mbrlist = data->getItem();
        attrlist->add(mbrlist);
    }

    return attrlist;
}

//-----------------------------------------------------------------------------
BS2Item * JGObject::makeIDandAttrList(vector<JGid>& attrids)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::makeIDandAttrList");

    BS2Atom * atom = new BS2Ascii(m_name);
    BS2ListItem * listitem = new BS2ListItem;
    BS2Item * item = BS2Item::factory(_TX("OBJID"), atom);
    listitem->add(item);
    listitem->add(this->makeAttrList(attrids));
    return listitem;
}

//-----------------------------------------------------------------------------
// Relational attribute
//-----------------------------------------------------------------------------
int JGObject::relation(vector<JGAttrFilter>& filters, JGErrorList& errlist)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::relation");
    int result = 0;
    for (size_t i = 0; i < filters.size(); i++)
    {
        string attrid;
        filters[i].m_attrid.get(attrid);
        JGAttrData * data = (JGAttrData *)(this->findData(attrid));
        if (data == NULL)
        {
            if (filters[i].m_attrreln == JGAttrFilter::ABSENT)
            {
                continue;
            }
            errlist.append(ERRCODE_UNKNOWN_ATTRIBUTE, attrid);
            result = -1;
        }
        else  if (filters[i].m_attrreln == JGAttrFilter::PRESENT)
        {
            continue;
        }
        else
        {

            if ((filters[i].m_attrreln == JGAttrFilter::EQUAL) &&
                (data->get() != (b_value&)filters[i].m_attrdata))
            {
                result = 1;
                break;
            }
            else if ((filters[i].m_attrreln == JGAttrFilter::NOT_EQUAL) &&
                     (data->get() == (b_value&)filters[i].m_attrdata))
            {
                result = 1;
                break;
            }
            else if ((filters[i].m_attrreln == JGAttrFilter::LESS) &&
                     (! (data->get() < (b_value&)filters[i].m_attrdata)))
            {
                result = 1;
                break;
            }
            else if ((filters[i].m_attrreln == JGAttrFilter::LESS_OR_EQUAL) &&
                     (! (data->get() < (b_value&)filters[i].m_attrdata ||
                         data->get() == (b_value&)filters[i].m_attrdata)))
            {
                result = 1;
                break;
            }
            else if ((filters[i].m_attrreln == JGAttrFilter::GREATER) &&
                     (data->get() < (b_value&)filters[i].m_attrdata ||
                      data->get() == (b_value&)filters[i].m_attrdata))
            {
                result = 1;
                break;
            }
            else if ((filters[i].m_attrreln == JGAttrFilter::GREATER_EQUAL) &&
                     (data->get() < (b_value&)filters[i].m_attrdata))
            {
                result = 1;
                break;
            }
            else
            {
                result = 1;
                break;
            }
        }
    }

    return result;
}

//-----------------------------------------------------------------------------
// Set attribute data
//-----------------------------------------------------------------------------
int JGObject::set(vector<JGAttrFilter>& filters, JGErrorList& errlist)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::set");
    int result = 0;
    for (size_t i = 0; i < filters.size(); i++)
    {
        string attrid;
        filters[i].m_attrid.get(attrid);
        JGAttrData * data = (JGAttrData *)(this->findData(attrid));
        if (data != NULL)
        {
            // if (filters[i].m_link.empty())
            {
                data->set(filters[i].m_attrdata);
            }
        }
        else
        {   // Not found attribute in top layer
            errlist.append(ERRCODE_UNKNOWN_ATTRIBUTE, attrid);
            result = -1;
        }
    }

    return result;
}

//-----------------------------------------------------------------------------
// Dump
//-----------------------------------------------------------------------------
void JGObject::dump() const
{
    this->b_object::dump();
}


//-----------------------------------------------------------------------------
//
// Helper which set ID and DATA of Message
//
//-----------------------------------------------------------------------------
//
// nest layer
//    +1: array of primitive
//    +2: structure
//    +3: array of structure
//
//
//-----------------------------------------------------------------------------
// Setup attribute data by id
//-----------------------------------------------------------------------------
int JGObject::Setter::setID(JGid& id, int nest)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::setID");

    int result = 0;
    string attrid;
    id.get(attrid);
    if (! m_objMember)
    {
        m_topLayer = nest;
        m_objMember = true;
    }
    if (nest == m_topLayer)
    {
        JGAttrData * data = (JGAttrData *)(m_obj->findData(attrid));
        if (data == NULL)
        {
            TRACE_ERROR((_TX("Can't find id(%s) in top level.\n"),
                         attrid.c_str()));
            m_errs->append(ERRCODE_INVALID_PARAMETER, attrid);
            return -1;
        }
        m_current.m_attrdata = data;
        m_current.m_attr = data->attribute();
        m_current.m_nodedata = NULL;
        if (m_current.m_attr->isVector() || m_current.m_attr->isStruct())
        {
            m_current.m_nodedata = m_current.m_attrdata;
        }
        m_current.m_layer = m_topLayer;
        m_current.m_row = 0;
        m_current.m_dummy = false;
        m_current.m_dummyCount = 0;
        m_current.m_beforeNode = NULL;
    }
    else
    {
        result = setSameLevelID(attrid);
    }
    m_current.m_layer = nest;
    return result;
}

//-----------------------------------------------------------------------------
// Set attribute data
//-----------------------------------------------------------------------------
int JGObject::Setter::setData(JGvalue& value, int nest)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::setData");

    int result = 0;
    JGAttribute * mbrAttr;
    JGAttribute * attr = m_current.m_attr;
    JGAttrData * data = m_current.m_attrdata;
    if (attr == NULL)
    {
        TRACE_ERROR((_TX("Illega object format.\n")));
        m_errs->append(ERRCODE_INVALID_PARAMETER, _TX("DATA"));
        return -1;
    }

    if (nest == m_topLayer)
    {
        if (attr->isVector())
        {
            JGVector * vecAttr = reinterpret_cast<JGVector *>(attr);
            mbrAttr = vecAttr->member();
            JGAttrData * mbrData = reinterpret_cast<JGAttrData *>(mbrAttr->instance(m_obj));
            value.get(*mbrData);
            data->m._vec->push_back(mbrData);
        }
        else
        {
            value.get(*data);
        }
    }
    else
    {
        result = setSameLevelDATA(value);
    }
    m_current.m_layer = nest;
    return result;
}

//-----------------------------------------------------------------------------
// Setup attribute data in the same layer
//-----------------------------------------------------------------------------
int JGObject::Setter::setSameLevelID(string& attrid)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::setSameLevelID");

    JGAttrData * mbrData;
    JGAttribute * mbrAttr;
    if (m_current.m_attr == NULL)
    {
        TRACE_ERROR((_TX("Only ID/NAME(%s) in nest level.\n"),
                     attrid.c_str()));
        m_errs->append(ERRCODE_INVALID_PARAMETER, _TX("ID"));
        return -1;
    }

    JGAttribute * attr = m_save.top().m_attr;
    if (attr->isVector())
    {   // which is vector, construct the new data
        JGVector * vecAttr = reinterpret_cast<JGVector *>(attr);
        mbrAttr = vecAttr->member();
        mbrData = reinterpret_cast<JGAttrData *>(mbrAttr->instance(m_obj));
        m_save.top().m_nodedata->m._vec->push_back(mbrData);
    }
    else if (attr->isStruct())
    {   // which is struct, data has been already created.
        // JGStruct * clsAttr = reinterpret_cast<JGStruct *>(attr);
        JGAttrData * strukt = m_save.top().m_nodedata;
        b_valvect * members = strukt->m._vec;
        int i, q = members->size();
        for (i = 0; i < q; i++)
        {
            mbrData = reinterpret_cast<JGAttrData *>((*members)[i]);
            mbrAttr = mbrData->attribute();
//            mbrAttr = clsAttr->at(i);
            if (mbrAttr->name() == attrid)
            {
                break;
            }
        }
        if (i == q)
        {
            TRACE_ERROR((_TX("Not found id(%s) in nested struct.\n"),
                         attrid.c_str()));
            m_errs->append(ERRCODE_INVALID_PARAMETER, attrid);
            return -1;
        }
        m_current.m_attrdata = mbrData;
    }
    else
    {
        TRACE_ERROR((_TX("Illegal object structure as parent attribute is not vecter or struct (%s).\n"),
                    attrid.c_str()));
        return -1;
    }
    m_save.top().m_row++;
    m_current.m_attrdata = mbrData;

    return 0;
}

//-----------------------------------------------------------------------------
// Setup attribute data in the same layer
//-----------------------------------------------------------------------------
int JGObject::Setter::setSameLevelDATA(JGvalue& value)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::setSameLevelDATA");

    JGAttribute * mbrAttr;
    if (m_current.m_attr == NULL)
    {
        m_errs->append(ERRCODE_INVALID_PARAMETER, _TX("DATA"));
        return -1;
    }

    if (m_current.m_attr->isVector())
    {
        JGVector * vecAttr = reinterpret_cast<JGVector *>(m_current.m_attr);
        mbrAttr = vecAttr->member();
        JGAttrData * mbrData = reinterpret_cast<JGAttrData *>(mbrAttr->instance(m_obj));
        value.get(*mbrData);
        m_current.m_nodedata->m._vec->push_back(mbrData);
    }
    else if ((! m_save.empty()) && m_save.top().m_attr->isVector())
    {
        mbrAttr = m_current.m_attr;
        JGAttrData * mbrData = reinterpret_cast<JGAttrData *>(mbrAttr->instance(m_obj));
        value.get(*mbrData);
        m_save.top().m_nodedata->m._vec->push_back(mbrData);
    }
    else  /* if (m_current.m_attr->isStruct()) */
    {
        value.get(m_current.m_attrdata->get());
    }
    return 0;
}

//-----------------------------------------------------------------------------
// Down nested layer
//-----------------------------------------------------------------------------
int JGObject::Setter::downLayer()
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::downLayer");

    JGAttribute * mbrAttr;
    JGAttribute * attr = m_current.m_attr;
    if (attr == NULL)
    {
        TRACE_ERROR((_TX("Illega object format.\n")));
        m_errs->append(ERRCODE_INVALID_PARAMETER, _TX("ATTRID"));
        return -1;
    }

    if (! m_current.m_dummy)
    {
        JGAttrData * beforeNode = m_current.m_nodedata;
        int rel = m_current.m_row;

        if (attr->isVector())
        {
            m_save.push(m_current);   // Save recent node info.
            JGVector * vecAttr = reinterpret_cast<JGVector *>(attr);
            mbrAttr = vecAttr->member();
            if (mbrAttr->isVector() || mbrAttr->isStruct())
            {
                m_current.m_nodedata = reinterpret_cast<JGAttrData *>(mbrAttr->instance(m_obj));
                beforeNode->m._vec->push_back(m_current.m_nodedata);
            }
            else
            {
                m_current.m_nodedata = NULL;
            }
            m_current.m_beforeNode = beforeNode;
        }
        else if (attr->isStruct())
        {
            m_save.push(m_current);   // Save recent node info.
            JGStruct * clsAttr = reinterpret_cast<JGStruct *>(attr);
            mbrAttr = clsAttr->at(rel);
            if (mbrAttr == NULL)
            {
                TRACE_ERROR((_TX("Illegal member position %d.\n"), rel));
                return -1;
            }
            if (mbrAttr->isVector() || mbrAttr->isStruct())
            {
                m_current.m_nodedata = reinterpret_cast<JGAttrData *>(mbrAttr->instance(m_obj));
                beforeNode->m._vec->push_back(m_current.m_nodedata);
            }
            else
            {
                m_current.m_nodedata = NULL;
            }
            m_current.m_beforeNode = beforeNode;
        }
        else if (m_save.size() > 1)
        {
            NodeInfo dummy(m_save.top());
            dummy.m_dummy = true;
            dummy.m_row = 0;
            if (m_save.top().m_dummyCount > 0)
            {   // First node has been created
                if (dummy.m_attr->isVector() || dummy.m_attr->isStruct())
                {
                    dummy.m_nodedata = reinterpret_cast<JGAttrData *>(dummy.m_attr->instance(m_obj));
                    dummy.m_beforeNode->m._vec->push_back(dummy.m_nodedata);
                }
                else
                {
                    dummy.m_nodedata = NULL;
                }
            }
            m_save.top().m_dummyCount++;
            m_save.push(dummy);

            mbrAttr = attr;
        }
        else
        {
            mbrAttr = NULL;
        }
        m_current.m_attr = mbrAttr;
        m_current.m_attrdata = m_current.m_nodedata;
        m_current.m_row = 0;
    }
    else
    {
        if (attr->isVector())
        {
            m_save.push(m_current);   // Save recent node info.
            JGVector * vecAttr = reinterpret_cast<JGVector *>(attr);
            mbrAttr = vecAttr->member();
        }
        else if (attr->isStruct())
        {
            m_save.push(m_current);   // Save recent node info.
            JGStruct * clsAttr = reinterpret_cast<JGStruct *>(attr);
            mbrAttr = clsAttr->at(m_current.m_row);
            if (mbrAttr == NULL)
            {
                TRACE_ERROR((_TX("Illegal member position %d.\n"), m_current.m_row));
            }
        }
        else
        {
            TRACE_ERROR((_TX("Attribute is not node(%s).\n"),
                        attr->charName()));
            m_errs->append(ERRCODE_INVALID_PARAMETER, _TX("ID"));
            return -1;
        }
        m_current.m_attr = mbrAttr;
    }

    return 0;
}

//-----------------------------------------------------------------------------
// Up nested layer
//-----------------------------------------------------------------------------
int JGObject::Setter::upLayer()
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::upLayer");

    m_current = m_save.top();
    m_save.pop();
    return 0;
}

//-----------------------------------------------------------------------------
// Enter/Exit list node
//-----------------------------------------------------------------------------
int JGObject::Setter::enterList(int nest)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::enterList");

    if (m_objMember && ((m_topLayer + 1) < nest))
    {
        downLayer();
    }
    return 0;
}

//-----------------------------------------------------------------------------
int JGObject::Setter::exitList(int nest)
{
    TRACE_FUNCTION(TRL_LOW, "JGObject::Setter::exitList");

    if (m_objMember && ((m_topLayer + 1) < nest))
    {
        upLayer();
    }
    return 0;
}
