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

Module: Program Transformation

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

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

#ifndef CPROVER_GOTO_PROGRAMS_GOTO_CONVERT_CLASS_H
#define CPROVER_GOTO_PROGRAMS_GOTO_CONVERT_CLASS_H

#include <list>

#include <namespace.h>
#include <replace_expr.h>
#include <guard.h>
#include <std_code.h>
#include <options.h>
#include <error_handler.h>

#include "goto_program.h"

class goto_convertt:public error_handlert
{
public:
  void goto_convert(codet &code, goto_programt &dest);

  goto_convertt(
    contextt &_context,
    const optionst &_options,
    message_handlert &_message_handler):
    error_handlert(_message_handler),
    context(_context),
    options(_options),
    ns(_context),
    temporary_counter(0),
    tmp_symbol_prefix("goto_convertt::")
  {
  }
  
  virtual ~goto_convertt()
  {
  }
  
protected:
  contextt &context;
  const optionst &options;
  namespacet ns;
  unsigned temporary_counter;
  std::string tmp_symbol_prefix;

  void goto_convert_rec(codet &code, goto_programt &dest);

  //
  // tools for symbols
  // 
  void new_name(symbolt &symbol);
  const symbolt &lookup(const irep_idt &identifier) const;
  
  symbolt &new_tmp_symbol(const typet &type);
  
  typedef std::list<irep_idt> tmp_symbolst;
  tmp_symbolst tmp_symbols;

  //
  // side effect removal
  //
  
  void read(exprt &expr, goto_programt &dest);
  
  void remove_sideeffects(exprt &expr, guardt &guard,
                          goto_programt &dest,
                          bool result_is_used=true);

  void remove_sideeffects(exprt &expr, goto_programt &dest,
                          bool result_is_used=true);

  bool has_sideeffect(const exprt &expr);
  bool has_functioncall(const exprt &expr);
  
  void remove_assignment(exprt &expr, guardt &guard, goto_programt &dest);
  void remove_pre(exprt &expr, guardt &guard, goto_programt &dest);
  void remove_post(exprt &expr, guardt &guard, goto_programt &dest, bool result_is_used);
  void remove_functioncall(exprt &expr, guardt &guard, goto_programt &dest, bool result_is_used);
  void remove_cpp_new(exprt &expr, guardt &guard, goto_programt &dest, bool result_is_used);
  void remove_temporary_object(exprt &expr, guardt &guard, goto_programt &dest, bool result_is_used);
  void remove_statement_expression(exprt &expr, guardt &guard, goto_programt &dest, bool result_is_used);

  static void replace_new_object(
    const exprt &object,
    exprt &dest);                            

  void cpp_new_initializer(
    const exprt &lhs, exprt &rhs, goto_programt &dest);

  //
  // function calls  
  //
  virtual void do_cpp_new(exprt &lhs, exprt &rhs, goto_programt &dest);
  virtual void do_functioncall(exprt &lhs, exprt &rhs, goto_programt &dest);
  virtual void do_functioncall_symbol(exprt &lhs, exprt &rhs, goto_programt &dest);

  virtual void do_functioncall_symbol(const symbolt &symbol)
  {
  }

  virtual void do_functioncall_dereference(exprt &lhs, exprt &rhs, goto_programt &dest);
  
  //
  // conversion
  //
  void convert_block(codet &code, goto_programt &dest);
  void convert_decl(codet &code, goto_programt &dest);
  void convert_expression(codet &code, goto_programt &dest);
  void convert_sideeffect(exprt &expr, goto_programt &dest);
  void convert_assign(codet &code, goto_programt &dest);
  void convert_cpp_delete(codet &code, goto_programt &dest);
  void convert_for(codet &code, goto_programt &dest);
  void convert_while(codet &code, goto_programt &dest);
  void convert_dowhile(codet &code, goto_programt &dest);
  void convert_assume(codet &code, goto_programt &dest);
  void convert_assert(codet &code, goto_programt &dest);
  void convert_switch(codet &code, goto_programt &dest);
  void convert_break(codet &code, goto_programt &dest);
  void convert_return(codet &code, goto_programt &dest);
  void convert_continue(codet &code, goto_programt &dest);
  void convert_ifthenelse(codet &code, goto_programt &dest);
  void convert_goto(codet &code, goto_programt &dest);
  void convert_skip(codet &code, goto_programt &dest);
  void convert_non_deterministic_goto(codet &code, goto_programt &dest);
  void convert_label(codet &code, goto_programt &dest);
  void convert_functioncall(codet &code, goto_programt &dest);
  void convert_specc_notify(codet &code, goto_programt &dest);
  void convert_specc_wait(codet &code, goto_programt &dest);
  void convert_specc_par(codet &code, goto_programt &dest);
  void convert_specc_event(const exprt &op,
                           std::set<irep_idt> &events);
  void convert_start_thread(codet &code, goto_programt &dest);
  void convert_end_thread(codet &code, goto_programt &dest);
  void convert_sync(codet &code, goto_programt &dest);
  void convert_atomic_begin(codet &code, goto_programt &dest);
  void convert_atomic_end(codet &code, goto_programt &dest);
  void convert_bp_enforce(codet &code, goto_programt &dest);
  void convert_bp_abortif(codet &code, goto_programt &dest);
  void convert(codet &code, goto_programt &dest);
  void move(codet &code, goto_programt &dest);

  //
  // gotos
  //

  void finish_gotos();

  typedef std::map<irep_idt, goto_programt::targett> labelst;
  typedef std::set<goto_programt::targett> gotost;
  typedef exprt::operandst caset;
  typedef std::map<goto_programt::targett, caset> casest;
  
  struct break_continue_targetst
  {
    break_continue_targetst():break_set(false), continue_set(false)
    {
    }
  
    goto_programt::targett break_target;
    bool break_set;

    goto_programt::targett continue_target;
    bool continue_set;
    
    void restore(const break_continue_targetst &targets)
    {
      *this=targets;
    }
  
    void set_break(goto_programt::targett _break_target)
    {
      break_set=true;
      break_target=_break_target;
    }

    void set_continue(goto_programt::targett _continue_target)
    {
      continue_set=true;
      continue_target=_continue_target;
    }
  };
  
  struct break_continue_switch_targetst:public break_continue_targetst
  {
    break_continue_switch_targetst():
      default_set(false)
    {
    }
    
    using break_continue_targetst::restore;
    
    void restore(const break_continue_switch_targetst &targets)
    {
      *this=targets;
    }
  
    void set_default(goto_programt::targett _default_target)
    {
      default_set=true;
      default_target=_default_target;
    }

    goto_programt::targett default_target;
    bool default_set;
    casest cases;
  };

  struct targetst:public break_continue_switch_targetst
  {
    bool return_set;
    bool return_value;

    labelst labels;
    gotost gotos;

    targetst():
      return_set(false)
    {
    }
    
    void swap(targetst &targets)
    {
      std::swap(targets.break_target, break_target);
      std::swap(targets.break_set, break_set);

      std::swap(targets.continue_target, continue_target);
      std::swap(targets.continue_set, continue_set);

      std::swap(targets.return_value, return_value);
      std::swap(targets.return_set, return_set);   
      
      std::swap(targets.default_target, default_target);
      std::swap(targets.default_set, default_set);
      
      targets.labels.swap(labels);
      targets.gotos.swap(gotos);
      targets.cases.swap(cases);
    }
  } targets;
  
  void case_guard(
    const exprt &value,
    exprt::operandst &case_op,
    exprt &dest);
  
  //
  // misc
  //
  void set_location(
    const exprt &code,
    goto_programt::instructiont &dest);
    
  const std::string &get_string_constant(const exprt &expr);

  // built-in functions    
  void do_create_thread(exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_atomic_begin (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_atomic_end   (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_event        (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_malloc       (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_free         (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_sync         (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_exit         (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_array_set    (exprt &lhs, exprt &rhs, goto_programt &dest);
  void do_printf       (exprt &lhs, exprt &rhs, goto_programt &dest);
};

#endif
