#include <array>
#include <cstring>

#include "dxvk_spirv_code_buffer.h"

namespace dxvk {
  
  DxvkSpirvCodeBuffer:: DxvkSpirvCodeBuffer() { }
  DxvkSpirvCodeBuffer::~DxvkSpirvCodeBuffer() { }
  
  
  DxvkSpirvCodeBuffer::DxvkSpirvCodeBuffer(
    std::basic_istream<uint32_t>& stream)
  : m_code(
    std::istreambuf_iterator<uint32_t>(stream),
    std::istreambuf_iterator<uint32_t>()) { }
  
  
  void DxvkSpirvCodeBuffer::append(const DxvkSpirvCodeBuffer& other) {
    const size_t size = m_code.size();
    m_code.resize(size + other.m_code.size());
    
          uint32_t* dst = this->m_code.data();
    const uint32_t* src = other.m_code.data();
    
    std::memcpy(dst + size, src, sizeof(uint32_t) * size);
  }
  
  
  void DxvkSpirvCodeBuffer::putWord(uint32_t word) {
    m_code.push_back(word);
  }
  
  
  void DxvkSpirvCodeBuffer::putIns(spv::Op opCode, uint16_t wordCount) {
    this->putWord(
        (static_cast<uint32_t>(opCode)    <<  0)
      | (static_cast<uint32_t>(wordCount) << 16));
  }
  
  
  void DxvkSpirvCodeBuffer::putInt32(uint32_t word) {
    this->putWord(word);
  }
  
  
  void DxvkSpirvCodeBuffer::putInt64(uint64_t value) {
    this->putWord(value >>  0);
    this->putWord(value >> 32);
  }
  
  
  void DxvkSpirvCodeBuffer::putFloat32(float value) {
    uint32_t tmp;
    static_assert(sizeof(tmp) == sizeof(value));
    std::memcpy(&tmp, &value, sizeof(value));
    this->putInt32(tmp);
  }
  
  
  void DxvkSpirvCodeBuffer::putFloat64(double value) {
    uint64_t tmp;
    static_assert(sizeof(tmp) == sizeof(value));
    std::memcpy(&tmp, &value, sizeof(value));
    this->putInt64(tmp);
  }
  
  
  void DxvkSpirvCodeBuffer::putStr(const char* str) {
    uint32_t word = 0;
    uint32_t nbit = 0;
    
    for (uint32_t i = 0; str[i] != '\0'; str++) {
      word |= (static_cast<uint32_t>(str[i]) & 0xFF) << nbit;
      
      if ((nbit += 8) == 32) {
        this->putWord(word);
        word = 0;
        nbit = 0;
      }
    }
    
    // Commit current word
    this->putWord(word);
  }
  
  
  void DxvkSpirvCodeBuffer::putHeader(uint32_t boundIds) {
    this->putWord(spv::MagicNumber);
    this->putWord(spv::Version);
    this->putWord(0); // Generator
    this->putWord(boundIds);
    this->putWord(0); // Schema
  }
  
  
  uint32_t DxvkSpirvCodeBuffer::strLen(const char* str) {
    // Null-termination plus padding
    return (std::strlen(str) + 4) / 4;
  }
  
  
  void DxvkSpirvCodeBuffer::store(std::basic_ostream<uint32_t>& stream) const {
    stream.write(m_code.data(), m_code.size());
  }
  
}