/* -*- 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 <SmithView.h>
#include <DrawingFrame.h>
#include <GraphUtils.h>

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

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

#define sqr(x) x*x

using namespace std;

//----------------------------------------------------------------------------
SmithView::SmithView( const char* name, WFlags f )
    : GraphView( GraphView::smithView, name, f )
{
  setCursorLabel("Cursor %d:\n\tR=%3.3f\n\tX=%3.3f");
}

//----------------------------------------------------------------------------
SmithView::~SmithView()
{}

//----------------------------------------------------------------------------
bool SmithView::isInsideView(const QPoint& p)
{
  int s = sqr(p.x() - xOrigin_) + sqr(p.y() - yOrigin_);
  return (s <= sqr(radius_));
}

//----------------------------------------------------------------------------
void SmithView::setDefaults()
{
  GraphView::setDefaults();
  setOrigin(TComplex(0.0,0.0));
}

//----------------------------------------------------------------------------
void SmithView::setOrigin(TComplex value)
{
  reOrigin_ = value.real();
  imOrigin_ = value.imag();
}

//----------------------------------------------------------------------------
void SmithView::rCircle(QPainter *p, double r )
{
  int xc = xOrigin_ + (int) floor(radius_ * (r/(1+r)));
  int rd = (int) floor(radius_ / (1+r));
  p->drawEllipse(xc - rd, yOrigin_ - rd,
                 2 * rd, 2 * rd);
}

//----------------------------------------------------------------------------
void SmithView::xCircle(QPainter *p, double x)
{
  int rd = (int)floor(radius_/x);
  int yc = xOrigin_+radius_-rd;
  int rd2 = rd*2;
  p->drawEllipse(yc, yOrigin_    , rd2, rd2);
  p->drawEllipse(yc, yOrigin_-rd2, rd2, rd2);
}

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

  x = xOrigin_ + (int) floor(radius_ * xValue);
  y = yOrigin_ - (int) floor(radius_ * yValue);

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

//----------------------------------------------------------------------------
TComplex SmithView::fromClient( int x, int y )
{
  TReal xValue, yValue;
  xValue = (TReal) (x - xOrigin_) / radius_;
  yValue = (TReal) (yOrigin_ - y) / radius_;
  TComplex value(xValue, yValue);
  return value;
}

//----------------------------------------------------------------------------
TComplex SmithView::transformCoordinates( TComplex coord )
{
  TReal x = real(coord);
  TReal y = imag(coord);
  TReal z = x*x + y*y;
  TReal a = z-2*x+1;
  TReal re = (1-z)/a;
  TReal im = (2*y)/a;
  TComplex value(re, im);
  return value;
}

//----------------------------------------------------------------------------
void SmithView::drawCursors(QPainter* p)
{
  GraphView::drawCursors(p, cursorLegendRect_);
}

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

//----------------------------------------------------------------------------
void SmithView::draw( QPainter *p )
{
  //Get the window size
  QRect rect = p->window();

  computeScaling(p);

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

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

  //Draw title
  p->setFont( titleFont );
  p->drawText(gap_, gap_, width_, height_,
              AlignLeft | AlignTop,
              getTitle() );

  //Legend settings
  int legendPos = p->fontMetrics().lineSpacing() + 2*gap_;
  p->setFont( labelFont );
  int lineSpacing = p->fontMetrics().lineSpacing();
  int legendGap = (int) floor(10*scale_);

  //Draw frame
  p->setPen( lightGray );

  //Define clipping regions
  QRegion clipRegion1(xOrigin_-radius_-1, yOrigin_-radius_-1,
                      radius_*2+2, radius_*2+2, QRegion::Ellipse);
  QRegion clipRegion2(rect, QRegion::Rectangle);


  p->setClipRegion(clipRegion1);

  drawGrid(p);

  int rowNo = 0;
  int colorCount = 0;
  int markerSize = (int) floor(3*scale_);

  p->setClipRegion(clipRegion2);

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

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

      // Plot data
      bool firstPoint = true;

      int markerSpace = vector.getSize() / 10;

      int x = gap_;
      int y = legendPos;

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

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

      legendPos += lineSpacing;

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

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

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

          if (firstPoint)
            {
              p->moveTo(pos);
              firstPoint = false;
            }
          else
            p->lineTo(pos);

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

        } //for

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

      colorCount++;
      rowNo++;

    } //while  //Plot each series

  int x = gap_;
  int y = legendPos;
  int w = xOrigin_ - radius_ - x;
  int h = height_ - legendPos;
  cursorLegendRect_.setRect(x, y, w, h);
}

//----------------------------------------------------------------------------
void SmithView::computeScaling(QPainter* p)
{
  //Get the window size
  QRect rect = p->window();

  //Calculate boundaries
  width_ = rect.width();
  height_ = rect.height();
  gap_ = (int)(0.05*height_);
  if (width_ > height_)
    {
      radius_ = (height_ - 2*gap_)/2;
    }
  else
    {
      radius_ = (width_ - 2*gap_)/2;
    }
  xOrigin_ = width_ - radius_ - 10;
  yOrigin_ = height_/2;
  scale_ = radius_ / 130.0;
}

//----------------------------------------------------------------------------
void SmithView::drawGrid(QPainter* p)
{
  p->moveTo(xOrigin_ - radius_, yOrigin_);
  p->lineTo(xOrigin_ + radius_, yOrigin_);
  rCircle(p, 0.0);
  rCircle(p, 0.5);
  rCircle(p, 1.0);
  rCircle(p, 2.0);
  xCircle(p, 0.5);
  xCircle(p, 1.0);
  xCircle(p, 2.0);
}
