#include "SliceWindowDecorationRenderer.h"
#include "GenericSliceModel.h"
#include "SNAPAppearanceSettings.h"
#include "GlobalUIModel.h"
#include "GenericSliceRenderer.h"
#include "IRISException.h"
#include "IRISApplication.h"
#include "DisplayLayoutModel.h"

SliceWindowDecorationRenderer::SliceWindowDecorationRenderer()
{
}

SliceWindowDecorationRenderer::~SliceWindowDecorationRenderer()
{

}

void SliceWindowDecorationRenderer::paintGL()
{
  DrawOrientationLabels();
  DrawRulers();
  DrawNicknames();

}

void SliceWindowDecorationRenderer::DrawOrientationLabels()
{
  GenericSliceModel *parentModel = this->GetParentRenderer()->GetModel();
  SNAPAppearanceSettings *as =
      parentModel->GetParentUI()->GetAppearanceSettings();

  // The letter labels
  static const char *letters[3][2] = {{"R","L"},{"A","P"},{"I","S"}};
  const char *labels[2][2];

  // Get the properties for the labels
  const OpenGLAppearanceElement *elt =
      as->GetUIElement(SNAPAppearanceSettings::MARKERS);

  // Leave if the labels are disabled
  if(!elt->GetVisible()) return;

  // Repeat for X and Y directions
  for(unsigned int i=0;i<2;i++)
    {
    // Which axis are we on in anatomy space?
    unsigned int anatomyAxis =
        parentModel->GetDisplayToAnatomyTransform().GetCoordinateIndexZeroBased(i);

    // Which direction is the axis facing (returns -1 or 1)
    unsigned int anatomyAxisDirection =
        parentModel->GetDisplayToAnatomyTransform().GetCoordinateOrientation(i);

    // Map the direction onto 0 or 1
    unsigned int letterIndex = (1 + anatomyAxisDirection) >> 1;

    // Compute the two labels for this axis
    labels[i][0] = letters[anatomyAxis][1-letterIndex];
    labels[i][1] = letters[anatomyAxis][letterIndex];
    }

  double vppr = parentModel->GetSizeReporter()->GetViewportPixelRatio();
  Vector2ui vp = parentModel->GetSizeReporter()->GetLogicalViewportSize();

  glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
  glPushMatrix();
  glLoadIdentity();
  glScaled(vppr, vppr, 1.0);

  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

  // Get the various sizes and offsets
  int offset = 4 + elt->GetFontSize();
  int margin = elt->GetFontSize() / 3;
  int w = vp[0], h = vp[1];

  // Create the font info
  AbstractRendererPlatformSupport::FontInfo font_info =
        { AbstractRendererPlatformSupport::TYPEWRITER, elt->GetFontSize() * vppr, true };

  // Use the delegate to draw text
  this->m_PlatformSupport->RenderTextInOpenGL(
        labels[0][0], margin, (h-offset)/2, offset, offset, font_info, -1, 0, elt->GetNormalColor());

  this->m_PlatformSupport->RenderTextInOpenGL(
        labels[0][1], w - (offset+margin), (h-offset)/2, offset, offset, font_info, 1, 0, elt->GetNormalColor());

  this->m_PlatformSupport->RenderTextInOpenGL(
        labels[1][0], (w-offset)/2, 0, offset, offset, font_info, 0, -1, elt->GetNormalColor());

  this->m_PlatformSupport->RenderTextInOpenGL(
        labels[1][1], (w-offset)/2, h - (offset+1), offset, offset, font_info, 0, 1, elt->GetNormalColor());

  glPopMatrix();
  glPopAttrib();
}

void SliceWindowDecorationRenderer::DrawNicknames()
{
  // Draw the nicknames
  GenericSliceModel *parentModel = this->GetParentRenderer()->GetModel();
  DisplayLayoutModel *dlm = parentModel->GetParentUI()->GetDisplayLayoutModel();
  Vector2ui layout = dlm->GetSliceViewLayerTilingModel()->GetValue();
  int nrows = (int) layout[0];
  int ncols = (int) layout[1];

  if(nrows * ncols == 1)
    return;

  // Get the properties for the labels
  SNAPAppearanceSettings *as =
      parentModel->GetParentUI()->GetAppearanceSettings();

  const OpenGLAppearanceElement *elt =
      as->GetUIElement(SNAPAppearanceSettings::RULER);

  // Leave if the labels are disabled
  if(!elt->GetVisible()) return;

  // Viewport properties (retina-related)
  double vppr = parentModel->GetSizeReporter()->GetViewportPixelRatio();
  Vector2ui vp = parentModel->GetSizeReporter()->GetLogicalViewportSize();

  // Apply the label properties
  glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
  glPushMatrix();
  glLoadIdentity();
  glScaled(vppr, vppr, 1.0);

  elt->ApplyLineSettings();
  glColor4d( elt->GetNormalColor()[0], elt->GetNormalColor()[1], elt->GetNormalColor()[2], 1.0 );

  // Get the viewport size
  int w = vp[0] / ncols, h = vp[1] / nrows;

  AbstractRendererPlatformSupport::FontInfo font_info =
        { AbstractRendererPlatformSupport::SANS, elt->GetFontSize() * vppr, false };

  // Find the longest nickname
  int maxwidth = 0;
  for(int i = 0; i < nrows; i++)
    {
    for(int j = 0; j < ncols; j++)
      {
      // Define the ROI for this label
      ImageWrapperBase *layer =
          this->GetParentRenderer()->GetLayerForNthTile(i, j);

      if(layer)
        {
        int fw = this->m_PlatformSupport->MeasureTextWidth(
              layer->GetNickname().c_str(), font_info);
        if(fw > maxwidth)
          maxwidth = fw;
        }
      }
    }

  // Adjust the font size
  if(maxwidth > 0.92 * w)
    {
    font_info.pixel_size = (int) (font_info.pixel_size * w * 0.92 / maxwidth);
    }

  // Draw each nickname
  for(int i = 0; i < nrows; i++)
    {
    for(int j = 0; j < ncols; j++)
      {
      // Define the ROI for this label
      ImageWrapperBase *layer =
          this->GetParentRenderer()->GetLayerForNthTile(i, j);
      if(layer)
        {
        this->m_PlatformSupport->RenderTextInOpenGL(
              layer->GetNickname().c_str(),
              w * j, h * (nrows - i) - 20, w, 15, font_info,
              AbstractRendererPlatformSupport::HCENTER,
              AbstractRendererPlatformSupport::TOP,
              elt->GetNormalColor());
        }
      }
    }


  glPopMatrix();
  glPopAttrib();
}

void SliceWindowDecorationRenderer::DrawRulers()
{
  GenericSliceModel *parentModel = this->GetParentRenderer()->GetModel();
  SNAPAppearanceSettings *as =
      parentModel->GetParentUI()->GetAppearanceSettings();

  // Get the properties for the labels
  const OpenGLAppearanceElement *elt =
      as->GetUIElement(SNAPAppearanceSettings::RULER);

  // Leave if the labels are disabled
  if(!elt->GetVisible()) return;

  // Get the viewport properties (retina-capable)
  float vppr = parentModel->GetSizeReporter()->GetViewportPixelRatio();
  Vector2ui vp = parentModel->GetSizeReporter()->GetLogicalViewportSize();

  glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
  glPushMatrix();
  glLoadIdentity();
  glScaled(vppr, vppr, 1.0);

  elt->ApplyLineSettings();
  glColor4d( elt->GetNormalColor()[0], elt->GetNormalColor()[1], elt->GetNormalColor()[2], 1.0 );

  // The ruler bar should be as large as possible but less than one half
  // of the screen width (not to go over the markers)
  double maxw = 0.5 * vp[0] - 20.0;
  maxw = maxw < 5 ? 5 : maxw;

  double zoom = parentModel->GetViewZoom();
  double scale = 1.0;
  while(zoom * scale > maxw) scale /= 10.0;
  while(zoom * scale < 0.1 * maxw) scale *= 10.0;

  // Draw a zoom bar
  double bw = scale * zoom;
  glBegin(GL_LINES);
  glVertex2d(vp[0] - 5,5);
  glVertex2d(vp[0] - 5,15);
  glVertex2d(vp[0] - 5,10);
  glVertex2d(vp[0] - (5 + bw),10);
  glVertex2d(vp[0] - (5 + bw),5);
  glVertex2d(vp[0] - (5 + bw),15);
  glEnd();

  // Based on the log of the scale, determine the unit
  string unit = "mm";
  if(scale >= 10 && scale < 1000)
    { unit = "cm"; scale /= 10; }
  else if(scale >= 1000)
    { unit = "m"; scale /= 1000; }
  else if(scale >= 1000000)
    { unit = "km"; scale /= 1000000; }
  else if(scale < 1 && scale > 0.001)
    { unit = "\xb5m"; scale *= 1000; }
  else if(scale < 0.001)
    { unit = "nm"; scale *= 1000000; }

  std::ostringstream oss;
  oss << scale << " " << unit;

  // Get the fontsize in GL pixels
  int font_size = elt->GetFontSize();

  // Create the font info
  AbstractRendererPlatformSupport::FontInfo font_info =
        { AbstractRendererPlatformSupport::SANS, font_size * vppr, false };

  // See if we can squeeze the label under the ruler
  if(bw > font_size * 4)
    {
    this->m_PlatformSupport->RenderTextInOpenGL(
        oss.str().c_str(),
        vp[0]-(bw+10), 12, (int) bw, font_size+8,
        font_info, 0, -1, elt->GetNormalColor());
    }
  else
    {
    this->m_PlatformSupport->RenderTextInOpenGL(
          oss.str().c_str(),
          vp[0] - (int) (2 * bw + font_size * 4 + 20), 5,
          (int) (bw + font_size * 4+10), font_size,
          font_info, 1, 0, elt->GetNormalColor());
    }

  glPopMatrix();
  glPopAttrib();
}
