/* -*- C++ -*-
 
  This file is part of ViPEC
  Copyright (C) 1991-2000 Johan Rossouw (jrossouw@alcatel.altech.co.za)
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library General Public License as
  published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU Library General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
*/

#include <GridView.h>
#include <DrawingFrame.h>
#include <GraphUtils.h>
#include <Logger.h>

#include <qpopupmenu.h>
#include <qmenubar.h>
#include <qstatusbar.h>

#include <stdio.h>
#include <iostream>

using namespace std;

//----------------------------------------------------------------------------
GridView::GridView( const char* name, WFlags f )
    : GraphView( GraphView::gridView, name, f )
{
  //Nothing
}

//----------------------------------------------------------------------------
GridView::~GridView()
{
  //Nothing
}

//----------------------------------------------------------------------------
void GridView::setDefaults()
{
  GraphView::setDefaults();
  linXAxis_ = false;
  xAxisMax_ = 10;
  xAxisMin_ = 0.1;
  yAxisMax_ = 10;
  yAxisMin_ = -10;
  numberXSteps_ = 10;
  numberYSteps_ = 5;
  xAxisTitle_ = "";
}

//----------------------------------------------------------------------------
void GridView::setXAxisTitle(const char* value)
{
  xAxisTitle_ = value;
}

//----------------------------------------------------------------------------
const QString& GridView::getXAxisTitle() const
  {
    return xAxisTitle_;
  }

//----------------------------------------------------------------------------
void GridView::setLinXAxis(bool value)
{
  linXAxis_ = value;
}

//----------------------------------------------------------------------------
bool GridView::getLinXAxis() const
  {
    return linXAxis_;
  }

//----------------------------------------------------------------------------
void GridView::setXAxisMin(TReal value)
{
  xAxisMin_ = value;
}

//----------------------------------------------------------------------------
TReal GridView::getXAxisMin() const
  {
    return xAxisMin_;
  }

//----------------------------------------------------------------------------
void GridView::setXAxisMax(TReal value)
{
  xAxisMax_ = value;
}

//----------------------------------------------------------------------------
TReal GridView::getXAxisMax() const
  {
    return xAxisMax_;
  }

//----------------------------------------------------------------------------
void GridView::setNoXSteps(uint value)
{
  numberXSteps_ = value;
}

//----------------------------------------------------------------------------
uint GridView::getNoXSteps() const
  {
    return numberXSteps_;
  }

//----------------------------------------------------------------------------
void GridView::setYAxisMin(TReal value)
{
  yAxisMin_ = value;
}

//----------------------------------------------------------------------------
TReal GridView::getYAxisMin() const
  {
    return yAxisMin_;
  }

//----------------------------------------------------------------------------
void GridView::setYAxisMax(TReal value)
{
  yAxisMax_ = value;
}

//----------------------------------------------------------------------------
TReal GridView::getYAxisMax() const
  {
    return yAxisMax_;
  }

//----------------------------------------------------------------------------
void GridView::setNoYSteps(uint value)
{
  numberYSteps_ = value;
}

//----------------------------------------------------------------------------
uint GridView::getNoYSteps() const
  {
    return numberYSteps_;
  }

//----------------------------------------------------------------------------
void GridView::computeScaling( QPainter *p )
{
  QRect rect = p->window();
  viewWidth_ = rect.width();
  viewHeight_ = rect.height();
  leftGap_ = (int)(0.10 * viewWidth_);
  rightGap_ = (int)(0.20 * viewWidth_);
  topGap_ = (int)(0.10 * viewHeight_);
  bottomGap_ = (int)(0.06 * viewHeight_);

  scale_ = std::sqrt((float)viewHeight_*viewHeight_ + viewWidth_*viewWidth_) / 600.0;

  //Calculate legend width
  QFont labelFont = getLabelFont(p->device());
  QFontMetrics metrics( labelFont );
  SeriesMap::Iterator it;
  int maxWidth = 0;
  for( it = seriesMap_.begin(); it != seriesMap_.end(); ++it )
    {
      const QString& legend = it.key();
      int width = metrics.width(legend);
      if (width>maxWidth)
        {
          maxWidth = width;
        }
    }
  rightGap_ = (int) floor(maxWidth+30*scale_);

  graphWidth_ = viewWidth_ - leftGap_ - rightGap_;
  graphHeight_ = viewHeight_ - topGap_ - bottomGap_;

  // Set scaling
  yScale_ = TReal(graphHeight_ - 1) / (yAxisMax_ - yAxisMin_);
  if (linXAxis_)
    {
      xScale_ = TReal(graphWidth_ - 1) / (xAxisMax_ - xAxisMin_);
    }
  else
    {
      xScale_ = TReal(graphWidth_ - 1) / (log10(xAxisMax_) - log10(xAxisMin_));
    }

}

//----------------------------------------------------------------------------
QRect GridView::graphRect()
{
  QRect rect(leftGap_, topGap_, graphWidth_, graphHeight_);
  return rect;
}

//----------------------------------------------------------------------------
bool GridView::isInsideView(const QPoint& p)
{
  return graphRect().contains(p);
}

//----------------------------------------------------------------------------
QPoint GridView::toClient( TReal xValue, TReal yValue )
{
  int x, y;

  if (linXAxis_)
    {
      x = (int)floor((xValue - xAxisMin_)*xScale_);
    }
  else
    {
      x = (int)floor((log10(xValue) - log10(xAxisMin_))*xScale_);
    }

  y = (int)floor((yAxisMax_ - yValue) * yScale_);

  x += leftGap_;
  y += topGap_;

  QPoint clientPos(x,y);
  return clientPos;
}

//----------------------------------------------------------------------------
TComplex GridView::fromClient( int x, int y )
{
  TReal xValue, yValue;
  x -= leftGap_;
  y -= topGap_;
  // Compute X and Y values
  yValue = yAxisMax_ - ((TReal) y / yScale_) ;

  if (linXAxis_)
    {
      xValue = (TReal) x / xScale_ + xAxisMin_;
    }
  else
    {
      TReal n = (TReal) x / xScale_ + log10(xAxisMin_);
      xValue = pow(10,n);
    }
  TComplex value(xValue, yValue);
  return value;
}

TComplex GridView::transformCoordinates( TComplex coord )
{
  return coord;
}

void GridView::drawCursors(QPainter* p)
{
  GraphView::drawCursors(p, cursorLegendRect_);
}

void GridView::print(QPainter *p)
{
  draw(p);
  GraphView::drawCursors(p, cursorLegendRect_);
  frame_->reDraw();
}

//----------------------------------------------------------------------------
//Draws the graph on the paint device
//----------------------------------------------------------------------------
void GridView::draw(QPainter* p)
{
  computeScaling(p);

  QFont titleFont = getTitleFont(p->device());
  QFont labelFont = getLabelFont(p->device());

  p->setPen( black );                            // black pen outline

  //Draw frame
  p->drawRect( leftGap_, topGap_, graphWidth_, graphHeight_ );

  //Draw title
  p->setFont( titleFont );
  p->drawText(leftGap_, 0, graphWidth_, topGap_,
              AlignCenter,
              getTitle());

  p->setFont( labelFont );
  drawLabels(p);

  drawGrid(p);

  p->setPen( SolidLine );

  // Prepare to plot data
  int rowNo = 0;
  int colorCount = 0;
  int legendPos = topGap_;
  int markerSize = (int) floor(3*scale_);
  int legendGap = (int) floor(10*scale_);

  // Setup legend font
  p->setFont( labelFont );
  int lineSpacing = p->fontMetrics().lineSpacing();

  //Plot each series
  SeriesMap::Iterator it;
  for( it = seriesMap_.begin(); it != seriesMap_.end(); ++it )
    {
      DataVector& vector = it.data();

      int markerSpace = (int)floor((double)vector.getSize() / 10);

      // Update pen color
      p->setPen( GraphUtils::getColor(colorCount) );

      int x = leftGap_ + graphWidth_ + legendGap;
      int y = legendPos;

      if (showMarkers())
        {
          GraphUtils::drawMarker(p, colorCount, x, y+(lineSpacing/2), markerSize);
        }

      p->drawText(x+legendGap, y, viewWidth_, y+lineSpacing,
                  AlignLeft | AlignTop, it.key());

      legendPos += lineSpacing;

      // Plot data
      bool firstPoint = true;

      p->setClipRect(leftGap_, topGap_, graphWidth_, graphHeight_);

      for (uint index=0; index<vector.getSize(); index++)
        {
#ifndef HP_CC
          TReal xValue = vector(index).real();
          TReal yValue = vector(index).imag();
#else

          TReal xValue = real(vector(index));
          TReal yValue = imag(vector(index));
#endif

          // Compute X and Y offsets
          QPoint pos = toClient(xValue, yValue);
          x = pos.x();
          y = pos.y();

          p->setClipRect(leftGap_, topGap_, graphWidth_, graphHeight_);

          if ( (x >= leftGap_) && (x <= (leftGap_+graphWidth_)) )
            {
              if (firstPoint)
                {
                  p->moveTo(x,y);
                  firstPoint = false;
                }
              else
                p->lineTo(x,y);
            }

          p->setClipRect(0,0,viewWidth_, viewHeight_);

          // Draw markers
          if ( ((index % markerSpace) == 0) && showMarkers() )
            {
              GraphUtils::drawMarker(p, colorCount, x, y, markerSize);
            }

        } //for

      if ( showMarkers() )
        {
          GraphUtils::drawMarker(p, colorCount, x, y, markerSize);
        }

      colorCount++;
      rowNo++;

    } //while

  int x = leftGap_ + graphWidth_;
  int y = legendPos;
  int w = viewWidth_ - x;
  int h = viewHeight_ - y;
  cursorLegendRect_.setRect(x, y, w, h);
}

//----------------------------------------------------------------------------
void GridView::drawLabels(QPainter* p)
{
  //Draw labels
  QString label;
  label.sprintf("%3.3f", yAxisMax_);
  p->drawText(0, topGap_, leftGap_, graphHeight_,
              AlignRight | AlignTop, label);
  label.sprintf("%3.3f", yAxisMin_);
  p->drawText(0, 0, leftGap_, topGap_+graphHeight_,
              AlignRight | AlignBottom, label);
  label.sprintf("%3.3f", xAxisMin_);
  p->drawText(leftGap_, topGap_+graphHeight_, graphWidth_, viewHeight_,
              AlignLeft | AlignTop, label);
  label.sprintf("%3.3f", xAxisMax_);
  p->drawText(0, topGap_+graphHeight_, leftGap_+graphWidth_, viewHeight_,
              AlignRight | AlignTop, label);
  p->drawText(leftGap_, topGap_+graphHeight_,
              graphWidth_, viewHeight_ - graphHeight_ - topGap_,
              AlignCenter, xAxisTitle_);
}

//----------------------------------------------------------------------------
void GridView::drawGrid(QPainter* p)
{

  p->setPen( DashLine );
  p->setPen( lightGray );

  p->setClipRect(leftGap_, topGap_, graphWidth_, graphHeight_);

  // Y grid
  TReal yScale = TReal(graphHeight_-1) / numberYSteps_;
  for (uint index = 1; index < numberYSteps_; index++)
    {
      p->moveTo(leftGap_, topGap_ + (int)floor(index*yScale));
      p->lineTo(leftGap_ + graphWidth_, topGap_ + (int)floor(index*yScale));
    }
  // Linear X grid
  TReal xScale;
  if (linXAxis_)
    {
      xScale = TReal(graphWidth_-1) / numberXSteps_;
      for (uint index = 1; index < numberXSteps_; index++)
        {
          int x = (int)floor(index*xScale);
          p->moveTo(leftGap_ + x, topGap_);
          p->lineTo(leftGap_ + x, topGap_ + graphHeight_);
        }
    }
  else
    // Log X Axis
    {
      xScale = TReal(graphWidth_-1) / (log10(xAxisMax_)-log10(xAxisMin_));
      int start = (int) (floor(log10(xAxisMin_)));
      int stop = (int) (ceil(log10(xAxisMax_)));
      for (int index = start; index < stop; index++)
        {
          TReal step = pow(10.0,(double)index);
          for (int index2 = 1; index2 < 10; index2++)
            {
              int x = (int)(floor((log10(step*index2)-log10(xAxisMin_))*xScale));
              p->moveTo(leftGap_+x,topGap_);
              p->lineTo(leftGap_+x,topGap_+graphHeight_-1);
            } //for index2
        } //for Index
    } //if
  p->setClipRect(p->window());
}

//----------------------------------------------------------------------------
