#pragma once

#include <iostream>

#include "../ir.h"
#include "../ir_builder.h"

#include "../../util/util_small_vector.h"

namespace dxbc_spv::ir {

/** Pass to convert scoped control flow to structurized constructs.
 *
 * Must be one of the fist passes run, since most other passes will
 * expect fully structured control flow. */
class ConvertControlFlowPass {

public:

  ConvertControlFlowPass(Builder& builder);

  ~ConvertControlFlowPass();

  ConvertControlFlowPass             (const ConvertControlFlowPass&) = delete;
  ConvertControlFlowPass& operator = (const ConvertControlFlowPass&) = delete;

  /** Runs conversion pass.
   *
   * Note that the IR generated by this pass is not valid and may
   * contain unreachable blocks, and must be run through dead code
   * elimination first to resolve them. */
  void run();

  /** Initializes and runs pass on the given builder. */
  static void runPass(Builder& builder);

private:

  struct ConstructInfo {
    SsaDef def = { };

    struct {
      SsaDef    mergeBlock      = { };
      SsaDef    condTrueBlock   = { };
      SsaDef    condFalseBlock  = { };
    } if_;

    struct {
      Op        switchOp        = { };
      SsaDef    mergeBlock      = { };
    } switch_;

    struct {
      SsaDef    headerBlock     = { };
      SsaDef    mergeBlock      = { };
      SsaDef    bodyBlock       = { };
      SsaDef    continueBlock   = { };
    } loop_;
  };

  Builder& m_builder;

  /* Active label, if any. */
  SsaDef m_currFunction = { };
  SsaDef m_currBlock = { };

  /* Nested control flow constructs, with the innermost block last. */
  util::small_vector<ConstructInfo, 64u> m_constructs;

  /* Looks up construct referenced by a scoped instruction. The
   * definition must be an active scoped control flow construct. */
  ConstructInfo& findConstruct(SsaDef def);


  Builder::iterator handleFunction(Builder::iterator op);

  Builder::iterator handleFunctionEnd(Builder::iterator op);

  Builder::iterator handleReturn(Builder::iterator op);

  Builder::iterator handleIf(Builder::iterator op);

  Builder::iterator handleElse(Builder::iterator op);

  Builder::iterator handleEndIf(Builder::iterator op);

  Builder::iterator handleLoop(Builder::iterator op);

  Builder::iterator handleLoopBreak(Builder::iterator op);

  Builder::iterator handleLoopContinue(Builder::iterator op);

  Builder::iterator handleEndLoop(Builder::iterator op);

  Builder::iterator handleSwitch(Builder::iterator op);

  Builder::iterator handleCase(Builder::iterator op);

  Builder::iterator handleDefault(Builder::iterator op);

  Builder::iterator handleSwitchBreak(Builder::iterator op);

  Builder::iterator handleEndSwitch(Builder::iterator op);

  Builder::iterator handleLabel(Builder::iterator op);

};

}
