/*
 * Copyright (c) 2012 Mark McCurry
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#ifndef RTOSC_PORTS
#define RTOSC_PORTS

#include <vector>
#include <functional>
#include <initializer_list>
#include <rtosc/rtosc.h>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <cstdio>
#include <string>

namespace rtosc {

//First define all types
typedef const char *msg_t;

struct Port;
struct Ports;

struct RtData
{
    RtData(void);

    char *loc;
    size_t loc_size;
    void *obj;
    int  matches;
    const Port *port;
    const char *message;

    virtual void replyArray(const char *path, const char *args,
            rtosc_arg_t *vals);
    virtual void reply(const char *path, const char *args, ...);
    virtual void reply(const char *msg);
    virtual void chain(const char *path, const char *args, ...);
    virtual void chain(const char *msg);
    virtual void chainArray(const char *path, const char *args,
            rtosc_arg_t *vals);
    virtual void broadcast(const char *path, const char *args, ...);
    virtual void broadcast(const char *msg);
    virtual void broadcastArray(const char *path, const char *args,
            rtosc_arg_t *vals);

    virtual void forward(const char *rational=NULL);
};


/**
 * Port in rtosc dispatching hierarchy
 */
struct Port {
    const char  *name;    //< Pattern for messages to match
    const char  *metadata;//< Statically accessable data about port
    const Ports *ports;   //< Pointer to further ports
    std::function<void(msg_t, RtData&)> cb;//< Callback for matching functions

    class MetaIterator
    {
        public:
            MetaIterator(const char *str);

            //A bit odd to return yourself, but it seems to work for this
            //context
            const MetaIterator& operator*(void) const {return *this;}
            const MetaIterator* operator->(void) const {return this;}
            bool operator==(MetaIterator a) {return title == a.title;}
            bool operator!=(MetaIterator a) {return title != a.title;}
            MetaIterator& operator++(void);

            const char *title;
            const char *value;
    };

    class MetaContainer
    {
        public:
            MetaContainer(const char *str_);

            MetaIterator begin(void) const;
            MetaIterator end(void) const;

            MetaIterator find(const char *str) const;
            size_t length(void) const;
            const char *operator[](const char *str) const;

            const char *str_ptr;
    };

    MetaContainer meta(void) const
    {
        if(metadata && *metadata == ':')
            return MetaContainer(metadata+1);
        else
            return MetaContainer(metadata);
    }
};

/**
 * Ports - a dispatchable collection of Port entries
 *
 * This structure makes it somewhat easier to perform actions on collections of
 * port entries and it is responsible for the dispatching of OSC messages to
 * their respective ports.
 * That said, it is a very simple structure, which uses a stl container to store
 * all data in a simple dispatch table.
 * All methods post-initialization are RT safe (assuming callbacks are RT safe)
 */
struct Ports
{
    std::vector<Port> ports;
    std::function<void(msg_t, RtData&)> default_handler;

    typedef std::vector<Port>::const_iterator itr_t;

    /**Forwards to builtin container*/
    itr_t begin() const {return ports.begin();}

    /**Forwards to builtin container*/
    itr_t end() const {return ports.end();}

    /**Forwards to builtin container*/
    size_t size() const {return ports.size();}

    /**Forwards to builtin container*/
    const Port &operator[](unsigned i) const {return ports[i];}

    Ports(std::initializer_list<Port> l);
    ~Ports(void);

    Ports(const Ports&) = delete;

    /**
     * Dispatches message to all matching ports.
     * This uses simple pattern matching available in rtosc::match.
     *
     * @param m a valid OSC message
     * @param d The RtData object shall contain a path buffer (or null), the length of
     *          the buffer, a pointer to data.
     */
    void dispatch(const char *m, RtData &d, bool base_dispatch=false) const;

    /**
     * Retrieve local port by name
     * TODO implement full matching
     */
    const Port *operator[](const char *name) const;


    /**
     * Find the best match for a given path
     *
     * @parameter path partial OSC path
     * @returns first path prefixed by the argument
     *
     * Example usage:
     * @code
     *    Ports p = {{"foo",0,0,dummy_method},
     *               {"flam",0,0,dummy_method},
     *               {"bar",0,0,dummy_method}};
     *    p.apropos("/b")->name;//bar
     *    p.apropos("/f")->name;//foo
     *    p.apropos("/fl")->name;//flam
     *    p.apropos("/gg");//NULL
     * @endcode
     */
    const Port *apropos(const char *path) const;

    /**
     * Collapse path with parent path identifiers "/.."
     *
     * e.g. /foo/bar/../baz => /foo/baz
     */
    static char *collapsePath(char *p);

    protected:
    void refreshMagic(void);
    private:
    //Performance hacks
    class Port_Matcher *impl;
    unsigned elms;
};

struct ClonePort
{
    const char *name;
    std::function<void(msg_t, RtData&)> cb;
};

struct ClonePorts:public Ports
{
    ClonePorts(const Ports &p,
            std::initializer_list<ClonePort> c);
};

struct MergePorts:public Ports
{
    MergePorts(std::initializer_list<const Ports*> c);
};


/*********************
 * Port walking code *
 *********************/
//typedef std::function<void(const Port*,const char*)> port_walker_t;
typedef void(*port_walker_t)(const Port*,const char*,void*);

void walk_ports(const Ports *base,
        char          *name_buffer,
        size_t         buffer_size,
        void          *data,
        port_walker_t  walker);

/*********************
 * Port Dumping code *
 *********************/

struct OscDocFormatter
{
    const Ports *p;
    std::string prog_name;
    std::string uri;
    std::string doc_origin;
    std::string author_first;
    std::string author_last;
    //TODO extend this some more
};

std::ostream &operator<<(std::ostream &o, OscDocFormatter &formatter);
};
#endif
