#pragma once

#include <memory>
#include <string>
#include <vector>

#include <Core/DifficultLvlStrategy.h>
#include <Core/TimeObserver.h>
#include "Ship/Cargo.h"

class PrintVisitor;
class Time;

class Ship : public TimeObserver {
public:
    enum class StatusCode {
        OK,
        MissingCargo,
        LackOfSpace
    };

    ~Ship();
    Ship(const Ship&) = default;
    Ship(Ship&&) = default;
    Ship& operator=(const Ship&) = default;
    Ship& operator=(Ship&&) = default;

    // Override from TimeObserver
    void nextDay() override;

    std::string& name() { return _name; }
    const std::string& name() const { return _name; }
    int crew() const { return _crew; }

    void printCargo() const;

    Cargo* load(std::unique_ptr<Cargo>&& cargo, StatusCode& code) noexcept;
    void unload(const std::string& name, int amount, StatusCode& code) noexcept;

    // May throw
    Cargo* load(std::unique_ptr<Cargo>&& cargo);
    void unload(const std::string& name, int amount);

    void takeDamage(int damage);
    const std::vector<std::unique_ptr<Cargo>>& cargoes() const;

    void increaseArmor(int armor) { _armor += armor; }
    int durability() const { return _durability; }
    int armor() const { return _armor; }

private:
    // Allow to be created only by std::unique_ptr
    friend std::unique_ptr<Ship> std::make_unique<Ship>();
    friend class ShipBuilder;
    Ship() = default;
    void initialize();

    bool unloadCargo(const std::string& cargoName, int amount);
    void rebel();

    Time* _time;
    std::unique_ptr<DifficultLvlStrategy<Ship, bool>> _strategy;
    std::string _name;
    int _capacity;
    int _crew;
    int _durability{1000};
    int _armor{0};
    std::vector<std::unique_ptr<Cargo>> _cargoes;
    std::unique_ptr<PrintVisitor> _visitor;
};
