/* -*- C++ -*-
 
  This file is part of ViPEC
  Copyright (C) 1991-2001 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 <CircuitNode.h>

#include <Utils.h>
#include <Setup.h>
#include <Logger.h>
#include <Exception.h>
#include <CircuitLine.h>
#include <MainWindow.h>
#include <Schematic.h>
#include <VectorFont.h>

#include <qcolor.h>
#include <qpainter.h>

#include <iostream>
#include <math.h>

using namespace std;

//-----------------------------------------------------------------
CircuitNode::CircuitNode( const QPoint& pos,
                          bool isComponentNode,
                          bool isPortNode,
                          bool isGndNode,
                          bool isFloatAllowed )
    : pos_( pos ),
    schematic_( 0 ),
    nodeNumber_( -1 ),
    isComponentNode_( isComponentNode ),
    isPortNode_( isPortNode ),
    isGndNode_( isGndNode ),
    isFloatAllowed_( isFloatAllowed ),
    z_(0)
{}

//-----------------------------------------------------------------
CircuitNode::~CircuitNode()
{
  ASSERT(schematic_ != 0);
  while (lineList_.count() > 0)
    {
      CircuitLine* line = lineList_.first();
      schematic_->removeLine( line );
    }
  schematic_->removeNode(this);
}

//-----------------------------------------------------------------
QPoint& CircuitNode::pos()
{
  return pos_;
}

//-----------------------------------------------------------------
bool CircuitNode::isComponentNode()
{
  return isComponentNode_;
}

//-----------------------------------------------------------------
bool CircuitNode::isPortNode()
{
  return isPortNode_;
}

//-----------------------------------------------------------------
bool CircuitNode::isGndNode()
{
  return isGndNode_;
}

//-----------------------------------------------------------------
bool CircuitNode::isFloatingAllowed()
{
  return isFloatAllowed_;
}

//-----------------------------------------------------------------
bool CircuitNode::isFree()
{
  bool used = isComponentNode_ || (lineList_.count() > 0);
  return !used;
}

//-----------------------------------------------------------------
void CircuitNode::makeComponentNode()
{
  isComponentNode_ = TRUE;
}

//-----------------------------------------------------------------
void CircuitNode::makeFloatingNode()
{
  ASSERT( isComponentNode_ );
  isComponentNode_ = FALSE;
}

//-----------------------------------------------------------------
void CircuitNode::setCircuit(Schematic* schematic)
{
  schematic_ = schematic;
}

//-----------------------------------------------------------------
void CircuitNode::setImpedance( TReal z )
{
  ASSERT( isPortNode_ );
  z_ = z;
}

//-----------------------------------------------------------------
TReal CircuitNode::getImpedance() const
  {
    ASSERT( isPortNode_ );
    return z_;
  }

//-----------------------------------------------------------------
void CircuitNode::setNodeNumber(int nr)
{
  nodeNumber_ = nr;
}

//-----------------------------------------------------------------
int CircuitNode::getNodeNumber()
{
  return nodeNumber_;
}

//-----------------------------------------------------------------
void CircuitNode::addLine(CircuitLine* line)
{
  lineList_.append(line);
}

//-----------------------------------------------------------------
void CircuitNode::removeLine(CircuitLine* line)
{
  ASSERT ( lineList_.remove(line) );
}

//-----------------------------------------------------------------
void CircuitNode::replaceWith(CircuitNode* node)
{
  CircuitLine* line = 0;
  bool replacedSomeNodes = FALSE;
  for ( line=lineList_.first(); line != 0; line=lineList_.next() )
    {
      bool replaced = line->replaceNode(this, node);
      replacedSomeNodes = replacedSomeNodes || replaced;
      if ( replaced )
        {
          node->addLine( line );
        }
    }
  ASSERT( replacedSomeNodes );
}

//-----------------------------------------------------------------
void CircuitNode::draw(QPainter* p)
{
  bool debugMode = Setup::instance()->isDebugMode();

  //Draw node number in debug mode
  if ( (!isPortNode()) && debugMode )
    {
      QPoint point = pos();
      point += QPoint(3,-3);
      QCString nr;
      nr.setNum(nodeNumber_);
      VectorFont::instance()->drawText(nr, p, point);
    }

  QColor color = Qt::black;
  int size = Component::getNodeSize();
  size = (int) floor(size * 0.8);
  int nrLines = lineList_.count();
  bool drawNode = FALSE;

  if ( (nrLines == 0) && (!isFloatingAllowed()) )
    {
      color = Qt::red;
      drawNode = TRUE;
    }

  if ( (!isComponentNode()) && (nrLines==1) )
    {
      color = Qt::red;
      drawNode = TRUE;
    }

  if ( (!isComponentNode()) && (nrLines>2) )
    {
      color = Qt::black;
      drawNode = TRUE;
    }

  if ( (!drawNode) && debugMode )
    {
      color = Qt::black;
      drawNode = TRUE;
    }

  if ( drawNode )
    {
      p->save();
      p->setPen( QPen(color, 1) );
      p->setBrush( QBrush(color) );
      int x = pos().x() - size;
      int y = pos().y() - size;
      p->drawEllipse( x, y, 2*size, 2*size );
      p->restore();
    }
}

//-----------------------------------------------------------------
void CircuitNode::buildLineList(QList<CircuitLine>& lineList)
{
  int nrLines = lineList_.count();
  if ( nrLines == 0 )
    {
      if ( isFloatAllowed_ && isComponentNode_ )
        {
          return;
        }
      Logger::error("CircuitNode::buildLineList - throwing ExceptionFloatingNode() exception");
      throw Exception::FloatingNode();
    }
  CircuitLine* line = 0;
  for ( line=lineList_.first(); line!=0; line=lineList_.next() )
    {
      if ( lineList.find( line ) < 0 ) //Not in list
        {
          lineList.append( line );
          CircuitNode* otherNode = 0;
          otherNode = ( this == line->start() ) ? line->stop() : line->start();
          otherNode->buildLineList(lineList);
        }
    }
}
