#pragma once

#include <cstdint>
#include "storm/storage/memorystructure/MemoryStructure.h"
#include "storm/storage/SchedulerChoice.h"

namespace storm {
    namespace storage {
        template <typename ValueType>
        class PostScheduler;
        /*
         * This class defines which action is chosen in a particular state of a non-deterministic model. More concretely, a scheduler maps a state s to i
         * if the scheduler takes the i-th action available in s (i.e. the choices are relative to the states).
         * A Choice can be undefined, deterministic
         */
        template <typename ValueType>
        class Scheduler {
        public:
            friend class PostScheduler<ValueType>;

            /*!
             * Initializes a scheduler for the given number of model states.
             *
             * @param numberOfModelStates number of model states
             * @param memoryStructure the considered memory structure. If not given, the scheduler is considered as memoryless.
             */
            Scheduler(uint_fast64_t numberOfModelStates, boost::optional<storm::storage::MemoryStructure> const& memoryStructure = boost::none);
            Scheduler(uint_fast64_t numberOfModelStates, boost::optional<storm::storage::MemoryStructure>&& memoryStructure);

            /*!
             * Sets the choice defined by the scheduler for the given state.
             *
             * @param choice The choice to set for the given state.
             * @param modelState The state of the model for which to set the choice.
             * @param memoryState The state of the memoryStructure for which to set the choice.
             */
            void setChoice(SchedulerChoice<ValueType> const& choice, uint_fast64_t modelState, uint_fast64_t memoryState = 0);

            /*!
             * Is the scheduler defined on the states indicated by the selected-states bitvector?
             */
            bool isChoiceSelected(BitVector const& selectedStates, uint64_t memoryState = 0) const;

            /*!
             * Clears the choice defined by the scheduler for the given state.
             *
             * @param modelState The state of the model for which to clear the choice.
             * @param memoryState The state of the memoryStructure for which to clear the choice.
             */
            void clearChoice(uint_fast64_t modelState, uint_fast64_t memoryState = 0);

            /*!
             * Gets the choice defined by the scheduler for the given model and memory state.
             *
             * @param state The state for which to get the choice.
             * @param memoryState the memory state which we consider.
             */
            SchedulerChoice<ValueType> const& getChoice(uint_fast64_t modelState, uint_fast64_t memoryState = 0) const;

            /*!
             * Compute the Action Support: A bit vector that indicates all actions that are selected with positive probability in some memory state
             */
            storm::storage::BitVector computeActionSupport(std::vector<uint64_t> const& nondeterministicChoiceIndicies) const;

            /*!
             * Retrieves whether there is a pair of model and memory state for which the choice is undefined.
             */
            bool isPartialScheduler() const;

            /*!
             * Retrieves whether all defined choices are deterministic
             */
            bool isDeterministicScheduler() const;

            /*!
             * Retrieves whether the scheduler considers a trivial memory structure (i.e., a memory structure with just a single state)
             */
            bool isMemorylessScheduler() const;

            /*!
             * Retrieves the number of memory states this scheduler considers.
             */
            uint_fast64_t getNumberOfMemoryStates() const;

            /*!
             * Retrieves the memory structure associated with this scheduler
             */
            boost::optional<storm::storage::MemoryStructure> const& getMemoryStructure() const;

            /*!
             * Returns a copy of this scheduler with the new value type
             */
            template<typename NewValueType>
			Scheduler<NewValueType> toValueType() const {
                uint_fast64_t numModelStates = schedulerChoices.front().size();
                Scheduler<NewValueType> newScheduler(numModelStates, memoryStructure);
                for (uint_fast64_t memState = 0; memState < this->getNumberOfMemoryStates(); ++memState) {
                    for (uint_fast64_t modelState = 0; modelState < numModelStates; ++modelState) {
                        newScheduler.setChoice(getChoice(modelState, memState).template toValueType<NewValueType>(), modelState, memState);
                    }
                }
				return newScheduler;
			}

            /*!
             * Prints the scheduler to the given output stream.
             * @param out The output stream
             * @param model If given, provides additional information for printing (e.g., displaying the state valuations instead of state indices)
             * @param skipUniqueChoices If true, the (unique) choice for deterministic states (i.e., states with only one enabled choice) is not printed explicitly.
             *                          Requires a model to be given.
             */
            void printToStream(std::ostream& out, std::shared_ptr<storm::models::sparse::Model<ValueType>> model = nullptr, bool skipUniqueChoices = false) const;


            /*!
             * Prints the scheduler in json format to the given output stream.
             */
             void printJsonToStream(std::ostream& out, std::shared_ptr<storm::models::sparse::Model<ValueType>> model = nullptr, bool skipUniqueChoices = false) const;

        private:

            boost::optional<storm::storage::MemoryStructure> memoryStructure;
            std::vector<std::vector<SchedulerChoice<ValueType>>> schedulerChoices;
        protected:
            uint_fast64_t numOfUndefinedChoices;
            uint_fast64_t numOfDeterministicChoices;
        };
    }
}