/*
 * Copyright 2017, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef VISITOR_H
#define VISITOR_H

#include <functional>

namespace android {
namespace spirit {

class Entity;
class Module;
class EntryPointDefinition;
class DebugInfoSection;
class AnnotationSection;
class GlobalSection;
class FunctionDeclaration;
class Block;
class FunctionDefinition;
class Instruction;
#define HANDLE_INSTRUCTION(OPCODE, INST_CLASS) class INST_CLASS;
#include "instruction_dispatches_generated.h"
#undef HANDLE_INSTRUCTION

class IVisitor {
public:
  virtual ~IVisitor() {}

  virtual void visit(Entity *) = 0;
  virtual void visit(Module *) = 0;
  virtual void visit(EntryPointDefinition *) = 0;
  virtual void visit(DebugInfoSection *) = 0;
  virtual void visit(AnnotationSection *) = 0;
  virtual void visit(GlobalSection *) = 0;
  virtual void visit(FunctionDeclaration *) = 0;
  virtual void visit(Block *) = 0;
  virtual void visit(FunctionDefinition *) = 0;
  virtual void visit(Instruction *) = 0;
#define HANDLE_INSTRUCTION(OPCODE, INST_CLASS)                                 \
  virtual void visit(INST_CLASS *) = 0;
#include "instruction_dispatches_generated.h"
#undef HANDLE_INSTRUCTION
};

class DoNothingVisitor : public IVisitor {
public:
  virtual ~DoNothingVisitor() {}

  virtual void visit(Entity *e);
  virtual void visit(Module *m);
  virtual void visit(EntryPointDefinition *);
  virtual void visit(DebugInfoSection *dinfo);
  virtual void visit(AnnotationSection *a);
  virtual void visit(GlobalSection *g);
  virtual void visit(FunctionDeclaration *fdecl);
  virtual void visit(Block *b);
  virtual void visit(FunctionDefinition *fdef);
  // This visit(Instruction *) necessary?
  virtual void visit(Instruction *inst);
#define HANDLE_INSTRUCTION(OPCODE, INST_CLASS) virtual void visit(INST_CLASS *);
#include "instruction_dispatches_generated.h"
#undef HANDLE_INSTRUCTION
};

template <typename T> class InstructionVisitor : public DoNothingVisitor {
public:
  InstructionVisitor(T action) : mAction(action) {}

  virtual ~InstructionVisitor() {}

#define HANDLE_INSTRUCTION(OPCODE, INST_CLASS)                                 \
  void visit(INST_CLASS *inst) override { mAction((Instruction *)inst); }
#include "instruction_dispatches_generated.h"
#undef HANDLE_INSTRUCTION

private:
  T mAction;
};

template <typename T> static IVisitor *CreateInstructionVisitor(T action) {
  return new InstructionVisitor<decltype(action)>(action);
}

} // namespace spirit
} // namespace android

#endif // VISITOR_H