#pragma once

#include "sm3_parser.h"
#include "sm3_types.h"

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

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

namespace dxbc_spv::sm3 {

class Converter;

class RegisterFile {

public:

  explicit RegisterFile(Converter& converter);

  ~RegisterFile();

  void initialize(ir::Builder& builder);

  /** Loads temporary register. */
  ir::SsaDef emitTempLoad(
          ir::Builder&            builder,
          uint32_t                regIndex,
          Swizzle                 swizzle,
          WriteMask               componentMask,
          ir::ScalarType          type);

  ir::SsaDef emitPredicateLoad(
          ir::Builder&            builder,
          Swizzle                 swizzle,
          WriteMask               componentMask);

  ir::SsaDef emitAddressLoad(
          ir::Builder&            builder,
          RegisterType            registerType,
          Swizzle                 swizzle,
          ir::SsaDef              loopCounter);

  /** Stores temporary register or address register. */
  bool emitStore(
          ir::Builder&            builder,
    const Operand&                operand,
          WriteMask               writeMask,
          ir::SsaDef              predicateVec,
          ir::SsaDef              value);

  /** Writes buffered stores to the actual temp registers. */
  void emitBufferedStores(
          ir::Builder&            builder);

  /** Loads value from texture register. */
  ir::SsaDef emitTextureRegLoad(
          ir::Builder&            builder,
          uint32_t                regIndex,
          Swizzle                 swizzle,
          WriteMask               componentMask,
          ir::ScalarType          type);

  /** Retrieves or declares label as function */
  ir::SsaDef getOrDeclareLabel(
          ir::Builder&            builder,
    const Operand&                operand);

private:

  ir::SsaDef getOrDeclareTemp(ir::Builder& builder, uint32_t index, Component component);

  ir::SsaDef getOrDeclareTextureReg(ir::Builder& builder, uint32_t index, Component component);

  Converter& m_converter;

  struct Store {
    Store(ir::SsaDef reg, ir::SsaDef value)
      : reg(reg), value(value) {}

    ir::SsaDef reg;
    ir::SsaDef value;
  };

  /** Temporary registers */
  util::small_vector<ir::SsaDef, 32u * 4u> m_rRegs = { };

  /** Address register */
  std::array<ir::SsaDef, 4u> m_a0Reg = { };

  /** Loop counter register */
  ir::SsaDef m_aLReg = { };

  /** Predicate register */
  std::array<ir::SsaDef, 4u> m_pReg = { };

  /* Buffered stores to deal with coissue */
  util::small_vector<Store, 8u> m_stores = { };

  /* In shader model 1.1 - 1.3, you put either texture data (from sampling) or texcoords into a texture register. */
  std::array<ir::SsaDef, 8u * 4u> m_textureRegs = { };

  /** Label functions */
  util::small_vector<ir::SsaDef, 8u> m_labels = { };

};

}
