#pragma once

#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "RadioInterface.h"
#include "mesh.pb.h"

/**
 * A mesh aware router that supports multiple interfaces.
 */
class Router
{
  private:
    RadioInterface *iface;

    /// Packets which have just arrived from the radio, ready to be processed by this service and possibly
    /// forwarded to the phone.
    PointerQueue<MeshPacket> fromRadioQueue;

  public:
    /// Local services that want to see _every_ packet this node receives can observe this.
    /// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting
    /// freed)
    Observable<const MeshPacket *> notifyPacketReceived;

    /**
     * Constructor
     *
     */
    Router();

    /**
     * Currently we only allow one interface, that may change in the future
     */
    void addInterface(RadioInterface *_iface)
    {
        iface = _iface;
        iface->setReceiver(&fromRadioQueue);
    }

    /**
     * do idle processing
     * Mostly looking in our incoming rxPacket queue and calling handleReceived.
     */
    virtual void loop();

    /**
     * Works like send, but if we are sending to the local node, we directly put the message in the receive queue
     *
     * NOTE: This method will free the provided packet (even if we return an error code)
     */
    ErrorCode sendLocal(MeshPacket *p);

    /// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
    MeshPacket *allocForSending();

    /**
     * @return our local nodenum */
    NodeNum getNodeNum();

  protected:
    /**
     * Send a packet on a suitable interface.  This routine will
     * later free() the packet to pool.  This routine is not allowed to stall.
     * If the txmit queue is full it might return an error
     *
     * NOTE: This method will free the provided packet (even if we return an error code)
     */
    virtual ErrorCode send(MeshPacket *p);

    /**
     * Should this incoming filter be dropped?
     *
     * Called immedately on receiption, before any further processing.
     * @return true to abandon the packet
     */
    virtual bool shouldFilterReceived(const MeshPacket *p) { return false; }

    /**
     * Every (non duplicate) packet this node receives will be passed through this method.  This allows subclasses to
     * update routing tables etc... based on what we overhear (even for messages not destined to our node)
     */
    virtual void sniffReceived(const MeshPacket *p);

    /**
     * Remove any encryption and decode the protobufs inside this packet (if necessary).
     *
     * @return true for success, false for corrupt packet.
     */
    bool perhapsDecode(MeshPacket *p);

  private:
    /**
     * Called from loop()
     * Handle any packet that is received by an interface on this node.
     * Note: some packets may merely being passed through this node and will be forwarded elsewhere.
     *
     * Note: this packet will never be called for messages sent/generated by this node.
     * Note: this method will free the provided packet.
     */
    void perhapsHandleReceived(MeshPacket *p);

    /**
     * Called from perhapsHandleReceived() - allows subclass message delivery behavior.
     * Handle any packet that is received by an interface on this node.
     * Note: some packets may merely being passed through this node and will be forwarded elsewhere.
     *
     * Note: this packet will never be called for messages sent/generated by this node.
     * Note: this method will free the provided packet.
     */
    void handleReceived(MeshPacket *p);
};

extern Router &router;

/// Generate a unique packet id
// FIXME, move this someplace better
PacketId generatePacketId();