/******************************************************
 * File: visualizer.cpp
 * Author: Keith Schwarz (htiek@cs.stanford.edu)
 *
 * Implementation of the Perceptron visualizer.  This code
 * generates an HTML document colored according to the relative
 * weights used by the perceptron network.  Brighter colors
 * indicate positive weight, negative colors negative weight.
 */

#include "visualizer.h"
#include <fstream>
#include <sstream>
#include <cassert>
#include <iomanip>
#include <algorithm>
using namespace std;

/** Module Constants **/
const size_t kImageDimension = 28; // Size of the image on one axis 
const size_t kImageSize = kImageDimension * kImageDimension; // Total image size

/* Function to convert an integer to a string.  This is based on the implementation
 * from earlier lecture code.
 */
static string IntegerToString(int value)
{
  stringstream converter;
  converter << value;
  return converter.str();
}

/* Given a value and the minimum and maximum values used by the perceptron,
 * returns a hex triplet (i.e. #ffffff) containing a color for that value.
 * This works by normalizing the value to the range 0 - 1 and then scaling
 * that to a value between 0 - 255 and taking the hex encoding.
 */
static string GetColor(double value, double min, double max)
{
  /* Normalize to a value between 0 and 1 */
  value -= min;
  value /= (max - min);

  /* Scale to 0 - 255 */
  int scaled = 255 * value;

  /* Use a stringstream to format the data properly. */
  stringstream result;
  result << setfill('0') << hex // Write in hex with 0 padding.
	 << '#'
	 << setw(2) << scaled   // Use two characters and print out
	 << setw(2) << scaled
	 << setw(2) << scaled;
  return result.str();
}

/* Generates the HTML document of the visualization.  The HTML document will
 * contain a table where each cell of the table is colored based on the
 * perceptron's weights.
 */
void SaveVisualization(vector<double>& classifier, int index)
{
  /* Ensure we have enough data (one element per pixel, plus one for the
   * constant term.
   */
  assert (classifier.size() == kImageSize + 1);

  /* Our filename is [index].html; i.e. 0.html, 1.html, etc. */
  string filename = IntegerToString(index) + ".html";
  ofstream output((IntegerToString(index) + ".html").c_str());

  /* Get the maximum and minimum values for normalization.  This uses the
   * min_element and max_element algorithms, which we'll cover in a few
   * weeks.
   */
  const double minimum = *min_element(classifier.begin(), classifier.end());
  const double maximum = *max_element(classifier.begin(), classifier.end());

  /* Start generating the HTML. */
  output << "<html>\n\t<head>\n\t\t<title> Perceptron for " << index << " </title>\n\t</head>" << endl;
  output << "\t<body>\n\t\t<table style=\"border: 0\" cellpadding=\"0\" cellspacing = \"0\">" << endl;

  /* Iterate across the rows and columns of the perceptron rending our findings. */
  for (size_t y = 0; y < kImageDimension; ++y) {
    output << "\t\t\t<tr>" << endl;

    for (size_t x = 0; x < kImageDimension; ++x) {
      /* Print a comment containing the actual value. */
      output << "<!-- " << classifier[y * kImageDimension + x] << " -->" << endl;

      /* Print out the actual cell. */
      output << "\t\t\t\t<td style=\"background-color: " 
	     << GetColor(classifier[y * kImageDimension + x], minimum, maximum)
	     << "; width: 10px; height: 10px;\"></td>" << endl;
    }
    output << "\t\t\t</tr>" << endl;
  }

  /* Close the table. */
  output << "\t\t</table>" << endl;

  /* Add a link to the next image. */
  output << "\t\t<a href=\"" << IntegerToString((index + 1) % 10) << ".html\">Next</a>" << endl;

  /* Finish the document */
  output << "\t</body>\n\t</html>" << endl;
}
