#include <ostream>
#include <dirent.h>
#include <assert.h>
#include <sstream>
#include <string>
#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>
typedef bool                bool_t;
typedef unsigned char       short_t;
typedef unsigned short      smallint_t;
typedef unsigned int        int_t;
typedef unsigned long       long_t;
namespace variadic
{
    template<bool D, typename Head, typename... Tail>
    struct accumulate_stream
    {
        static void call(std::ostringstream& stream, const Head& head, const Tail&... tail)
        {
            stream << head;
            accumulate_stream<sizeof...(Tail) == 1, Tail...>::call(stream, tail...);
        }
    };
    template<class Head>
    struct accumulate_stream<true, Head>
    {
        static void call(std::ostringstream& stream, const Head& head)
        {
            stream << head;
        }
    };
    template<bool D, typename Head, typename... Tail>
    struct accumulate_stream_separated
    {
        static void call(std::ostringstream& stream, const char separator, const Head& head, const Tail&... tail)
        {
            stream << separator << head;
            accumulate_stream_separated<sizeof...(Tail) == 1, Tail...>::call(stream, tail...);
        }
    };
    template<class Head>
    struct accumulate_stream_separated<true, Head>
    {
        static void call(std::ostringstream& stream, const char separator, const Head& head)
        {
            stream << separator << head;
        }
    };
    template<typename Head, typename... Tail>
    static const std::string accumulate(const Head& head, const Tail&... tail)
    {
        std::ostringstream oss;
        variadic::accumulate_stream<sizeof...(Tail) == 0, Head, Tail...>::call(oss, head, tail...);
        return oss.str();
    }
    template<typename Head, typename... Tail>
    static const std::string accumulate_separator(const char separator, const Head& head, const Tail&... tail)
    {
        std::ostringstream oss;
        variadic::accumulate_stream_separated<sizeof...(Tail) == 0, Head, Tail...>::call(oss, separator, head, tail...);
        return oss.str();
    }
}



////


namespace fs
{
    class any;
    typedef long_t size_t;
    typedef int_t inner_int_t;
    extern const fs::size_t not_fetched = -1;
    extern const fs::size_t unknown = -2;
    extern const fs::size_t empty = 0;
    static any* no_parent = nullptr;
    extern const char separator = '/';
    extern const char dot = '.';
    typedef inner_int_t file_type;
    typedef std::string directory_name;
    typedef std::string file_name;
    typedef std::string path;
    typedef std::string ext;
    class variadic_constructor {};
    class unknown_ext
    {
    public:
        unknown_ext(const fs::ext& E) : m_e(E) {}
        virtual ~unknown_ext() throw() {}
        const fs::ext& what() const throw() { return m_e; }
    private:
        const fs::ext m_e;
    };
    class any
    {
    public:
        typedef std::shared_ptr<any> ptr;
        typedef const std::shared_ptr<any> const_ptr;
        any() : m_parent(fs::no_parent), m_name(), m_size(fs::unknown), m_offset(fs::unknown), m_childs() {std::cout<<"any::no arg"<<std::endl;}
        any(const std::string& n) : m_parent(fs::no_parent), m_name(n), m_size(fs::not_fetched), m_offset(fs::not_fetched), m_childs() {std::cout<<"any::string"<<std::endl;}
        any(ptr p, const std::string& n) : m_parent(p), m_name(n), m_size(fs::not_fetched), m_offset(fs::not_fetched), m_childs() {std::cout<<"any::parent string"<<std::endl;}
        template<typename Head, typename... Tail>
        any(const Head& head, const Tail&... tail) : m_parent(fs::no_parent), m_name(variadic::accumulate_separator(fs::separator, head, tail...)), m_size(fs::unknown), m_offset(fs::unknown), m_childs() {std::cout<<"any::variadic"<<std::endl;}
        virtual ~any() { m_childs.clear(); }
        virtual inline bool is_file() const = 0;
        virtual inline bool is_directory() const = 0;
        const std::string& name() const { return m_name; }
        inline fs::size_t size() const { return m_size; }
        inline fs::size_t offset() const { return m_offset; }
        std::vector<ptr>::iterator begin() { return std::begin(m_childs); }
        std::vector<ptr>::iterator end() { return std::end(m_childs); }
        const std::string path(const std::string& Pf) const
        {
            auto str(variadic::accumulate(Pf, fs::separator, m_name));
            return m_parent ? path(str) : str;
        }
    protected:
        const_ptr m_parent;
        std::string m_name;
        fs::size_t m_size;
        fs::size_t m_offset; /// Only used if you're using fs::write
        std::vector<ptr> m_childs;
        void update_size()
        {
            m_size = std::accumulate(begin(), end(), 0,
                                     [](fs::size_t a, ptr b)
                                     {
                                         assert(b->size() != fs::unknown);
                                         return a+b->size();
                                     });
            if(m_parent) m_parent->update_size();
        }
    private:
        //virtual const std::string str() const = 0;
    };
    class directory: public any
    {
    public:
        typedef std::shared_ptr<directory> ptr;
        typedef const std::shared_ptr<directory> const_ptr;
        class could_not_open
        {
        public:
            could_not_open(const fs::directory_name& P) : m_p(P) {}
            virtual ~could_not_open() throw() {}
            const fs::directory_name& what() const throw() { return m_p; }
        private:
            const fs::directory_name m_p;
        };
        enum child_type: unsigned short
        {
            Unknown,
            OnlyFiles,
            OnlyDirectories,
            Mixed
        };
        directory() : any(){ std::cout << "dir::no arg" << std::endl; }
        directory(const fs::directory_name& n, bool recursive) : any(n){  std::cout << "dir::string/bool"<<std::endl;}
        directory(ptr p, const fs::directory_name& n, bool recursive) : any(p, n) { std::cout << "dir::parent/string/bool"<<std::endl;}
        template<typename Head, typename... Tail>
        directory(const Head& head, const Tail&... tail) : any(head, tail...) {std::cout<<"dir::variadic"<<std::endl;}
        virtual ~directory() {  }
        inline bool is_file() const { return false; }
        inline bool is_directory() const { return true; }
    };
}

int main()
{
    fs::directory::ptr A(new fs::directory());
    fs::directory::ptr B(new fs::directory("ABC", true));
    fs::directory::ptr C(new fs::directory(B, "DEF", true));
    fs::directory::ptr D(new fs::directory("A", "B", "C"));
}