#include <queue>
#include <iostream>

#include "absl/container/flat_hash_map.h"

#include "group.h"
#include "common.h"
#include "core.h"

namespace klotski {

using Common::range_reverse;

Group::block_num_t Group::block_num(const RawCode &raw_code) {
    block_num_t result;
    auto tmp = raw_code.unwrap();
    for (int addr = 0; addr < 20; ++addr, tmp >>= 3) {
        switch (tmp & 0b111) {
            case B_1x1:
                ++result.n_1x1;
                continue;
            case B_1x2:
                ++result.n_1x2;
                continue;
            case B_2x1:
                ++result.n_2x1;
                continue;
        }
    }
    return result;
}

Group::block_num_t Group::block_num(const CommonCode &common_code) {
    block_num_t result;
    auto range = range_reverse((uint32_t)common_code.unwrap());
    for (; range; range >>= 2) {
        switch (range & 0b11) {
            case 0b01: /// 1x2 block
                ++result.n_1x2;
                continue;
            case 0b10: /// 2x1 block
                ++result.n_2x1;
                continue;
            case 0b11: /// 1x1 block
                ++result.n_1x1;
                continue;
        }
    }
    return result;
}

uint32_t Group::demo(const RawCode &seed) {

//    struct group_cal_t {
//        uint64_t code;
//        uint64_t mask;
//    };
//    std::queue<group_cal_t*> cache;
//    std::queue<uint64_t> cache;
//    std::queue<std::pair<uint64_t, uint64_t>> cache;
//
//    absl::flat_hash_map<uint64_t, group_cal_t> cases;
//    absl::flat_hash_map<uint64_t, uint64_t> cases;
//    absl::flat_hash_map<uint64_t, std::unique_ptr<group_cal_t>> cases;

    std::queue<uint64_t> cache;
    std::queue<uint64_t> cache_;
    absl::flat_hash_map<uint64_t, uint64_t> cases;

    cases.reserve(65535 * 8);

//    cache.emplace(&cases.emplace(seed.unwrap(), group_cal_t {
//        .code = seed.unwrap(),
//        .mask = 0,
//    }).first->second);
//    cases.emplace(seed.unwrap(), 0);
//    cache.emplace(seed.unwrap());
//    cache.emplace(seed.unwrap(), 0);
//    cases.emplace(seed.unwrap(), std::make_unique<group_cal_t>(group_cal_t {
//        .code = seed.unwrap(),
//        .mask = 0,
//    }));

    cases.emplace(seed.unwrap(), 0);
    cache.emplace(seed.unwrap());
    cache_.emplace(0);

    auto core = Core(
        [&cases, &cache, &cache_](auto &&code, auto &&mask) {
            auto current = cases.find(code);

            if (current != cases.end()) { // find existed case

//                current->second.mask |= mask; // update mask info
//                current->second |= mask;
//                current->second->mask |= mask;

                current->second |= mask; // update mask info
                return;
            }

//            cache.emplace(&cases.emplace(code, group_cal_t {
//                .code = code,
//                .mask = mask,
//            }).first->second);
//            cases.emplace(code, mask);
//                cache.emplace(code);
//                cache.emplace(code, mask);
//            cases.emplace(code, std::make_unique<group_cal_t>(group_cal_t {
//                .code = code,
//                .mask = mask,
//            }));

            cases.emplace(code, mask);
            cache.emplace(code);
            cache_.emplace(mask);

        }
    );

    while (!cache.empty()) {
//        core.next_cases(cache.front()->code, cache.front()->mask);
//        core.next_cases(cache.front(), cases.find(cache.front())->second);
//        core.next_cases(cache.front().first, cache.front().second);
//        core.next_cases(cache.front(), cases.find(cache.front())->second->mask);
//        core.next_cases(cache.front(), cases.find(cache.front())->second);
        core.next_cases(cache.front(), cache_.front());

        cache.pop();
        cache_.pop();
    }

//    std::cout << cases.size() << std::endl;

    return cases.size();
}


} // namespace klotski