#include <iostream>
#include <iomanip>
#include <cctype>
#include "binbuf.h"
// Insert a string using a length byte followed by it's characters.
// For DNS requests, the limit is 63 characters, for the basic format
// 255 character limit.  This method performs no check on all that,
// so don't abuse it.
void BinaryBuffer::puts(std::string s) {
        put8(s.length());
        for(auto x: s) put(x);
}
// Get a string by reading a length byte then that many characters.
std::string BinaryBuffer::gets() {
        int len = get();
        std::string ret;
        while(len--) ret.push_back(get());
        return ret;
}
/*
 * Put a host name as a series of lenth/data strings ending with a zero
 * byte, per RFC 1035
 */
void BinaryBuffer::puthost(string host)
{
        int start = 0;
        while(start < host.length()) {
                int next = host.find(".", start);
                if(next == string::npos) next = host.length();
                puts(host.substr(start,next-start));
                start = next + 1;
        }
        puts("");
}
/*
 * Get a segment starting with a length.  If accum is not empty, add a .
 * then add characters until either a forward or terminator.  Returns
 * the location that stopped it.
 */
unsigned int BinaryBuffer::segment(unsigned int start, string &accum)
{
        unsigned ct = m_data[start++];
        if(accum != "") accum.push_back('.');
        while(ct--) {
                accum.push_back(m_data[start++]);
        }
        return start;
}
/*
 * Get a host name as a series of lenth/data strings, obeying the offset
 * compression semantics, per RFC 1035.
 */
string BinaryBuffer::gethost()
{
        string ret = "";
        // Reads segments which are in place until end or reference.
        while(m_data[m_rwloc] != 0 && !is_jump(m_data[m_rwloc]))
        {
                m_rwloc = segment(m_rwloc,ret);
        }
        // If we're done, we stop.
        unsigned int loc = m_rwloc++;
        if(m_data[loc] == 0) {
                // Terminator.  We're done, and m_wrloc is past terminator.
                return ret;
        } else {
                // Jump.  We continue, and m_wrloc is only half way past,
                // so we need to advance it again.
                ++m_rwloc;
        }
        // Process any back references.
        do {
                if(is_jump(m_data[loc]))
                        loc = (((static_cast<unsigned>(m_data[loc])) << 8) |
                               (static_cast<unsigned>(m_data[loc+1])))
                                & 0x03fff;
                loc = segment(loc,ret);
        } while(m_data[loc]);
        return ret;
}
// Dump the contents up to the location, or the indicated size.
void BinaryBuffer::dump(int limit, std::ostream &strm) const
{
        static const int bytes_per_group = 4;
        static const int groups_per_line = 5;
        static const int bytes_per_line = bytes_per_group * groups_per_line;
        if(limit < 0) limit = m_rwloc;
        unsigned int line = 0;
        while(limit > 0) {
                int linelimit = limit > bytes_per_line ? bytes_per_line : limit;
                strm << " " << std::setw(3) << std::setfill('0') << std::right
                     << std::dec << line << " ";
                int grct = bytes_per_group;
                for(int i = 0; i < bytes_per_line; ++i) {
                        if(i < linelimit) {
                                strm << std::setw(2) << std::setfill('0')
                                     << std::right << std::hex
                                     << static_cast<unsigned int>
                                        (m_data[line+i]);
                        } else {
                                strm << "  ";
                        }
                        
                        if(--grct <= 0) {
                                strm << " ";
                                grct = bytes_per_group;
                        }
                }
                strm << "  ";
                for(int i = 0; i < linelimit; ++i) {
                        char ch = static_cast<char>(m_data[line+i]);
                        if(isprint(ch))
                                strm << ch;
                        else
                                strm << ".";
                }
                strm << std::endl;
                limit -= linelimit;
                line += linelimit;
        }
}