/*******************************************************************\

Module: Symbolic Execution

Author: Daniel Kroening, kroening@cs.cmu.edu

\*******************************************************************/

#ifndef CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H
#define CPROVER_GOTO_SYMEX_GOTO_SYMEX_STATE_H

#include <guard.h>

#include <pointer-analysis/value_set.h>

#include "symex_target.h"

// central data structure: state
class goto_symex_statet
{
public:
  goto_symex_statet():use_value_set(true)
  {
  }

  guardt guard;
  symex_targett::sourcet source;
  
  // we have a two-level renaming

  typedef std::map<irep_idt, irep_idt> original_identifierst;
  
  struct renaming_levelt
  {
  public:
    original_identifierst original_identifiers;

    virtual const irep_idt &get_original_name(const irep_idt &identifier) const=0;
    virtual void get_original_name(exprt &expr) const=0;
    virtual void rename(exprt &expr)=0;
    virtual void rename(typet &type);
    virtual std::string current_name(const irep_idt &identifier) const=0;
    
    virtual ~renaming_levelt() { }
    
    virtual void print(std::ostream &out) const { }
  };
  
  // level 1 -- function frames
  
  struct level1t:public renaming_levelt
  {
  public:
    std::string name(
      const irep_idt &identifier,
      unsigned frame) const;
      
    typedef std::map<irep_idt, unsigned> current_namest;
    current_namest current_names;

    virtual const irep_idt &get_original_name(const irep_idt &identifier) const;
    virtual void get_original_name(exprt &expr) const;  
    virtual void rename(exprt &expr);
    virtual void rename(typet &type) { renaming_levelt::rename(type); }
    virtual std::string current_name(const irep_idt &identifier) const;
    
    void rename(const irep_idt &identifier, unsigned frame)
    {
      current_names[identifier]=frame;
      original_identifiers[name(identifier, frame)]=identifier;
    }

    level1t();
    virtual ~level1t();
    
    virtual void print(std::ostream &out) const
    {
      for(current_namest::const_iterator
          it=current_names.begin();
          it!=current_names.end();
          it++)
        out << it->first << " --> "
            << name(it->first, it->second) << std::endl;
    }
  } level1;
  
  // level 2 -- SSA

  struct level2t:public renaming_levelt
  {
  public:
    virtual const irep_idt &get_original_name(const irep_idt &identifier) const;
    virtual void get_original_name(exprt &expr) const;  
    virtual void rename(exprt &expr);
    virtual void rename(typet &type) { renaming_levelt::rename(type); }
    virtual std::string current_name(const irep_idt &identifier) const;

    std::string name(
      const irep_idt &identifier,
      unsigned count) const;

    struct valuet
    {
      unsigned count;
      exprt constant;
      
      valuet():
        count(0),
        constant((const exprt &)nil_rep)
      {
      }
    };
    
    typedef std::map<irep_idt, valuet> current_namest;
    current_namest current_names;
    
    void rename(const irep_idt &identifier, unsigned count)
    {
      valuet &entry=current_names[identifier];
      entry.count=count;
      original_identifiers[name(identifier, entry.count)]=identifier;
    }

    void get_variables(std::set<irep_idt> &vars) const
    {
      for(current_namest::const_iterator it=current_names.begin();
          it!=current_names.end();
          it++)
        vars.insert(it->first);
    }

    unsigned current_number(const irep_idt &identifier) const;

    level2t();
    virtual ~level2t();

    virtual void print(std::ostream &out) const
    {
      for(current_namest::const_iterator
          it=current_names.begin();
          it!=current_names.end();
          it++)
        out << it->first << " --> "
            << name(it->first, it->second.count) << std::endl;
    }
  } level2;
  
  // uses level 1 names  
  value_sett value_set;

  void rename(exprt &expr);
  void rename_address(exprt &expr);
  void rename(typet &type);
  
  void assignment(
    exprt &lhs,
    const exprt &rhs,
    const namespacet &ns,
    bool init,
    bool record_value);
  
  // undoes both levels of renaming
  const irep_idt &get_original_name(const irep_idt &identifier) const;
  void get_original_name(exprt &expr) const;
  
  // does both levels of renaming
  std::string current_name(const irep_idt &identifier) const;
  
  bool use_value_set;
};

#endif
