/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#ifndef ecflow_node_ClientSuites_HPP
#define ecflow_node_ClientSuites_HPP

///
/// \brief The definition file could hold hundreds of suites, however the client
/// may only be interested in a small subset. By allowing the client to register
/// the suites they are interested in, we can reduce the network traffic
/// when they ask for updates.(ie via sync or news)
/// When the Client registers an interest in suites it is returned a handle,
/// this handle must is passed back to the server as a reference, with the sync'ing commands
///
/// Users are allowed to register interest in suite that have not yet been added
/// This will only work provided we have a definition
///
///
/// \note Change of suite order is handled by OrderMemento
///       and *NOT* by the ClientSuites, however whenever suites are:
///       registered and added/deleted:
///       then:
///           defs_ptr create_defs(Defs* server_defs) const;
///
///       Will return the suites in the same order as the defs
///

#include <limits>
#include <string>
#include <vector>

#include "ecflow/node/NodeFwd.hpp"

namespace ecf {

struct HSuite
{
    HSuite(const std::string& name, weak_suite_ptr p, int index = std::numeric_limits<int>::max())
        : name_(name),
          weak_suite_ptr_(p),
          index_(index) {}

    explicit HSuite(const std::string& name) : name_(name), index_(std::numeric_limits<int>::max()) {}

    std::string name_;              // suite name
    weak_suite_ptr weak_suite_ptr_; // does suite exist in defs, need to lock, to find out
    int index_;                     // suites in handles must be in same order as defs
};

class ClientSuites {
public:
    /// Will automatically create a unique handle
    ClientSuites(Defs*,
                 unsigned int handle,
                 bool auto_add_new_suites,
                 const std::vector<std::string>& suites,
                 const std::string& user);

    /// Return the handle, returned to the client, and required for all correspondence
    /// between client server. This value is always > 0
    unsigned int handle() const { return handle_; }

    /// return the user who requested this handle
    const std::string& user() const { return user_; }

    /// The handle changed flag is set to true whenever a ClinetSuites is created
    /// or when suites are added/removed from it. The alternative way would have
    /// been to update the modify change number, but this would have affected all handles
    bool handle_changed() const { return handle_changed_; }

    /// Add an interest to this suite
    void add_suite(const std::string&);
    void add_suite(suite_ptr);

    /// no longer interested in this suite. Explicit remove
    void remove_suite(const std::string&);
    bool remove_suite(suite_ptr);

    /// A new suite has been added to the definition.
    /// If it was already registered update the suite ptr
    /// If auto add new suite enabled,register it
    void suite_added_in_defs(suite_ptr);

    /// A suite has been updated in the definition.
    void suite_replaced_in_defs(suite_ptr);

    /// The suite has been deleted, update modify_change_no. So we do a full sync
    /// RESETs suite ptr. Deleted suites are *NOT* automatically removed
    void suite_deleted_in_defs(suite_ptr);

    /// Collate the incremental changes, made to my suites
    void collateChanges(DefsDelta& changes) const;

    // Only return the defs state and suites that the client has registered in this suite
    // *HOWEVER* if the client has registered all the suites, just return the server defs
    //           *with* the updated change numbers
    // *OTHERWISE*
    /// Creates a new defs, by adding the suites to the defs
    /// The defs is to be transferred to the client
    /// Suites are returned in the same order as the defs
    /// We avoid copying, but need to adjust suites defs pointer
    /// This will clear the handle_changed_  flag
    defs_ptr create_defs(defs_ptr server_defs) const;

    // iterates overs its suites and return max state and modify change numbers
    void max_change_no(unsigned int& state_change_no, unsigned int& modify_change_no) const;

    /// Enable/disable interest in new suites.
    /// if the flag is true, when ever new suites are added to the defs
    /// The internal list is updated.
    void add_new_suite(bool flag) { auto_add_new_suites_ = flag; }
    bool auto_add_new_suites() const { return auto_add_new_suites_; }

    /// returns the list of suites
    void suites(std::vector<std::string>& names) const;

    /// Update suites to be in same order as Defs.
    /// This should be done externally to avoid update after individual add
    void update_suite_order();

    /// For debug dumps
    std::string dump() const;

private:
    std::vector<HSuite>::iterator find_suite(const std::string& name);

private:
    Defs* defs_;

    std::string user_; // user who create this handle
    std::vector<HSuite> suites_;

    unsigned int handle_; // This must be unique

    // The modify_change_no_ is required specifically when a registered suite is deleted
    // Both modify_change_no_ & state_change_no_ are required when user has registered
    // with *ALL* the suites. In this case we need to ensure that after a SYNC/create_defs
    // the NewsCmd call to max_change_no() is in sync with global change numbers.
    mutable unsigned int state_change_no_{0};
    mutable unsigned int modify_change_no_{0};

    bool auto_add_new_suites_;
    mutable bool handle_changed_{false}; // set when handle created, or when suites added/removed
};

} // namespace ecf

#endif /* ecflow_node_ClientSuites_HPP */
