#ifndef ROUTER_INFO_H__
#define ROUTER_INFO_H__

#include <inttypes.h>
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <boost/asio.hpp>
#include "Identity.h"
#include "Profiling.h"

namespace i2p
{
namespace data
{
    const char CAPS_FLAG_FLOODFILL = 'f';
    const char CAPS_FLAG_HIDDEN = 'H';
    const char CAPS_FLAG_REACHABLE = 'R';
    const char CAPS_FLAG_UNREACHABLE = 'U'; 
    const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K';      
    const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L';  
    const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M'; 
    const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
    const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';

    const char CAPS_FLAG_SSU_TESTING = 'B';
    const char CAPS_FLAG_SSU_INTRODUCER = 'C';

    const int MAX_RI_BUFFER_SIZE = 2048;
    class RouterInfo: public RoutingDestination
    {
        public:

            enum SupportedTranports
            {   
                eNTCPV4 = 0x01,
                eNTCPV6 = 0x02,
                eSSUV4 = 0x04,
                eSSUV6 = 0x08
            };
            
            enum Caps
            {
                eFloodfill = 0x01,
                eHighBandwidth = 0x02,
                eReachable = 0x04,
                eSSUTesting = 0x08,
                eSSUIntroducer = 0x10,
                eHidden = 0x20,
                eUnreachable = 0x40
            };

            enum TransportStyle
            {
                eTransportUnknown = 0,
                eTransportNTCP,
                eTransportSSU
            };

            struct Introducer           
            {
                boost::asio::ip::address iHost;
                int iPort;
                Tag<32> iKey;
                uint32_t iTag;
            };

            struct Address
            {
                TransportStyle transportStyle;
                boost::asio::ip::address host;
                std::string addressString;
                int port, mtu;
                uint64_t date;
                uint8_t cost;
                // SSU only
                Tag<32> key; // intro key for SSU
                std::vector<Introducer> introducers;

                bool IsCompatible (const boost::asio::ip::address& other) const 
                {
                    return (host.is_v4 () && other.is_v4 ()) ||
                        (host.is_v6 () && other.is_v6 ());
                }   
            };
            
            RouterInfo (const std::string& fullPath);
            RouterInfo (): m_Buffer (nullptr) { };

            RouterInfo (const RouterInfo& ) = default;
            RouterInfo& operator=(const RouterInfo& ) = default;
            RouterInfo (const uint8_t * buf, int len);
            ~RouterInfo ();
            
            const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; };
            void SetRouterIdentity (const IdentityEx& identity);
            std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
            std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); };
            uint64_t GetTimestamp () const { return m_Timestamp; };
            std::vector<Address>& GetAddresses () { return m_Addresses; };
            const Address * GetNTCPAddress (bool v4only = true) const;
            const Address * GetSSUAddress (bool v4only = true) const;
            const Address * GetSSUV6Address () const;
            
            void AddNTCPAddress (const char * host, int port);
            void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
            bool AddIntroducer (const Address * address, uint32_t tag);
            bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
            void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only
            void DeleteProperty (const std::string& key); // called from RouterContext only
            void ClearProperties () { m_Properties.clear (); };
            bool IsFloodfill () const;
            bool IsNTCP (bool v4only = true) const;
            bool IsSSU (bool v4only = true) const;
            bool IsV6 () const;
            void EnableV6 ();
            void DisableV6 ();
            bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
            bool UsesIntroducer () const;
            bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
            bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
            bool IsHidden () const { return m_Caps & eHidden; };
            bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };

            uint8_t GetCaps () const { return m_Caps; };    
            void SetCaps (uint8_t caps);
            void SetCaps (const char * caps);

            void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; 
            bool IsUnreachable () const { return m_IsUnreachable; };

            const uint8_t * GetBuffer () const { return m_Buffer; };
            const uint8_t * LoadBuffer (); // load if necessary
            int GetBufferLen () const { return m_BufferLen; };          
            void CreateBuffer (const PrivateKeys& privateKeys);

            bool IsUpdated () const { return m_IsUpdated; };
            void SetUpdated (bool updated) { m_IsUpdated = updated; }; 
            void SaveToFile (const std::string& fullPath);

            std::shared_ptr<RouterProfile> GetProfile () const;
            void SaveProfile () { if (m_Profile) m_Profile->Save (); };
            
            void Update (const uint8_t * buf, int len);
            void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
            
            // implements RoutingDestination
            const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); };
            const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; };
            bool IsDestination () const { return false; };

            
        private:

            bool LoadFile ();
            void ReadFromFile ();
            void ReadFromStream (std::istream& s);
            void ReadFromBuffer (bool verifySignature);
            void WriteToStream (std::ostream& s);
            size_t ReadString (char * str, std::istream& s);
            void WriteString (const std::string& str, std::ostream& s);
            void ExtractCaps (const char * value);
            const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
            void UpdateCapsProperty ();         

        private:

            std::string m_FullPath;
            IdentityEx m_RouterIdentity;
            uint8_t * m_Buffer;
            int m_BufferLen;
            uint64_t m_Timestamp;
            std::vector<Address> m_Addresses;
            std::map<std::string, std::string> m_Properties;
            bool m_IsUpdated, m_IsUnreachable;
            uint8_t m_SupportedTransports, m_Caps;
            mutable std::shared_ptr<RouterProfile> m_Profile;
    };  
}   
}

#endif