#pragma once
#include <iostream>

class Array{

public:

    Array(size_t x, size_t y, size_t z);
    ~Array();
    Array& operator=(const Array& other);
    static Array MakeArray(size_t x, size_t y, size_t z);
    int GetIndex(size_t x, size_t y, size_t z) const;
    int GetCurX() const;
    int GetCurY() const;
    int GetCurZ() const;
    int GetX() const;
    int GetY() const;
    int GetZ() const;
    void StoreElement(size_t x, size_t y, size_t z, uint32_t number);
    uint32_t GetElement(size_t x, size_t y, size_t z) const;
    Array& operator[](size_t index);
    Array& operator=(uint32_t x);
    friend std::ostream& operator<<(std::ostream& stream, const Array& val);
    friend std::istream& operator>>(std::istream& stream, Array& val);
    Array operator*(uint32_t num);
    friend Array operator-(const Array& lhs, const Array& rhs);
    friend Array operator+(const Array& lhs, const Array& rhs);

private:

    uint32_t* arr_;
    size_t x_;
    size_t y_;
    size_t z_;
    size_t current_x_;
    size_t current_y_;
    size_t current_z_;

};
#include "/Users/alishashelby/Desktop/labwork5-alishashelby/lib/3D17BitArray.h"

Array::Array(size_t x, size_t y, size_t z) {
    x_ = x;
    y_ = y;
    z_ = z;
    arr_ = new uint32_t[x * y * z];
    for (size_t i = 0; i < x * y * z; ++i) {
        arr_[i] = 0;
    }
}

Array::~Array() {
    delete[] arr_;
}

Array& Array::operator=(const Array& other) {
    delete[] arr_;
    x_ = other.x_;
    y_ = other.y_;
    z_ = other.z_;
    arr_ = new uint32_t[x_ * y_ * z_];
    for (size_t i = 0; i < x_ * y_ * z_; ++i) {
        arr_[i] = other.arr_[i];
    }

    return *this;
}

Array Array::MakeArray(size_t x, size_t y, size_t z) {
    return Array(x, y, z);
}

int Array::GetIndex(size_t x, size_t y, size_t z) const {
    return ((x * y_ + y) * z_ + z) * 17;
}

int Array::GetCurX() const {
    return current_x_;
}

int Array::GetCurY() const {
    return current_y_;
}

int Array::GetCurZ() const {
    return current_z_;
}

int Array::GetX() const {
    return x_;
}

int Array::GetY() const {
    return y_;
}

int Array::GetZ() const {
    return z_;
}

void Array::StoreElement(size_t x, size_t y, size_t z, uint32_t number) {
    size_t bit_start = GetIndex(x, y, z);
    //std::cout << bit_start<< '\n';
    for (int i = 0; i < 17; ++i) {
        if ((number >> i) & 1) {
            arr_[bit_start / 32] |= (1 << bit_start % 32);
        } else {
            arr_[bit_start / 32] &= ~(1 << bit_start % 32);
        }
        ++bit_start;
    }
}

uint32_t Array::GetElement(size_t x, size_t y, size_t z) const {
    size_t bit_start = GetIndex(x, y, z);
    uint32_t value = 0;
    for (int i = 0; i < 17; ++i) {
        if (arr_[bit_start / 32] & (1 << (bit_start % 32))) {
            value |= (1 << i);
        }
        ++bit_start;
    }

    return value;
}

Array& Array::operator[](size_t index) {
    static int operator_count = 0;
    if (operator_count == 0) {
        current_x_ = index;
    }
    if (operator_count == 1) {
        current_y_ = index;
    }
    if (operator_count == 2) {
        current_z_ = index;
        operator_count = -1;
    }
    ++operator_count;

    return *this;
}

Array& Array::operator=(uint32_t x) {
    StoreElement(current_x_, current_y_, current_z_, x);

    return *this;
}

std::ostream& operator<<(std::ostream& stream, const Array& val) {
    uint32_t number = val.GetElement(val.GetCurX(), val.GetCurY(), val.GetCurZ());
    stream << number;
    /*for (int i = 16; i >= 0; --i) {
        stream << ((number >> i) & 1);
    }*/

    return stream;
}

std::istream& operator>>(std::istream& stream, Array& val) {
    size_t x, y, z;
    uint32_t value;
    stream >> x >> y >> z >> value;
    val.StoreElement(x, y, z, value);

    return stream;
}

Array Array::operator*(uint32_t num) {
    Array new_arr(x_, y_, z_);
    for (size_t i = 0; i < x_; ++i) {
        for (size_t j = 0; j < y_; ++j) {
            for (size_t k = 0; k < z_; ++k) {
                uint32_t temp = (*this).GetElement(i, j, k);
                new_arr.StoreElement(i, j, k, temp * num);
            }
        }
    }
    return new_arr;
}

Array operator-(const Array& lhs, const Array& rhs) {
    if (lhs.GetX() != rhs.GetX() || lhs.GetY() != rhs.GetY() || lhs.GetZ() != rhs.GetZ()) {
        std::cerr << "--array sizes do not match";
        std::exit(EXIT_FAILURE);
    }
    size_t x = lhs.GetX();
    size_t y = lhs.GetY();
    size_t z = lhs.GetZ();
    Array new_arr(x, y, z);
    for (size_t i = 0; i < x; ++i) {
        for (size_t j = 0; j < y; ++j) {
            for (size_t k = 0; k < z; ++k) {
                uint32_t temp = lhs.GetElement(i, j, k) - rhs.GetElement(i, j, k);
                new_arr.StoreElement(i, j, k, temp);
            }
        }
    }
    
    return new_arr;
}

Array operator+(const Array& lhs, const Array& rhs) {
    if (lhs.GetX() != rhs.GetX() || lhs.GetY() != rhs.GetY() || lhs.GetZ() != rhs.GetZ()) {
        std::cerr << "--array sizes do not match";
        std::exit(EXIT_FAILURE);
    }
    size_t x = lhs.GetX();
    size_t y = lhs.GetY();
    size_t z = lhs.GetZ();
    Array new_arr(x, y, z);
    for (size_t i = 0; i < x; ++i) {
        for (size_t j = 0; j < y; ++j) {
            for (size_t k = 0; k < z; ++k) {
                uint32_t temp = lhs.GetElement(i, j, k) + rhs.GetElement(i, j, k);
                new_arr.StoreElement(i, j, k, temp);
            }
        }
    }
    
    return new_arr;
}
write in file 3D17BitArray.cpp GoogleTests for my implementation #include "/Users/alishashelby/Desktop/labwork5-alishashelby/lib/3D17BitArray.h"
#include <gtest/gtest.h>
#include <sstream>

TEST(3D17BitArrayTestSuite, EmptyTest) {
    Array arr = Array::MakeArray(10, 2, 30);
    arr[4][1][2] = 100500;
    std::cout << arr[4][1][2];
}include(FetchContent)

FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG release-1.12.1
)

# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(
    3D17BitArray_tests
    3D17BitArray_test.cpp
)

target_link_libraries(
    3D17BitArray_tests
    3D17BitArray
    GTest::gtest_main
)

target_include_directories(3D17BitArray_tests PUBLIC ${PROJECT_SOURCE_DIR})

include(GoogleTest)

gtest_discover_tests(3D17BitArray_tests)