#pragma once
#ifdef WIN32
#include <Windows.h>
#endif
#include <memory>
#include <stdexcept>
#include <vector>
#include <string>
#include <sstream>
#include <GL/glew.h>
#include "common/logger.h"
#include "common/fmt.h"

#ifdef WIN32
typedef unsigned int uint;
#endif

#ifndef COUNTOF
#define COUNTOF(__x__) (sizeof(__x__) / sizeof((__x__)[0]))
#endif
#ifndef CEILDIV
#define CEILDIV(__x__, __y__) (uint) ceil((__x__) / (float)(__y__))
#endif
#define INTERVAL(__start__, __end__) (((__end__) - (__start__)) / (float)CLOCKS_PER_SEC * 1000)

inline uint getElementSize(nv::DataType t) {
    switch (t) {
    case nv::DataType::kINT32:
    case nv::DataType::kFLOAT:
        return 4;
    case nv::DataType::kHALF:
        return 2;
    case nv::DataType::kBOOL:
    case nv::DataType::kINT8:
        return 1;
    default:
        throw std::runtime_error("Invalid DataType.");
    }
}

template <typename T> void dumpRow(std::ostream &os, T *buf, size_t n) {
    os << buf[0];
    for (size_t i = 1; i < n; ++i)
        os << " " << buf[i];
    os << std::endl;
}

template <typename T>
void dumpHostBuffer(std::ostream &os, T *buf, size_t bufSize, size_t rowCount,
                    size_t maxDumpRows = 0) {
    size_t numItems = bufSize / sizeof(T);
    size_t nInLastRow = numItems % rowCount;
    size_t rows;
    if (nInLastRow == 0) {
        rows = numItems / rowCount;
        nInLastRow = rowCount;
    } else {
        rows = numItems / rowCount + 1;
    }
    if (maxDumpRows == 0) {
        for (size_t i = 0; i < rows - 1; ++i) {
            dumpRow(os, buf, rowCount);
            buf += rowCount;
        }
        dumpRow(os, buf, nInLastRow);
    } else {
        for (size_t i = 0; i < maxDumpRows / 2; ++i)
            dumpRow(os, buf + i * rowCount, rowCount);
        os << "..." << std::endl;
        for (size_t i = rows - maxDumpRows + maxDumpRows / 2; i < rows - 1; ++i)
            dumpRow(os, buf + i * rowCount, rowCount);
        dumpRow(os, buf + (rows - 1) * rowCount, nInLastRow);
    }
}

template <typename T> struct Destroy {
    void operator()(T *t) {
        if (t != nullptr)
            t->destroy();
    }
};

#include "Formatter.h"

template <class T> using uptr = std::unique_ptr<T, ::Destroy<T>>;
template <class T> using sptr = std::shared_ptr<T>;

enum Eye { Eye_Left, Eye_Right };