#ifndef SIMALG_H
#define SIMALG_H

#include "SimCompState.h"
#include "SimConfig.h"
#include "ProjectEvent.h"
#include "DesHierProject.h"
#include "DesFlatProject.h"

class SimWorkspace;

namespace DESpot
{

class SimAlg
{

public:

    enum RunStatus{Stop, Reach_condition, User_request_stop, user_exit, Running, Runable, no_elig_seqev, end_of_seq, Uninit};
    typedef std::set<DesEvent*> EventSet;

    /* default contsturctor. Read events file and init state*/
    SimAlg( DesProject* desProject,										//Project
            SimWorkspace* configWorkspace,
            SimCompState::StateTuple initStateTuple,					//Init state
            SimConfig::ProjectEventSet eventSet,							//Event set
            SimConfig::SimMode simMode,
            SimConfig::EventSetMode eventSetMode,
            SimStopCondition stopCondition);							//Stop condition

    ProjectEvent* chooseEvent();
    void updateCurrentState(ProjectEvent* ev);

    void setEventSetMode(SimConfig::EventSetMode mode)
        {eventSetMode = mode;}

    SimConfig::SimMode getSimMode() const { return simMode; }
    //
    std::set<ProjectEvent*> getEligibleEventSet();
    SimCompState::StateTuple* getCurrentState();
    SimCompState* getCurState();
    //To support next state tuple viewer
    SimCompState::StateTuple getNextStateTuple(ProjectEvent* ev);
    // To support graphical simulator previous state marker
    SimCompState* getPrevState();


    // runtime operations
    RunStatus getStatus()
        {return status;}
    ProjectEvent* getPrevEvent();
    ProjectEvent* getNextEvent();
    ProjectEvent* getNextEventInSeq();
    ProjectEvent* getPrevEvent(int row);
    ProjectEvent* getNextEvent(int row);

    void setStatus(RunStatus st)
        {status = st;}
    void enableTrace(bool enable)
        {traceEnabled = enable;}
    void enableHist(bool enable)
        {histEnabled = enable;}
    bool isHistEnabled()
        {return histEnabled;}

    bool isTraceEnabled()
        {return traceEnabled;}
    void setTrcStepsForLongStateName(int step)
        {trcStepsForLongStateName=step;}
    void setStepsToFlush(int step)
        {stepsToFlush=step;}
    void runFlat();
    void runFastHISC();
    void runFastSeqHISC();
    void checkSeqRange(); //this is to fix a seq bug in fix11

    void stop() //implemented in timeToStop
        {if (status!=Stop) status = User_request_stop;}
    void setStepSize(unsigned int sz)
        {stepSize = sz;}
    bool timeToStop();
    unsigned long getCurrentStep()
        {return currentStep;}
    void stepForwardFlat(ProjectEvent *choosenEvent);
    void stepForwardHISC(ProjectEvent *choosenEvent);

    void stepBackward();
    void stepBackward(int stepsToBack);
    bool isCurrentStateInit()
        {return currentState->isInit();}
    bool isCurrentStateMarked()
        {return currentState->isMarked();}
    void setStopCondition(SimStopCondition	newStopCondition)
        {stopCondition=newStopCondition;}
    void addElapsed(long n)
    {elapsedMilliseconds += n;}


    //If reurn false, we have to stop
    bool oneStepForwardFlat(bool computeNextState);
    bool oneStepForwardFlat(ProjectEvent *choosenEvent);
    bool oneStepForwardHISC(ProjectEvent *choosenEvent);
    bool oneStepForwardHISC(bool compAllEligEvent);
    bool oneStepForwardSeqHISC();

    bool oneStepBackward();

    //History
    SimConfig::ProjectEventSet getHistEventSeq();
    int getElapsed()
        {return elapsedMilliseconds;}
    SimCompState* getHistState(int index)
        {return stateHist[index];}
    int getHistSize()
        {return stateHist.size();}

    // Functions to support SimCompState
    //std::set<Des*> getDesSet(){return desSet;}
    std::set<Des*> getDefinedDesSet(const ProjectEvent* ev);
    std::set<Des*> getUndefinedDesSet(const ProjectEvent* ev);
    std::set<Des*> getDefinedDesSet(const ProjectEvent* ev, const DesSubsystem *subsys);
    std::set<Des*> getUndefinedDesSet(const ProjectEvent* ev, const DesSubsystem *subsys);

    std::set<DesEvent*> getDesEventSet(const ProjectEvent* ev);
    bool isTransDefined(Des* des, DesState* state, const DesEvent* ev);
    DesState* getNextState(Des* des, DesState* state, const DesEvent* ev);
    //std::set<DesEvent*> getEventSet(){return eventSet;}
    std::set<const ProjectEvent*> getSeqSimEvents();
    ProjectEvent* getSeqSimEvent();
    std::set<const ProjectEvent*>& getSimEvents();
    std::set<const ProjectEvent*>& getSimEvents(const DesSubsystem *subsys);
    void computeEligEventsFlat(bool computeNextState);
    void computeEligEventsHISC(bool computeAllEligEvent);
    void computeEligEventsSeqHISC();


    //HISC optimization
    std::set<const DesSubsystem*>& getLevelSet()
        {return levelSet;}
    std::set<const DesSubsystem*>& getLevelSet(const ProjectEvent* ev)
        {return *eventLevelMap[ev];}
    const DesSubsystem* getCtlLevelSet(const ProjectEvent* ev)
        {return eventLevelCtlMap[ev];}


    //Utility functions
    void printSimConfig();
    void pringTraceHeader();
    void dbg(const QString &str);
    void printEligEvents(){currentState->printEligEvents();}

private:
    /* Populate eligible events for this composite state*/
    void getEligEvents();
    void writeTrace(const QString &str);
    void writeStateToTrace(SimCompState* state);
    void writeEvSetToTrace(std::set<const ProjectEvent*> eventSet);
    void writeEvSeqToTrace(SimConfig::ProjectEventSet &evSeq);
    void createTransTab();
    void printTransTab();

    //Runtime status
    RunStatus status;

    //Tracing
    bool traceEnabled;
    bool histEnabled;

    //HISC optimization
    void initHISCOptim();
    void createLevelDesMap();
    void printLevelDesMap();
    void createLevelEventMap();
    void printLevelEventMap();
    void printEventLevelMap();
    void printEventLevelCtlMap();
    void createInterSubsysMap();

    std::map<const DesSubsystem*,std::set<const Des*>*> levelDesMap;
    std::map<const DesSubsystem*,std::set<const ProjectEvent*>*> levelEventMap;
    std::map<const ProjectEvent*,std::set<const DesSubsystem*>*> eventLevelMap; //level the event impacted
    std::map<const ProjectEvent*,const DesSubsystem*> eventLevelCtlMap; //level that control the event
    std::set<const DesSubsystem*> levelSet;
    std::map<const DesInterface*,const DesSubsystem*> interSubsysMap;

    //Configuration of the simulation task
    SimConfig::ProjectEventSet projectEventSet;
    SimConfig::EventSetMode		eventSetMode;
    SimConfig::SimMode			simMode;
    SimStopCondition			stopCondition;
    SimCompState*				currentState;
    unsigned long				currentStep;

    // set to 0 when 1) stop condition changed 2) start run 3) init simAlg
    // to achieve 1) run, stepForward, current state does not count
    //            2) all subseuqenent step report
    unsigned long               sessionStep;   //number of steps for this session - to check init/marked
    unsigned int				stepSize;      //number of steps to forward or backward

    //As event set does not change if we simulate a set
    //add this so we don't have to do it everytime.
    std::set<const ProjectEvent*>		simEventSet;

    // Transition function table
    typedef DesState*					TransTab;
    typedef std::map<DesState*,int>		StateIdxMap;			//index into 2-d array
    typedef std::map<DesEvent*,int>		EventIdxMap;			//
    std::map<Des*, StateIdxMap>			transTabStateIdxMap;	//index into 2-d array
    std::map<Des*, EventIdxMap>			transTabEventIdxMap;	//
    std::map<Des*, TransTab**>			desTransTabMap;
    std::map<const ProjectEvent*, std::set<Des*> > undefDesSet;
    std::map<const ProjectEvent*, std::set<Des*> > defDesSet;


    // The proejct reference
    DesProject*				desProject;
    DesHierProject*         hierProject;
    DesFlatProject*			flatProject;
    ProjectType             projectType;
    SimWorkspace*			workSpace;

    //History of the states we have simulated
    std::vector<SimCompState*> 	stateHist;

    //tracing option
    int trcStepsForLongStateName;
    int	stepsToFlush;

    //Timing
    int elapsedMilliseconds;
};
}
#endif
