/****************************************************************************
** Copyright (c) 2001-2014
**
** This file is part of the QuickFIX FIX Engine
**
** This file may be distributed under the terms of the quickfixengine.org
** license as defined by quickfixengine.org and appearing in the file
** LICENSE included in the packaging of this file.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.quickfixengine.org/LICENSE for licensing information.
**
** Contact ask@quickfixengine.org if any conditions of this licensing are
** not clear to you.
**
****************************************************************************/

#ifdef _MSC_VER
#include "stdafx.h"
#else
#include "config.h"
#include <poll.h>
#endif

#include "HtmlBuilder.h"
#include "HttpConnection.h"
#include "HttpMessage.h"
#include "Session.h"
#include "Utility.h"

using namespace HTML;

namespace FIX {
HttpConnection::HttpConnection(socket_handle s)
    : m_socket(s) {
#ifdef _MSC_VER
  FD_ZERO(&m_fds);
  FD_SET(m_socket, &m_fds);
#endif
}

bool HttpConnection::send(const std::string &msg) { return socket_send(m_socket, msg.c_str(), msg.length()) >= 0; }

void HttpConnection::disconnect(int error) {
  if (error > 0) {
    send(HttpMessage::createResponse(error));
  }

  socket_close(m_socket);
}

bool HttpConnection::read() {
#if _MSC_VER
  struct timeval timeout = {2, 0};
  fd_set readset = m_fds;
#else
  int timeout = 2000; // 2000ms = 2 seconds
  struct pollfd pfd = {m_socket, POLLIN | POLLPRI, 0};
#endif

  try {
#if _MSC_VER
    // Wait for input (1 second timeout)
    int result = select(0, &readset, 0, 0, &timeout);
#else
    // Wait for input (2 second timeout)
    int result = poll(&pfd, 1, timeout);
#endif

    if (result > 0) // Something to read
    {
      // We can read without blocking
      ssize_t size = socket_recv(m_socket, m_buffer, sizeof(m_buffer));
      if (size <= 0) {
        throw SocketRecvFailed(size);
      }
      m_parser.addToStream(m_buffer, size);
    } else if (result == 0) // Timeout
    {
      disconnect(408);
      return false;
    } else if (result < 0) // Error
    {
      throw SocketRecvFailed(result);
    }

    processStream();
    return true;
  } catch (SocketRecvFailed &) {
    disconnect();
    return false;
  }
}

bool HttpConnection::readMessage(std::string &msg) EXCEPT(SocketRecvFailed) {
  try {
    return m_parser.readHttpMessage(msg);
  } catch (MessageParseError &) {
    disconnect(400);
  }
  return true;
}

void HttpConnection::processStream() {
  std::string msg;
  try {
    if (!readMessage(msg)) {
      return;
    }
    HttpMessage request(msg);
    processRequest(request);
  } catch (InvalidMessage &) {
    disconnect(400);
    return;
  }

  return;
}

void HttpConnection::processRequest(const HttpMessage &request) {
  int error = 200;
  std::stringstream h;
  std::stringstream b;
  std::string titleString = "QuickFIX Engine Web Interface";

  {
    HEAD head(h);
    head.text();
    {
      CENTER center(h);
      center.text();
      {
        TITLE title(h);
        title.text(titleString);
      }
      {
        H1 h1(h);
        h1.text(titleString);
      }
    }
    {
      CENTER center(h);
      center.text();
      {
        A a(h);
        a.href("/").text("HOME");
      }
      h << NBSP;
      {
        A a(h);
        a.href(request.toString()).text("RELOAD");
      }
    }
    HR hr(h);
    hr.text();
  }

  BODY body(b);
  body.text();

  try {
    if (request.getRootString() == "/") {
      processRoot(request, h, b);
    } else if (request.getRootString() == "/resetSessions") {
      processResetSessions(request, h, b);
    } else if (request.getRootString() == "/refreshSessions") {
      processRefreshSessions(request, h, b);
    } else if (request.getRootString() == "/enableSessions") {
      processEnableSessions(request, h, b);
    } else if (request.getRootString() == "/disableSessions") {
      processDisableSessions(request, h, b);
    } else if (request.getRootString() == "/session") {
      processSession(request, h, b);
    } else if (request.getRootString() == "/resetSession") {
      processResetSession(request, h, b);
    } else if (request.getRootString() == "/refreshSession") {
      processRefreshSession(request, h, b);
    } else {
      error = 404;
    }
  } catch (std::exception &e) {
    error = 400;
    b << e.what();
  }

  std::string response = "<HTML>" + h.str() + b.str() + "</HTML>";
  send(HttpMessage::createResponse(error, error == 200 ? response : ""));

  disconnect();
}

void HttpConnection::processRoot(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  TABLE table(b);
  table.border(1).cellspacing(2).width(100).text();

  {
    CAPTION caption(b);
    caption.text();
    EM em(b);
    em.text();
    b << Session::numSessions() << " Sessions managed by QuickFIX";
    {
      HR hr(b);
      hr.text();
    }
    {
      b << NBSP;
      A a(b);
      a.href("/resetSessions" + request.getParameterString()).text("RESET");
    }
    {
      b << NBSP;
      A a(b);
      a.href("/refreshSessions" + request.getParameterString()).text("REFRESH");
    }
    {
      b << NBSP;
      A a(b);
      a.href("/enableSessions" + request.getParameterString()).text("ENABLE");
    }
    {
      b << NBSP;
      A a(b);
      a.href("/disableSessions" + request.getParameterString()).text("DISABLE");
    }
  }

  {
    TR tr(b);
    tr.text();
    {
      TD td(b);
      td.align("center").text("Session");
    }
    {
      TD td(b);
      td.align("center").text("ConnectionType");
    }
    {
      TD td(b);
      td.align("center").text("Enabled");
    }
    {
      TD td(b);
      td.align("center").text("Session Time");
    }
    {
      TD td(b);
      td.align("center").text("Logged On");
    }
    {
      TD td(b);
      td.align("center").text("Next Incoming");
    }
    {
      TD td(b);
      td.align("center").text("Next Outgoing");
    }
  }

  std::set<SessionID> sessions = Session::getSessions();
  for (const SessionID &sessionID : sessions) {
    Session *pSession = Session::lookupSession(sessionID);
    if (!pSession) {
      continue;
    }

    {
      TR tr(b);
      tr.text();
      {
        TD td(b);
        td.text();
        std::string href = "/session?BeginString=" + sessionID.getBeginString().getValue()
                           + "&SenderCompID=" + sessionID.getSenderCompID().getValue()
                           + "&TargetCompID=" + sessionID.getTargetCompID().getValue();
        if (sessionID.getSessionQualifier().size()) {
          href += "&SessionQualifier=" + sessionID.getSessionQualifier();
        }

        A a(b);
        a.href(href).text(sessionID.toString());
      }
      {
        TD td(b);
        td.text(pSession->isInitiator() ? "initiator" : "acceptor");
      }
      {
        TD td(b);
        td.text(pSession->isEnabled() ? "yes" : "no");
      }
      {
        TD td(b);
        td.text(pSession->isSessionTime(UtcTimeStamp::now()) ? "yes" : "no");
      }
      {
        TD td(b);
        td.text(pSession->isLoggedOn() ? "yes" : "no");
      }
      {
        TD td(b);
        td.text(pSession->getExpectedTargetNum());
      }
      {
        TD td(b);
        td.text(pSession->getExpectedSenderNum());
      }
    }
  }
}

void HttpConnection::processResetSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;

    bool confirm = false;
    if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
      confirm = true;
      std::set<SessionID> sessions = Session::getSessions();
      std::set<SessionID>::iterator session;
      for (session = sessions.begin(); session != sessions.end(); ++session) {
        Session::lookupSession(*session)->reset();
      }
      copy.removeParameter("confirm");
    }

    if (confirm) {
      h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
      CENTER center(b);
      center.text();
      H2 h2(b);
      h2.text();
      {
        A a(b);
        a.href("/").text("Sessions");
      }
      b << " have been reset";
    } else {
      {
        CENTER center(b);
        center.text();
        H2 h2(b);
        h2.text();
        b << "Are you sure you want to reset all sessions ?";
      }
      {
        CENTER center(b);
        center.text();
        b << "[";
        {
          A a(b);
          a.href(request.toString() + "?confirm=1").text("YES, reset sessions");
        }
        b << "]" << NBSP << "[";
        {
          A a(b);
          a.href("/").text("NO, do not reset sessions");
        }
        b << "]";
      }
    }
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::processRefreshSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;

    bool confirm = false;
    if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
      confirm = true;
      std::set<SessionID> sessions = Session::getSessions();
      for (const SessionID &sessionID : sessions) {
        Session::lookupSession(sessionID)->refresh();
      }
      copy.removeParameter("confirm");
    }

    if (confirm) {
      h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
      CENTER center(b);
      center.text();
      H2 h2(b);
      h2.text();
      {
        A a(b);
        a.href("/").text("Sessions");
      }
      b << " have been refreshed";
    } else {
      {
        CENTER center(b);
        center.text();
        H2 h2(b);
        h2.text();
        b << "Are you sure you want to refresh all sessions ?";
      }
      {
        CENTER center(b);
        center.text();
        b << "[";
        {
          A a(b);
          a.href(request.toString() + "?confirm=1").text("YES, refresh sessions");
        }
        b << "]" << NBSP << "[";
        {
          A a(b);
          a.href("/").text("NO, do not refresh sessions");
        }
        b << "]";
      }
    }
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::processEnableSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;

    bool confirm = false;
    if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
      confirm = true;
      std::set<SessionID> sessions = Session::getSessions();
      for (const SessionID &sessionID : sessions) {
        Session::lookupSession(sessionID)->logon();
      }
      copy.removeParameter("confirm");
    }

    if (confirm) {
      h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
      CENTER center(b);
      center.text();
      H2 h2(b);
      h2.text();
      {
        A a(b);
        a.href("/").text("Sessions");
      }
      b << " have been enabled";
    } else {
      {
        CENTER center(b);
        center.text();
        H2 h2(b);
        h2.text();
        b << "Are you sure you want to enable all sessions ?";
      }
      {
        CENTER center(b);
        center.text();
        b << "[";
        {
          A a(b);
          a.href(request.toString() + "?confirm=1").text("YES, enable sessions");
        }
        b << "]" << NBSP << "[";
        {
          A a(b);
          a.href("/").text("NO, do not enable sessions");
        }
        b << "]";
      }
    }
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::processDisableSessions(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;

    bool confirm = false;
    if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
      confirm = true;
      std::set<SessionID> sessions = Session::getSessions();
      for (const SessionID &sessionID : sessions) {
        Session::lookupSession(sessionID)->logout();
      }
      copy.removeParameter("confirm");
    }

    if (confirm) {
      h << "<META http-equiv='refresh' content=2;URL='" << "/'>";
      CENTER center(b);
      center.text();
      H2 h2(b);
      h2.text();
      {
        A a(b);
        a.href("/").text("Sessions");
      }
      b << " have been disabled";
    } else {
      {
        CENTER center(b);
        center.text();
        H2 h2(b);
        h2.text();
        b << "Are you sure you want to disable all sessions ?";
      }
      {
        CENTER center(b);
        center.text();
        b << "[";
        {
          A a(b);
          a.href(request.toString() + "?confirm=1").text("YES, disable sessions");
        }
        b << "]" << NBSP << "[";
        {
          A a(b);
          a.href("/").text("NO, do not disable sessions");
        }
        b << "]";
      }
    }
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::processSession(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;
    std::string url = request.toString();
    std::string beginString = copy.getParameter("BeginString");
    std::string senderCompID = copy.getParameter("SenderCompID");
    std::string targetCompID = copy.getParameter("TargetCompID");
    std::string sessionQualifier;
    if (copy.hasParameter("SessionQualifier")) {
      sessionQualifier = copy.getParameter("SessionQualifier");
    }

    SessionID sessionID(beginString, senderCompID, targetCompID, sessionQualifier);
    Session *pSession = Session::lookupSession(sessionID);
    if (pSession == 0) {
      throw SessionNotFound();
    }

    if (copy.hasParameter("Enabled")) {
      copy.getParameter("Enabled") == "0" ? pSession->logout() : pSession->logon();
      copy.removeParameter("Enabled");
    }
    if (copy.hasParameter("Next%20Incoming")) {
      int value = IntConvertor::convert(copy.getParameter("Next%20Incoming"));
      pSession->setNextTargetMsgSeqNum(value <= 0 ? 1 : value);
      copy.removeParameter("Next%20Incoming");
    }
    if (copy.hasParameter("Next%20Outgoing")) {
      int value = IntConvertor::convert(copy.getParameter("Next%20Outgoing"));
      pSession->setNextSenderMsgSeqNum(value <= 0 ? 1 : value);
      copy.removeParameter("Next%20Outgoing");
    }
    if (copy.hasParameter(SEND_REDUNDANT_RESENDREQUESTS)) {
      pSession->setSendRedundantResendRequests(copy.getParameter(SEND_REDUNDANT_RESENDREQUESTS) != "0");
      copy.removeParameter(SEND_REDUNDANT_RESENDREQUESTS);
    }
    if (copy.hasParameter(CHECK_COMPID)) {
      pSession->setCheckCompId(copy.getParameter(CHECK_COMPID) != "0");
      copy.removeParameter(CHECK_COMPID);
    }
    if (copy.hasParameter(CHECK_LATENCY)) {
      pSession->setCheckLatency(copy.getParameter(CHECK_LATENCY) != "0");
      copy.removeParameter(CHECK_LATENCY);
    }
    if (copy.hasParameter(MAX_LATENCY)) {
      int value = IntConvertor::convert(copy.getParameter(MAX_LATENCY));
      pSession->setMaxLatency(value <= 0 ? 1 : value);
      copy.removeParameter(MAX_LATENCY);
    }
    if (copy.hasParameter(LOGON_TIMEOUT)) {
      int value = IntConvertor::convert(copy.getParameter(LOGON_TIMEOUT));
      pSession->setLogonTimeout(value <= 0 ? 1 : value);
      copy.removeParameter(LOGON_TIMEOUT);
    }
    if (copy.hasParameter(LOGOUT_TIMEOUT)) {
      int value = IntConvertor::convert(copy.getParameter(LOGOUT_TIMEOUT));
      pSession->setLogoutTimeout(value <= 0 ? 1 : value);
      copy.removeParameter(LOGOUT_TIMEOUT);
    }
    if (copy.hasParameter(RESET_ON_LOGON)) {
      pSession->setResetOnLogon(copy.getParameter(RESET_ON_LOGON) != "0");
      copy.removeParameter(RESET_ON_LOGON);
    }
    if (copy.hasParameter(RESET_ON_LOGOUT)) {
      pSession->setResetOnLogout(copy.getParameter(RESET_ON_LOGOUT) != "0");
      copy.removeParameter(RESET_ON_LOGOUT);
    }
    if (copy.hasParameter(RESET_ON_DISCONNECT)) {
      pSession->setResetOnDisconnect(copy.getParameter(RESET_ON_DISCONNECT) != "0");
      copy.removeParameter(RESET_ON_DISCONNECT);
    }
    if (copy.hasParameter(REFRESH_ON_LOGON)) {
      pSession->setRefreshOnLogon(copy.getParameter(REFRESH_ON_LOGON) != "0");
      copy.removeParameter(REFRESH_ON_LOGON);
    }
    if (copy.hasParameter(MILLISECONDS_IN_TIMESTAMP)) {
      pSession->setMillisecondsInTimeStamp(copy.getParameter(MILLISECONDS_IN_TIMESTAMP) != "0");
      copy.removeParameter(MILLISECONDS_IN_TIMESTAMP);
    }
    if (copy.hasParameter(PERSIST_MESSAGES)) {
      pSession->setPersistMessages(copy.getParameter(PERSIST_MESSAGES) != "0");
      copy.removeParameter(PERSIST_MESSAGES);
    }
    if (copy.hasParameter(SEND_NEXT_EXPECTED_MSG_SEQ_NUM)) {
      pSession->setSendNextExpectedMsgSeqNum(copy.getParameter(SEND_NEXT_EXPECTED_MSG_SEQ_NUM) != "0");
      copy.removeParameter(SEND_NEXT_EXPECTED_MSG_SEQ_NUM);
    }

    if (url != copy.toString()) {
      h << "<META http-equiv='refresh' content=0;URL='" << copy.toString() << "'>";
    }

    TABLE table(b);
    table.border(1).cellspacing(2).width(100).text();

    {
      CAPTION caption(b);
      caption.text();
      EM em(b);
      em.text();
      b << sessionID;
      {
        HR hr(b);
        hr.text();
      }
      {
        b << NBSP;
        A a(b);
        a.href("/resetSession" + copy.getParameterString()).text("RESET");
      }
      {
        b << NBSP;
        A a(b);
        a.href("/refreshSession" + copy.getParameterString()).text("REFRESH");
      }
    }

    showRow(b, "Enabled", pSession->isEnabled(), url);
    showRow(b, "ConnectionType", std::string(pSession->isInitiator() ? "initiator" : "acceptor"));
    showRow(b, "SessionTime", pSession->isSessionTime(UtcTimeStamp::now()));
    showRow(b, "Logged On", pSession->isLoggedOn());
    showRow(b, "Next Incoming", (int)pSession->getExpectedTargetNum(), url);
    showRow(b, "Next Outgoing", (int)pSession->getExpectedSenderNum(), url);
    showRow(b, SEND_REDUNDANT_RESENDREQUESTS, pSession->getSendRedundantResendRequests(), url);
    showRow(b, CHECK_COMPID, pSession->getCheckCompId(), url);
    showRow(b, CHECK_LATENCY, pSession->getCheckLatency(), url);
    showRow(b, MAX_LATENCY, pSession->getMaxLatency(), url);
    showRow(b, LOGON_TIMEOUT, pSession->getLogonTimeout(), url);
    showRow(b, LOGOUT_TIMEOUT, pSession->getLogoutTimeout(), url);
    showRow(b, RESET_ON_LOGON, pSession->getResetOnLogon(), url);
    showRow(b, RESET_ON_LOGOUT, pSession->getResetOnLogout(), url);
    showRow(b, RESET_ON_DISCONNECT, pSession->getResetOnDisconnect(), url);
    showRow(b, REFRESH_ON_LOGON, pSession->getRefreshOnLogon(), url);
    showRow(b, MILLISECONDS_IN_TIMESTAMP, pSession->getMillisecondsInTimeStamp(), url);
    showRow(b, PERSIST_MESSAGES, pSession->getPersistMessages(), url);
    showRow(b, SEND_NEXT_EXPECTED_MSG_SEQ_NUM, pSession->getSendNextExpectedMsgSeqNum(), url);
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::processResetSession(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;
    std::string beginString = request.getParameter("BeginString");
    std::string senderCompID = request.getParameter("SenderCompID");
    std::string targetCompID = request.getParameter("TargetCompID");
    std::string sessionQualifier;
    if (copy.hasParameter("SessionQualifier")) {
      sessionQualifier = copy.getParameter("SessionQualifier");
    }

    SessionID sessionID(beginString, senderCompID, targetCompID, sessionQualifier);
    Session *pSession = Session::lookupSession(sessionID);
    if (pSession == 0) {
      throw SessionNotFound();
    }

    std::string sessionUrl = "/session" + request.getParameterString();

    bool confirm = false;
    if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
      confirm = true;
      pSession->reset();
      copy.removeParameter("confirm");
    }

    if (confirm) {
      h << "<META http-equiv='refresh' content=2;URL='" << "/session" << copy.getParameterString() << "'>";
      CENTER center(b);
      center.text();
      H2 h2(b);
      h2.text();
      {
        A a(b);
        a.href("/session" + copy.getParameterString()).text(sessionID.toString());
      }
      b << " has been reset";
    } else {
      {
        CENTER center(b);
        center.text();
        H2 h2(b);
        h2.text();
        b << "Are you sure you want to reset session ";
        {
          A a(b);
          a.href(sessionUrl + request.getParameterString()).text(sessionID.toString());
        }
        b << "?";
      }
      {
        CENTER center(b);
        center.text();
        b << "[";
        {
          A a(b);
          a.href(request.toString() + "&confirm=1").text("YES, reset session");
        }
        b << "]" << NBSP << "[";
        {
          A a(b);
          a.href(sessionUrl).text("NO, do not reset session");
        }
        b << "]";
      }
    }
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::processRefreshSession(const HttpMessage &request, std::stringstream &h, std::stringstream &b) {
  try {
    HttpMessage copy = request;
    std::string beginString = request.getParameter("BeginString");
    std::string senderCompID = request.getParameter("SenderCompID");
    std::string targetCompID = request.getParameter("TargetCompID");
    std::string sessionQualifier;
    if (copy.hasParameter("SessionQualifier")) {
      sessionQualifier = copy.getParameter("SessionQualifier");
    }

    SessionID sessionID(beginString, senderCompID, targetCompID, sessionQualifier);
    Session *pSession = Session::lookupSession(sessionID);
    if (pSession == 0) {
      throw SessionNotFound();
    }

    std::string sessionUrl = "/session" + request.getParameterString();

    bool confirm = false;
    if (copy.hasParameter("confirm") && copy.getParameter("confirm") != "0") {
      confirm = true;
      pSession->refresh();
      copy.removeParameter("confirm");
    }

    if (confirm) {
      h << "<META http-equiv='refresh' content=2;URL='" << "/session" << copy.getParameterString() << "'>";
      CENTER center(b);
      center.text();
      H2 h2(b);
      h2.text();
      {
        A a(b);
        a.href("/session" + copy.getParameterString()).text(sessionID.toString());
      }
      b << " has been refreshed";
    } else {
      {
        CENTER center(b);
        center.text();
        H2 h2(b);
        h2.text();
        b << "Are you sure you want to refresh session ";
        {
          A a(b);
          a.href(sessionUrl + request.getParameterString()).text(sessionID.toString());
        }
        b << "?";
      }
      {
        CENTER center(b);
        center.text();
        b << "[";
        {
          A a(b);
          a.href(request.toString() + "&confirm=1").text("YES, refresh session");
        }
        b << "]" << NBSP << "[";
        {
          A a(b);
          a.href(sessionUrl).text("NO, do not refresh session");
        }
        b << "]";
      }
    }
  } catch (std::exception &e) {
    b << e.what();
  }
}

void HttpConnection::showRow(std::stringstream &s, const std::string &name, bool value, const std::string &url) {
  {
    TR tr(s);
    tr.text();
    {
      TD td(s);
      td.text(name);
    }
    {
      TD td(s);
      td.text(value ? "yes" : "no");
    }
    {
      TD td(s);
      td.text();
      CENTER center(s);
      center.text();
      if (url.size()) {
        std::stringstream href;
        href << url << "&" << name << "=" << !value;
        A a(s);
        a.href(href.str()).text("toggle");
      }
    }
  }
}

void HttpConnection::showRow(
    std::stringstream &s,
    const std::string &name,
    const std::string &value,
    const std::string &url) {
  {
    TR tr(s);
    tr.text();
    {
      TD td(s);
      td.text(name);
    }
    {
      TD td(s);
      td.text(value);
    }
    {
      TD td(s);
      td.text();
      CENTER center(s);
      center.text();
    }
  }
}

void HttpConnection::showRow(std::stringstream &s, const std::string &name, int value, const std::string &url) {
  {
    TR tr(s);
    tr.text();
    {
      TD td(s);
      td.text(name);
    }
    {
      TD td(s);
      td.text(value);
    }
    {
      TD td(s);
      td.text();
      CENTER center(s);
      center.text();
      {
        std::stringstream href;
        href << url << "&" << name << "=" << value - 10;
        A a(s);
        a.href(href.str()).text("<<");
      }
      s << NBSP;
      {
        std::stringstream href;
        href << url << "&" << name << "=" << value - 1;
        A a(s);
        a.href(href.str()).text("<");
      }
      s << NBSP << "|" << NBSP;
      {
        std::stringstream href;
        href << url << "&" << name << "=" << value + 1;
        A a(s);
        a.href(href.str()).text(">");
      }
      s << NBSP;
      {
        std::stringstream href;
        href << url << "&" << name << "=" << value + 10;
        A a(s);
        a.href(href.str()).text(">>");
      }
    }
  }
}

} // namespace FIX
