#ifndef __OUTPUT__PARAMETER__ANALYSIS__HPP
#define __OUTPUT__PARAMETER__ANALYSIS__HPP

#include "DataFlow.hpp"
#include "CommonTypes.hpp"

#include "llvm/Pass.h"
#include "llvm/CallGraphSCCPass.h"
#include "llvm/Support/InstVisitor.h"

#include <boost/scoped_ptr.hpp>

#include <tr1/unordered_map>

class AliasHelper;

namespace llvm
{
  class Function;
  class Argument;
}

enum ArgType_tag
{
  Opaque, // Equivalent to unused or non-pointer.
  In,
  Out,
  InOut
};

struct ArgType
{
  ArgType_tag _type;
  ArgType():_type(Opaque){}
  ArgType(ArgType_tag at):_type(at){}
  bool isOpaque() const { return _type == Opaque; }
  bool isIn() const { return _type == In; } 
  bool isOut() const { return _type == Out; } 
  bool isInOut() const { return _type == InOut; }
  
  bool operator < (const ArgType & rhs) const
  {
    return this->_type < rhs._type;
  }
  
  bool operator == (const ArgType & rhs) const
  {
    return this->_type == rhs._type;
  }
};

std::ostream & operator << (std::ostream & os, const ArgType & at);

typedef std::vector<ArgType> var_states;

struct OutputParameterAnalysis : public llvm::InstVisitor<OutputParameterAnalysis>,
                                 public DataFlow<var_states>,
                                 public llvm::CallGraphSCCPass
{
  static char ID;
  results_t * _res;
  OutputParameterAnalysis(results_t * res = NULL);

  // InstVisitor
  void visitCallInst(llvm::CallInst & CI);
  void visitBitCastInst(llvm::BitCastInst & BCI);

  // DataFlow
  var_states transfer(var_states & inputStates,
                      const llvm::BasicBlock * bb);
  var_states* join(var_states * accum, var_states * vs);

  // CallGraphSCCPass
  virtual bool doInitialization(llvm::CallGraph & CG);
  virtual bool runOnSCC(const std::vector<llvm::CallGraphNode *> &SCCM);
  virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const;

  
private:
  void checkInOut(llvm::Function & F);
  var_states inOutAnalyzer(llvm::Function & F);
  
  void registerValue(var_states & vs,
                     const llvm::Value * target,
                     ArgType_tag t);
  const llvm::Value * resolveCasts(const llvm::Value * target);
  void markInOut(const llvm::Function & F,
                 const var_states & s,
                 const llvm::Argument & arg);
  void printState(const var_states & s);
  size_t valueIndex(const llvm::Value * v);
  void resetStateVariables();
  
private:
  boost::scoped_ptr<AliasHelper> _AH;
  
  // Things to reset
  value_set calls;
  value_map casts;
  std::tr1::unordered_map<const llvm::Value*, unsigned> _mapping;
  std::set<size_t> _interestingIndices;
  size_t _indexSource;
  size_t _totalValues;
};

#endif // __OUTPUT__PARAMETER__ANALYSIS__H
