// @Time    :   2023-10-24 11:17:40
// @FileName:   controller.h
// @Author  :   423A35C7
// @Software:   VSCode

#ifndef _CONTROLLER
#define _CONTROLLER

#include "constants.h"
// #include "model.hpp" // view里已经依赖model了
#include "view.hpp"

class Controller {
    // virtual Status main() = 0;
};

template <typename T>
class DriftingController;

template <typename T>
class SingleQueueController : Controller {
friend class DriftingController<T>;
private:
    int_ consumed_time = 0;
    int speed = DEFAULT_SPEED;

    SingleQueueModel<T> &model;
    // BackgroundView background_view;
    QueueView<T, SingleQueueModel<T>> &view;
    // void (View:: a)() = background_view.init;
    // 捕获了this的lambda函数好像不能转化为void函数指针
    // initializer_list<void ()> temp {
    //     [&](this){background_view.init();},
    //     [&](this){view.refresh(model);}
    // };
public:
    // refresh可以认为是controller内部自己进行更新
    void refresh() {
        if (this->model.get_length() <= 0) {
            this->consumed_time = 0;
        } else {
            this->consumed_time++;
            if (this->consumed_time * this->speed >= this->model.top().cash) {
                this->model.shift();
                this->consumed_time = 0;
            }
        }
    }
    SingleQueueController(SingleQueueModel<T> &model, QueueView<T, SingleQueueModel<T>> &view, int speed = DEFAULT_SPEED) : model(model), view(view), speed(speed) {
        this->refresh();
    }
};

template <typename T>
class DriftingController : Controller {
private:
    std::vector<std::shared_ptr<SingleQueueController<T>>> &single_queue_controllers;
    DriftingView<T> &drifting_view;
    double walk_speed = DEFAULT_WALK_SPEED;
public:
    void push(T data, int target, coordinate current_pos = std::make_pair(DEFAULT_GATE_X, DEFAULT_GATE_Y), double walk_speed = DEFAULT_WALK_SPEED) {
        auto &view = this->single_queue_controllers[target]->view;
        coordinate target_pos = view.get_last_pos();
        int_ remained_time = (int_)(sqrt(pow(target_pos.first - current_pos.first, 2) + pow(target_pos.second - current_pos.second, 2)) / walk_speed);
        // DriftingView<Customer>::Node temp ;
        // 如果形参是引用的话会出现如下问题:
        // error: cannot bind non-const lvalue reference of type 'DriftingView<Customer>::Node&' to an rvalue of type 'DriftingView<Customer>::Node'
        this->drifting_view.push({
            data,
            view,
            remained_time,
            current_pos
        });
    }
    DriftingController(
        std::vector<std::shared_ptr<SingleQueueController<T>>> &single_queue_controllers,
        DriftingView<T> &drifting_view
    ) : 
        single_queue_controllers(single_queue_controllers),
        drifting_view(drifting_view) {}
};

Status mainloop(std::function<Status()> main, std::function<void()> refresh, int_ total_time = DEFAULT_TOTAL_TIME) {
    Status temp;
    for (int i = 0; i < total_time; i++) {
        if ((temp = main()) != OK)
            return temp;
        refresh();
    }
    return OK;
}

#endif