/*
    Copyright (C) 2021 by Pawel Soja <kernel32.pl@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#pragma once

#include "indiproperty.h"
#include "indimacros.h"
#include <vector>
#include <deque>
#include <algorithm>

#define INDI_PROPERTIES_BACKWARD_COMPATIBILE
namespace INDI
{

class PropertiesPrivate;
class Properties
{
        DECLARE_PRIVATE(Properties)

    public:
        using iterator        = std::deque<INDI::Property>::iterator;
        using const_iterator  = std::deque<INDI::Property>::const_iterator;

        // #PS 2024: workaround; SWIG 4.2.0 - SwigSmartPointer, does not create a pointer, just an uninitialized reference.
        // using reference       = std::deque<INDI::Property>::reference;
        // using const_reference = std::deque<INDI::Property>::const_reference;
        using reference       = INDI::Property &;
        using const_reference = const INDI::Property &;
        
        using size_type       = std::deque<INDI::Property>::size_type;

    public:
        Properties();
        ~Properties();

    public:
        void push_back(const INDI::Property &property);
        void push_back(INDI::Property &&property);
        void clear();

    public:
        bool empty() const;

    public:
        size_type size() const;

    public:
        reference at(size_type pos);
        const_reference at(size_type pos) const;

        reference operator[](size_type pos);
        const_reference operator[](size_type pos) const;

        reference front();
        const_reference front() const;

        reference back();
        const_reference back() const;

    public:
        iterator begin();
        iterator end();

        const_iterator begin() const;
        const_iterator end() const;

    public:
        iterator erase(iterator pos);
        iterator erase(const_iterator pos);
        iterator erase(iterator first, iterator last);
        iterator erase(const_iterator first, const_iterator last);

        template<typename Predicate>
        iterator erase_if(Predicate predicate);

    public:
#ifdef INDI_PROPERTIES_BACKWARD_COMPATIBILE
        INDI::Properties operator *();
        const INDI::Properties operator *() const;

        Properties *operator->();
        const Properties *operator->() const;

        operator std::vector<INDI::Property *> *();
        operator const std::vector<INDI::Property *> *() const;

        operator Properties *();
        operator const Properties *() const;
#endif

    protected:
        std::shared_ptr<PropertiesPrivate> d_ptr;
        Properties(std::shared_ptr<PropertiesPrivate> dd);
};

template<typename Predicate>
inline Properties::iterator Properties::erase_if(Predicate predicate)
{
    return erase(std::remove_if(begin(), end(), predicate), end());
}

}
