/*************************************************************************
  FILE:  BddSdSub.cpp
  DESCR: Processing subsystem file(.sub)
  AUTH:  Raoguang Song, Yu Wang
  DATE:  (C) Jan, 2006, 2009
*************************************************************************/
#include "BddSdSub.h"
#include <string>
#include "BddSdDES.h"
#include <map>
#include <cassert>
#include <fdd.h>
#include "BddSdPubfunc.h"
#include "BddSdErrmsg.h"
#include "BddSdType.h"



using namespace std;

namespace BDDSD
{

/**
 * DESCR:   Constructor
 * PARA:    vsSubFile: subsystem file name with path (.sub)(input)
 * RETURN:  None
 * ACCESS:  public
 */
CSub::CSub(const string & vsSubFile)
{
    m_AllEventsMap.clear();
    m_InvAllEventsMap.clear();

    m_iErrCode = 0;
    m_sErrMsg.clear();

    m_sSubFile = vsSubFile;
    m_sSubName.clear();

    m_iNumofPlants = -1;
    m_iNumofSpecs = -1;

    m_pDESArr = NULL;

    m_SubEventsMap.clear();

    m_usiMaxCon = 0xFFFF;
    m_usiMaxUnCon = 0x0;

    m_piDESOrderArr = NULL;
    m_piDESPosArr = NULL;

    InitBddFields();
}

/**
 * DESCR:   Destructor
 * PARA:    None
 * RETURN:  None
 * ACCESS:  public
 */
CSub::~CSub()
{
    if (m_pDESArr != NULL)
    {
        int iNumofDES = this->GetNumofDES();

        for (int i = 0; i < iNumofDES; i++)
        {
            if (m_pDESArr[i] != NULL)
            {
                    delete m_pDESArr[i];
                    m_pDESArr[i] = NULL;
            }
        }
        delete[] m_pDESArr;
        m_pDESArr = NULL;
    }


    delete[] m_piDESOrderArr;
    m_piDESOrderArr = NULL;
    delete[] m_piDESPosArr;
    m_piDESPosArr = NULL;

    ClearBddFields();
}

/*
 * DESCR:   Initialize BDD related data members
 * PARA:    None
 * RETURN:  0
 * ACCESS:  protected
 */
int CSub::InitBddFields()
{
    m_iNumofBddNormVar = 0;
    m_bddInit = bddtrue;
    m_bddMarking = bddtrue;
    m_bddSuper = bddfalse;

        m_pbdd_ConTrans = NULL;
        m_pbdd_ConPlantTrans = NULL;
        m_pbdd_ConSupTrans = NULL;

        m_pbdd_UnConTrans = NULL;
        m_pbdd_UnConPlantTrans = NULL;
        m_pbdd_UnConSupTrans = NULL;

        m_pbdd_ConVar = NULL;
        m_pbdd_ConVarPrim = NULL;

        m_pbdd_UnConVar = NULL;
        m_pbdd_UnConVarPrim = NULL;

        m_pbdd_UnConPlantVar = NULL;
        m_pbdd_UnConPlantVarPrim = NULL;

        m_pbdd_UnConSupVar = NULL;
        m_pbdd_UnConSupVarPrim = NULL;

        m_pbdd_ConPhysicVar = NULL;
        m_pbdd_ConPhysicVarPrim = NULL;

        m_pbdd_ConSupVar = NULL;
        m_pbdd_ConSupVarPrim = NULL;

        m_pPair_Con = NULL;
        m_pPair_UnCon = NULL;
        m_pPair_ConPrim = NULL;
        m_pPair_UnConPrim = NULL;

    return 0;
}

/*
 * DESCR:   Release memory for BDD related data members
 * PARA:    None
 * RETURN:  0
 * ACCESS:  protected
 */
int CSub::ClearBddFields()
{
        delete[] m_pbdd_ConTrans;
        m_pbdd_ConTrans = NULL;

        delete[] m_pbdd_ConPlantTrans;
        m_pbdd_ConPlantTrans = NULL;

        delete[] m_pbdd_ConSupTrans;
        m_pbdd_ConSupTrans = NULL;

        delete[] m_pbdd_UnConTrans;
        m_pbdd_UnConTrans = NULL;

        delete[] m_pbdd_UnConPlantTrans;
        m_pbdd_UnConPlantTrans = NULL;

        delete[] m_pbdd_UnConSupTrans;
        m_pbdd_UnConSupTrans = NULL;

        delete[] m_pbdd_ConVar;
        m_pbdd_ConVar = NULL;
        delete[] m_pbdd_UnConVar;
        m_pbdd_UnConVar = NULL;

        delete[] m_pbdd_ConVarPrim;
        m_pbdd_ConVarPrim = NULL;
        delete[] m_pbdd_UnConVarPrim;
        m_pbdd_UnConVarPrim = NULL;

        delete[] m_pbdd_UnConPlantVar;
        m_pbdd_UnConPlantVar = NULL;
        delete[] m_pbdd_UnConPlantVarPrim;
        m_pbdd_UnConPlantVarPrim = NULL;

        delete[] m_pbdd_UnConSupVar;
        m_pbdd_UnConSupVar = NULL;
        delete[] m_pbdd_UnConSupVarPrim;
        m_pbdd_UnConSupVarPrim = NULL;

        delete[] m_pbdd_ConPhysicVar;
        m_pbdd_ConPhysicVar = NULL;
        delete[] m_pbdd_ConPhysicVarPrim;
        m_pbdd_ConPhysicVarPrim = NULL;

        delete[] m_pbdd_ConSupVar;
        m_pbdd_ConSupVar = NULL;
        delete[] m_pbdd_ConSupVarPrim;
        m_pbdd_ConSupVarPrim = NULL;

        if (m_pPair_UnCon != NULL)
        {
            for (int i = 0; i < m_usiMaxUnCon; i += 2)
            {
                if (m_pPair_UnCon[i/2] != NULL)
                {
                    bdd_freepair(m_pPair_UnCon[i/2]);
                    m_pPair_UnCon[i/2] = NULL;
                }
            }
            delete[] m_pPair_UnCon;
            m_pPair_UnCon = NULL;
        }

        if (m_pPair_Con != NULL)
        {
            for (int i = 1; i < (unsigned short)(m_usiMaxCon + 1); i += 2)
            {
                if (m_pPair_Con[(i - 1)/2] != NULL)
                {
                    bdd_freepair(m_pPair_Con[(i - 1)/2]);
                    m_pPair_Con[(i - 1)/2] = NULL;
                }
            }
            delete[] m_pPair_Con;
            m_pPair_Con = NULL;
        }

        if (m_pPair_UnConPrim != NULL)
        {
            for (int i = 0; i < m_usiMaxUnCon; i += 2)
            {
                if (m_pPair_UnConPrim[i/2] != NULL)
                {
                    bdd_freepair(m_pPair_UnConPrim[i/2]);
                    m_pPair_UnConPrim[i/2] = NULL;
                }
            }
            delete[] m_pPair_UnConPrim;
            m_pPair_UnConPrim = NULL;
        }

        if (m_pPair_ConPrim != NULL)
        {
            for (int i = 1; i < (unsigned short)(m_usiMaxCon + 1); i += 2)
            {
                if (m_pPair_ConPrim[(i - 1)/2] != NULL)
                {
                    bdd_freepair(m_pPair_ConPrim[(i - 1)/2]);
                    m_pPair_ConPrim[(i - 1)/2] = NULL;
                }
            }
            delete[] m_pPair_ConPrim;
            m_pPair_ConPrim = NULL;
        }

    return 0;
}

/*
 * DESCR:   Generate a DES file name with path (*.hsc) from a sub file name
 *          with path (.sub) and a DES file name without path.
 *          ex: vsSubFile = "/home/roger/high.sub", vsDES = "AttchCase.hsc",
 *              will return "/home/roger/AttchCase.hsc"
 * PARA:    vsSubFile: sub file name with path
 *          vsDES: DES file name without path
 * RETURN:  Generated DES file name with path
 * ACCESS:  protected
 */
string CSub::GetDESFileFromSubFile(const string & vsSubFile,
                        const string &vsDES)
{
    assert(vsSubFile.length() > 4);
    assert(vsSubFile.substr(vsSubFile.length() - 4) == ".sub");
    assert(vsDES.length() > 0);
    string sDES = vsDES;

    if (sDES.length() > 4)
    {
        if (sDES.substr(sDES.length() - 4) == ".hsc")
        {
            sDES = sDES.substr(0, sDES.length() - 4);
        }
    }
    sDES += ".hsc";

    unsigned int iPos = vsSubFile.find_last_of('/');

    if ( iPos == string::npos)
        return sDES;
    else
        return vsSubFile.substr(0, iPos + 1) + sDES;
}

/**
 * DESCR:   Add events to the event Map of this sub. If the event already exits,
 *          return its index; Otherwise generate a new 16 bit unsigned index
 *          and return the index.
 * PARA:    vsEventName:    Event name
 *          vEventType: Controllable? (CON_EVENT, UNCON_EVENT)
 * RETURN:  >0: event index (odd: controllable  even: uncontrollable)
 *          0:  error
 * ACCESS:  public
 */
unsigned short CSub::AddSubEvent(const string & vsEventName,
                                     const EVENTTYPE vEventType)
{
    const char * DEBUG = "CSub::AddSubEvent():";
    PRINT_DEBUG << "vsEventName = " << vsEventName << endl;

    LOCALEVENTS::const_iterator citer;

    citer = m_SubEventsMap.find(vsEventName);

    if (citer != m_SubEventsMap.end())  //the event exists, return its index
        return citer->second;
    else  //the event does not exist, generate a new index.
    {
        if (vEventType == CON_EVENT)
        {
            m_usiMaxCon += 2;
            m_SubEventsMap[vsEventName] = m_usiMaxCon;
            m_InvSubEventsMap[m_usiMaxCon] = vsEventName;
            #ifdef DEBUG_TIME
            PRINT_DEBUG << "vEventType = CON_EVENT, m_usiMaxCon = " << m_usiMaxCon << endl;
            #endif

            return m_usiMaxCon;
        }
        else
        {
            m_usiMaxUnCon += 2;
            m_SubEventsMap[vsEventName] = m_usiMaxUnCon;
            m_InvSubEventsMap[m_usiMaxUnCon] = vsEventName;

            #ifdef DEBUG_TIME
            PRINT_DEBUG << "vEventType = UNCON_EVENT, m_usiMaxUnCon = " << m_usiMaxUnCon << endl;
            #endif

            return m_usiMaxUnCon;
        }
    }
    return 0;
}

/**
 * DESCR:   Set error msg and err code in this project
 * PARA:    vsvsErrMsg: Error message
 *          viErrCode: Error Code
 * RETURN:  None
 * ACCESS:  public
 */
void CSub::SetErr(const string & vsErrMsg, const int viErrCode)
{
    m_iErrCode = viErrCode;
    m_sErrMsg = vsErrMsg;
    return;
}

/**
 * DESCR:   Generate global event index from the event info in para
 * PARA:    viSubIndex(Sub index, highsub = 0, low sub start from 1.
 *                     Next 12 bits), (input)
 *          vusiLocalEventIndex(local event index, odd: controllable,
 *                              even:uncontrollab. The rest 16 bits) (input)
 * RETURN:  Generated global event index
 * ACCESS:  public
 */
int CSub::GenEventIndex(const unsigned short vusiLocalEventIndex)
{
    int iEventIndex = L_EVENT;
    iEventIndex = iEventIndex << 28;

    int iSubIndex = 1;
    iSubIndex = iSubIndex << 16;
    iEventIndex += iSubIndex;

    iEventIndex += vusiLocalEventIndex;

    return iEventIndex;
}

/*
 * DESCR:   Search an event by its name
 * PARA:    vsEventName: Event name(input)
 * RETURN:  >0: Gloable event index
 *          <0: not found
 * ACCESS:  public
 */
int CSub::SearchPrjEvent(const string & vsEventName)
{
    EVENTS::const_iterator citer;

    citer = m_AllEventsMap.find(vsEventName);

    if (citer != m_AllEventsMap.end())  //the event exists
        return citer->second;
    else  //the event does not exist
        return -1;
}

/*
 * DESCR:   Search an event by its name
 * PARA:    vsEventName: Event name(input)
 * RETURN:  >0: Sub event index
 *          <0: not found
 * ACCESS:  public
 */
int CSub::SearchSubEvent(const string & vsEventName)
{
    LOCALEVENTS::const_iterator citer;

    citer = m_SubEventsMap.find(vsEventName);

    if (citer != m_SubEventsMap.end())  //the event exists
        return citer->second;
    else  //the event does not exist
        return -1;
}

/**
 * DESCR:   Clear error msg and err code in this project
 * PARA:    None
 * RETURN:  None
 * ACCESS:  public
 */
void CSub::ClearErr()
{
    m_iErrCode = 0;
    m_sErrMsg.empty();
    return;
}

/*
 * DESCR:   Add an event to CProject event map
 *          If the event exists already exists in the map, the  it should have
 *          same global index;  Otherwise the event sets are not disjoint
 * PARA:    vsEventName: Event name(input)
 *          viEventIndex: global event index (input)
 *          cEventSub: Event type ('H", 'L', 'R', 'A')
 *                     (output, only for new events)
 *          cControllable: Controllable? ('Y', 'N')(output)(only for new events)
 * RETURN:  0: success
 *          <0 the event sets are not disjoint.
 * ACCESS:  public
 */
int CSub::AddPrjEvent(const string & vsEventName, const int viEventIndex)
{
    EVENTS::const_iterator citer;

    citer = m_AllEventsMap.find(vsEventName);

    if (citer != m_AllEventsMap.end())  //the event exists, check if the global
                                        //event index is same.
    {
        if (citer->second != viEventIndex)
        {
            return -1;
        }
    }
    else  //the event does not exist
    {
        m_AllEventsMap[vsEventName] = viEventIndex;
        m_InvAllEventsMap[viEventIndex] = vsEventName;
    }

    return 0;
}

} //end of namespace BDDSD
