/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   copyright (C) 2002-2007                                               *
 *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
 ***************************************************************************/

// own header
#include "linepath.h"

// system includes
#include <cstdlib>
#include <cmath>

// qt includes
#include <tqcanvas.h>
#include <tqdatastream.h>
#include <tqdom.h>

// kde includes
#include <kdebug.h>

// application includes
#include "associationwidget.h"
#include "activitywidget.h"
#include "widget_utils.h"
#include "umlview.h"
#include "umldoc.h"
#include "uml.h"

LinePath::Circle::Circle(TQCanvas * canvas, int radius /* = 0 */)
        : TQCanvasEllipse(radius * 2, radius * 2, canvas) {
}

void LinePath::Circle::setX(int x) {
    TQCanvasItem::setX( (double) x );
}

void LinePath::Circle::setY(int y) {
    TQCanvasItem::setY( (double) y );
}

void LinePath::Circle::setRadius(int radius) {
    TQCanvasEllipse::setSize(radius * 2, radius * 2);
}

int LinePath::Circle::getRadius() const {
    return (TQCanvasEllipse::height() / 2);
}

void LinePath::Circle::drawShape(TQPainter& p) {
    int diameter = height();
    int radius = diameter / 2;
    p.drawEllipse( (int)x() - radius, (int)y() - radius, diameter, diameter);
}

LinePath::LinePath() {
    m_RectList.setAutoDelete( true );
    m_LineList.setAutoDelete( true );
    m_HeadList.setAutoDelete( true );
    m_ParallelList.setAutoDelete( true );
    m_bSelected = false;
    m_pClearPoly = 0;
    m_pCircle = 0;
    m_PointArray.resize( 4 );
    m_ParallelLines.resize( 4 );
    m_pAssociation = 0;
    m_bHeadCreated = false;
    m_bParallelLineCreated = false;
    m_DockRegion = TopBottom;
}

LinePath::~LinePath() {}

void LinePath::setAssociation(AssociationWidget * association ) {
    if( !association )
        return;
    cleanup();
    m_pAssociation = association;
    createHeadLines();
    if( getAssocType() == Uml::at_Coll_Message )
        setupParallelLine();
    UMLView * view =  (UMLView *)m_pAssociation -> parent();
    connect( view, TQ_SIGNAL( sigColorChanged( Uml::IDType ) ), this, TQ_SLOT( slotLineColorChanged( Uml::IDType ) ) );
    connect( view, TQ_SIGNAL( sigLineWidthChanged( Uml::IDType ) ), this, TQ_SLOT( slotLineWidthChanged( Uml::IDType ) ) );
}

TQPoint LinePath::getPoint( int pointIndex ) {
    int count = m_LineList.count();
    if( count == 0 || pointIndex > count  || pointIndex < 0)
        return TQPoint( -1, -1 );

    if( pointIndex == count ) {
        TQCanvasLine * line = m_LineList.last();
        return line -> endPoint();
    }
    TQCanvasLine * line = m_LineList.at( pointIndex );
    return line -> startPoint();
}

bool LinePath::setPoint( int pointIndex, const TQPoint &point ) {
    int count = m_LineList.count();
    if( count == 0 || pointIndex > count  || pointIndex < 0)
        return false;
    if (point.x() == 0 && point.y() == 0) {
        kError() << "LinePath::setPoint:ignoring request for (0,0)" << endl;
        return false;
    }

    if( pointIndex == count) {
        TQCanvasLine * line = m_LineList.last();
        TQPoint p = line -> startPoint();
        line -> setPoints( p.x(), p.y(), point.x(), point.y() );
        moveSelected( pointIndex );
        update();
        return true;
    }
    if( pointIndex == 0 ) {
        TQCanvasLine * line = m_LineList.first();
        TQPoint p = line -> endPoint();
        line -> setPoints( point.x(), point.y(), p.x(), p.y() );
        moveSelected( pointIndex );
        update();
        return true;
    }
    TQCanvasLine * line = m_LineList.at( pointIndex  );
    TQPoint p = line -> endPoint();
    line -> setPoints( point.x(), point.y(), p.x(), p.y() );
    line = m_LineList.at( pointIndex - 1 );
    p = line -> startPoint();
    line -> setPoints( p.x(), p.y(), point.x(), point.y() );
    moveSelected( pointIndex );
    update();
    return true;
}

bool LinePath::isPoint( int pointIndex, const TQPoint &point, unsigned short delta) {
    int count = m_LineList.count();
    if ( pointIndex >= count )
        return false;

    TQCanvasLine * line = m_LineList.at( pointIndex );

    /* check if the given point is the start or end point of the line */
    if ( (
                abs( line -> endPoint().x() - point.x() ) <= delta
                &&
                abs( line -> endPoint().y() - point.y() ) <= delta
            ) || (
                abs( line -> startPoint().x() - point.x() ) <= delta
                &&
                abs( line -> startPoint().y() - point.y() ) <= delta
            ) )
        return true;

    /* check if the given point is the start or end point of the line */
    return false;
}

bool LinePath::insertPoint( int pointIndex, const TQPoint &point ) {
    int count = m_LineList.count();
    if( count == 0 )
        return false;
    const bool bLoading = UMLApp::app()->getDocument()->loading();

    if( count == 1 || pointIndex == 1) {
        TQCanvasLine * first = m_LineList.first();
        TQPoint sp = first -> startPoint();
        TQPoint ep = first -> endPoint();
        first -> setPoints( sp.x(), sp.y(), point.x(), point.y() );
        TQCanvasLine * line = new TQCanvasLine( getCanvas() );
        line -> setZ( -2 );
        line -> setPoints( point.x(), point.y(), ep.x(), ep.y() );
        line -> setPen( getPen() );
        line -> setVisible( true );
        m_LineList.insert( 1, line );
        if (!bLoading)
            setupSelected();
        return true;
    }
    if( count + 1 == pointIndex ) {
        TQCanvasLine * before = m_LineList.last();
        TQPoint sp = before -> startPoint();
        TQPoint ep = before -> endPoint();
        before -> setPoints( sp.x(), sp.y(), point.x(), point.y() );
        TQCanvasLine * line = new TQCanvasLine( getCanvas() );
        line -> setPoints( point.x(), point.y(), ep.x(), ep.y() );
        line -> setZ( -2 );
        line -> setPen( getPen() );
        line -> setVisible( true );
        m_LineList.append( line );
        if (!bLoading)
            setupSelected();
        return true;
    }
    TQCanvasLine * before = m_LineList.at( pointIndex - 1 );
    TQPoint sp = before -> startPoint();
    TQPoint ep = before -> endPoint();
    before -> setPoints( sp.x(), sp.y(), point.x(), point.y() );
    TQCanvasLine * line = new TQCanvasLine(getCanvas() );
    line -> setPoints( point.x(), point.y(), ep.x(), ep.y() );
    line -> setZ( -2 );
    line -> setPen( getPen() );
    line -> setVisible( true );
    m_LineList.insert( pointIndex, line );
    if (!bLoading)
        setupSelected();
    return true;
}

bool LinePath::removePoint( int pointIndex, const TQPoint &point, unsigned short delta )
{
    /* get the number of line segments */
    int count = m_LineList.count();
    if ( pointIndex >= count )
        return false;

    /* we don't know if the user clicked on the start- or endpoint of a
     * line segment */
    TQCanvasLine * current_line = m_LineList.at( pointIndex );
    if (abs( current_line -> endPoint().x() - point.x() ) <= delta
            &&
            abs( current_line -> endPoint().y() - point.y() ) <= delta)
    {
        /* the user clicked on the end point of the line;
         * we have to make sure that this isn't the last line segment */
        if (pointIndex >= count - 1)
            return false;

        /* the next segment will get the starting point from the current one,
         * which is going to be removed */
        TQCanvasLine * next_line = m_LineList.at( pointIndex + 1 );
        TQPoint startPoint = current_line -> startPoint();
        TQPoint endPoint = next_line -> endPoint();
        next_line -> setPoints(startPoint.x(), startPoint.y(),
                               endPoint.x(), endPoint.y());

    } else
        if (abs( current_line -> startPoint().x() - point.x() ) <= delta
                &&
                abs( current_line -> startPoint().y() - point.y() ) <= delta)
        {
            /* the user clicked on the start point of the line;
             * we have to make sure that this isn't the first line segment */
            if (pointIndex < 1)
                return false;

            /* the previous segment will get the end point from the current one,
             * which is going to be removed */
            TQCanvasLine * previous_line = m_LineList.at( pointIndex - 1 );
            TQPoint startPoint = previous_line -> startPoint();
            TQPoint endPoint = current_line -> endPoint();
            previous_line -> setPoints(startPoint.x(), startPoint.y(),
                                       endPoint.x(), endPoint.y());
        } else {
            /* the user clicked neither on the start- nor on the end point of
             * the line; this really shouldn't happen, but just make sure */
            return false;
        }


    /* remove the segment from the list */
    m_LineList.remove( pointIndex );

    return true;
}

bool LinePath::setStartEndPoints( const TQPoint &start, const TQPoint &end ) {
    int count = m_LineList.count();

    if( count == 0 ) {
        TQCanvasLine * line = new TQCanvasLine(getCanvas() );
        line -> setPoints( start.x(), start.y(), end.x(), end.y() );
        line -> setZ( -2 );
        line -> setPen( getPen() );
        line -> setVisible( true );
        m_LineList.append( line );
        return true;
    }
    bool status = setPoint( 0, start );
    if( status)
        return setPoint( count , end );
    return false;
}

int LinePath::count() {
    return m_LineList.count() + 1;
}

int LinePath::onLinePath( const TQPoint &position ) {
    TQCanvasItemList list = getCanvas() -> collisions( position );
    int index = -1;

    TQCanvasItemList::iterator end(list.end());
    for(TQCanvasItemList::iterator item_it(list.begin()); item_it != end; ++item_it ) {
        if( ( index = m_LineList.findRef( (TQCanvasLine*)*item_it ) ) != -1 )
            break;
    }//end for
    return index;
}

void LinePath::setSelected( bool select ) {
    if(select)
        setupSelected();
    else if( m_RectList.count() > 0 )
        m_RectList.clear();
}

void LinePath::setAssocType( Uml::Association_Type type ) {
    LineListIt it( m_LineList );
    TQCanvasLine * line = 0;
    while( ( line = it.current() ) ) {
        line -> setPen( getPen() );
        ++it;
    }
    if( m_pClearPoly ) {
        delete m_pClearPoly;
        m_pClearPoly = 0;
    }
    if( type == Uml::at_Coll_Message )
        setupParallelLine();
    else
        createHeadLines();
    update();
}

void LinePath::update() {
    if (getAssocType() == Uml::at_Coll_Message) {
        if (m_bParallelLineCreated) {
            calculateParallelLine();
            updateParallelLine();
        } else
            setupParallelLine();
    } else if (m_bHeadCreated) {
        calculateHead();
        updateHead();
    } else {
        createHeadLines();
    }
}

void LinePath::slotLineColorChanged( Uml::IDType viewID ) {
    if(m_pAssociation->getUMLView()->getID() != viewID) {
        return;
    }
    setLineColor( m_pAssociation->getUMLView()->getLineColor() );
}


void LinePath::setLineColor( const TQColor &color ) {
    TQCanvasLine * line = 0;
    uint linewidth = 0;
    LineListIt it( m_LineList );
    while( ( line = it.current() ) ) {
        linewidth = line->pen().width();
        line -> setPen( TQPen( color, linewidth ) );
        ++it;
    }
    LineListIt hit( m_HeadList );
    while( ( line = hit.current() ) ) {
        linewidth = line->pen().width();
        line -> setPen( TQPen( color, linewidth ) );
        ++hit;
    }
    LineListIt pit( m_ParallelList );
    while( ( line = pit.current() ) ) {
        linewidth = line->pen().width();
        line -> setPen( TQPen( color, linewidth ) );
        ++pit;
    }

    if( getAssocType() == Uml::at_Aggregation )
        if (m_pClearPoly) m_pClearPoly -> setBrush( TQBrush( TQt::white ) );
        else if( getAssocType() == Uml::at_Composition )
            if (m_pClearPoly) m_pClearPoly -> setBrush( TQBrush( color ) );

    if( m_pCircle ) {
        linewidth = m_pCircle->pen().width();
        m_pCircle->setPen( TQPen(color, linewidth) );
    }
}

void LinePath::slotLineWidthChanged( Uml::IDType viewID ) {
    if(m_pAssociation->getUMLView()->getID() != viewID) {
        return;
    }
    setLineWidth( m_pAssociation->getUMLView()->getLineWidth() );
}

void LinePath::setLineWidth( uint width ) {
    TQCanvasLine * line = 0;
    TQColor linecolor;
    LineListIt it( m_LineList );
    while( ( line = it.current() ) ) {
        linecolor = line->pen().color();
        line -> setPen( TQPen( linecolor, width ) );
        ++it;
    }
    LineListIt hit( m_HeadList );
    while( ( line = hit.current() ) ) {
        linecolor = line->pen().color();
        line -> setPen( TQPen( linecolor, width ) );
        ++hit;
    }
    LineListIt pit( m_ParallelList );
    while( ( line = pit.current() ) ) {
        linecolor = line->pen().color();
        line -> setPen( TQPen( linecolor, width ) );
        ++pit;
    }

    if( m_pCircle ) {
        linecolor = m_pCircle->pen().color();
        m_pCircle->setPen( TQPen(linecolor, width) );
    }
}

void LinePath::moveSelected( int pointIndex ) {
    int lineCount = m_LineList.count();
    if( !m_bSelected ) {
        m_RectList.clear();
        return;
    }
    if( (int)m_RectList.count() + 1 != lineCount )
        setupSelected();
    TQCanvasRectangle * rect = 0;
    TQCanvasLine * line = 0;
    if( pointIndex == lineCount || lineCount == 1) {
        line = m_LineList.last();
        TQPoint p = line -> endPoint();
        rect = m_RectList.last();
        rect -> setX( p.x() );
        rect -> setY( p.y() );
        rect -> setZ( 4 );
        return;
    }
    line = m_LineList.at( pointIndex );
    TQPoint p = line -> startPoint();
    rect = m_RectList.at( pointIndex );
    rect -> setX( p.x() );
    rect -> setY( p.y() );
    rect -> setZ( 4 );
}

void LinePath::setupSelected() {
    m_RectList.clear();
    TQCanvasLine * line = 0;
    LineListIt it( m_LineList );
    while( ( line = it.current() ) ) {
        TQPoint sp = line -> startPoint();
        TQCanvasRectangle *rect = Widget_Utils::decoratePoint(sp);
        m_RectList.append( rect );
        ++it;
    }
    //special case for last point
    line = m_LineList.last();
    TQPoint p = line -> endPoint();
    TQCanvasRectangle *rect = Widget_Utils::decoratePoint(p);
    m_RectList.append( rect );
    update();
}

TQPen LinePath::getPen() {
    Uml::Association_Type type = getAssocType();
    if( type == Uml::at_Dependency || type == Uml::at_Realization || type == Uml::at_Anchor )
        return TQPen( getLineColor(), getLineWidth(), TQt::DashLine );
    return TQPen( getLineColor(), getLineWidth() );
}

void LinePath::calculateHead() {
    uint size = m_LineList.count();
    TQPoint farPoint;
    int halfLength = 10;
    double arrowAngle = 0.2618;   // 0.5 * atan(sqrt(3.0) / 3.0) = 0.2618
    Uml::Association_Type at = getAssocType();
    bool diamond = (at == Uml::at_Aggregation || at == Uml::at_Composition);
    if (diamond || at == Uml::at_Containment) {
        farPoint = getPoint(1);
        m_EgdePoint = getPoint(0);
        if (diamond) {
            arrowAngle *= 1.5;  // wider
            halfLength += 1;    // longer
        } else {
            // Containment has a circle-plus symbol at the
            // containing object.  What we are tweaking here
            // is the perpendicular line through the circle
            // (i.e. the horizontal line of the plus sign if
            // the objects are oriented north/south)
            arrowAngle *= 2.5;  // wider
            halfLength -= 4;    // shorter
        }
    } else {
        farPoint = getPoint(size - 1);
        m_EgdePoint = getPoint(size);
        // We have an arrow.
        arrowAngle *= 2.0;      // wider
        halfLength += 3;        // longer
    }
    int xa = farPoint.x();
    int ya = farPoint.y();
    int xb = m_EgdePoint.x();
    int yb = m_EgdePoint.y();
    double deltaX = xb - xa;
    double deltaY = yb - ya;
    double hypotenuse = sqrt(deltaX*deltaX + deltaY*deltaY); // the length
    double slope = atan2(deltaY, deltaX);       //slope of line
    double arrowSlope = slope + arrowAngle;
    double cosx, siny;
    if (hypotenuse < 1.0e-6) {
        cosx = 1.0;
        siny = 0.0;
    } else {
        cosx = halfLength * deltaX/hypotenuse;
        siny = halfLength * deltaY/hypotenuse;
    }

    m_ArrowPointA.setX( (int)rint(xb - halfLength * cos(arrowSlope)) );
    m_ArrowPointA.setY( (int)rint(yb - halfLength * sin(arrowSlope)) );
    arrowSlope = slope - arrowAngle;
    m_ArrowPointB.setX( (int)rint(xb - halfLength * cos(arrowSlope)) );
    m_ArrowPointB.setY( (int)rint(yb - halfLength * sin(arrowSlope)) );

    if(xa > xb)
        cosx = cosx > 0 ? cosx : cosx * -1;
    else
        cosx = cosx > 0 ? cosx * -1: cosx;

    if(ya > yb)
        siny = siny > 0 ? siny : siny * -1;
    else
        siny = siny > 0 ? siny * -1 : siny;

    m_MidPoint.setX( (int)rint(xb + cosx) );
    m_MidPoint.setY( (int)rint(yb + siny) );

    m_PointArray.setPoint(0, m_EgdePoint);
    m_PointArray.setPoint(1, m_ArrowPointA);
    if( getAssocType() == Uml::at_Realization ||
            getAssocType() == Uml::at_Generalization ) {
        m_PointArray.setPoint( 2, m_ArrowPointB );
        m_PointArray.setPoint( 3, m_EgdePoint );
    } else {
        TQPoint diamondFarPoint;
        diamondFarPoint.setX( (int)rint(xb + cosx * 2) );
        diamondFarPoint.setY( (int)rint(yb + siny * 2) );
        m_PointArray.setPoint(2, diamondFarPoint);
        m_PointArray.setPoint(3, m_ArrowPointB);
    }

}

void LinePath::updateHead() {
    int count = m_HeadList.count();
    TQCanvasLine * line = 0;

    switch( getAssocType() ) {
    case Uml::at_State:
    case Uml::at_Activity:
    case Uml::at_UniAssociation:
    case Uml::at_Dependency:
        if( count < 2)
            return;
        line = m_HeadList.at( 0 );
        line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointA.x(), m_ArrowPointA.y() );

        line = m_HeadList.at( 1 );
        line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointB.x(), m_ArrowPointB.y() );
        break;

    case Uml::at_Relationship:
        if (count < 2) {
            return;
        }
        {
            int xoffset = 0;
            int yoffset = 0;
            if( m_DockRegion == TopBottom )
                xoffset = 8;
            else
                yoffset = 8;
            line = m_HeadList.at( 0 );
            line->setPoints( m_PointArray[2].x(), m_PointArray[2].y(),
                             m_PointArray[0].x()-xoffset, m_PointArray[0].y()-yoffset );

            line = m_HeadList.at( 1 );
            line->setPoints( m_PointArray[2].x(), m_PointArray[2].y(),
                             m_PointArray[0].x()+xoffset, m_PointArray[0].y()+yoffset );
        }

    case Uml::at_Generalization:
    case Uml::at_Realization:
        if( count < 3)
            return;
        line = m_HeadList.at( 0 );
        line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointA.x(), m_ArrowPointA.y() );

        line = m_HeadList.at( 1 );
        line -> setPoints( m_EgdePoint.x(), m_EgdePoint.y(), m_ArrowPointB.x(), m_ArrowPointB.y() );

        line = m_HeadList.at( 2 );
        line -> setPoints( m_ArrowPointA.x(), m_ArrowPointA.y(), m_ArrowPointB.x(), m_ArrowPointB.y() );
        m_pClearPoly -> setPoints( m_PointArray );
        break;

    case Uml::at_Composition:
    case Uml::at_Aggregation:
        if( count < 4)
            return;
        line = m_HeadList.at( 0 );
        line -> setPoints( m_PointArray[ 0 ].x(), m_PointArray[ 0 ].y(), m_PointArray[ 1 ].x(), m_PointArray[ 1 ].y() );

        line = m_HeadList.at( 1 );
        line -> setPoints( m_PointArray[ 1 ].x(), m_PointArray[ 1 ].y(), m_PointArray[ 2 ].x(), m_PointArray[ 2 ].y() );

        line = m_HeadList.at( 2 );
        line -> setPoints( m_PointArray[ 2 ].x(), m_PointArray[ 2 ].y(), m_PointArray[ 3 ].x(), m_PointArray[ 3 ].y() );

        line = m_HeadList.at( 3 );
        line -> setPoints( m_PointArray[ 3 ].x(), m_PointArray[ 3 ].y(), m_PointArray[ 0 ].x(), m_PointArray[ 0 ].y() );
        m_pClearPoly -> setPoints( m_PointArray );
        break;

    case Uml::at_Containment:
        if (count < 1)
            return;
        line = m_HeadList.at( 0 );
        line->setPoints( m_PointArray[ 1 ].x(), m_PointArray[ 1 ].y(),
                         m_PointArray[ 3 ].x(), m_PointArray[ 3 ].y() );
        m_pCircle -> setX( m_MidPoint.x() );
        m_pCircle -> setY( m_MidPoint.y() );
        break;
    default:
        break;
    }
}

void LinePath::growList(LineList &list, int by) {
    TQPen pen( getLineColor(), getLineWidth() );
    for (int i = 0; i < by; i++) {
        TQCanvasLine * line = new TQCanvasLine( getCanvas() );
        line -> setZ( 0 );
        line -> setPen( pen );
        line -> setVisible( true );
        list.append( line );
    }
}

void LinePath::createHeadLines() {
    m_HeadList.clear();
    TQCanvas * canvas = getCanvas();
    switch( getAssocType() ) {
    case Uml::at_Activity:
    case Uml::at_State:
    case Uml::at_Dependency:
    case Uml::at_UniAssociation:
    case Uml::at_Relationship:
        growList(m_HeadList, 2);
        break;

    case Uml::at_Generalization:
    case Uml::at_Realization:
        growList(m_HeadList, 3);
        m_pClearPoly = new TQCanvasPolygon( canvas );
        m_pClearPoly -> setVisible( true );
        m_pClearPoly -> setBrush( TQBrush( TQt::white ) );
        m_pClearPoly -> setZ( -1 );
        break;

    case Uml::at_Composition:
    case Uml::at_Aggregation:
        growList(m_HeadList, 4);
        m_pClearPoly = new TQCanvasPolygon( canvas );
        m_pClearPoly -> setVisible( true );
        if( getAssocType() == Uml::at_Aggregation )
            m_pClearPoly -> setBrush( TQBrush( TQt::white ) );
        else
            m_pClearPoly -> setBrush( TQBrush( getLineColor() ) );
        m_pClearPoly -> setZ( -1 );
        break;

    case Uml::at_Containment:
        growList(m_HeadList, 1);
        if (!m_pCircle) {
            m_pCircle = new Circle( canvas, 6 );
            m_pCircle->show();
            m_pCircle->setPen( TQPen( getLineColor(), getLineWidth() ) );
        }
        break;
    default:
        break;
    }
    m_bHeadCreated = true;
}

void LinePath::calculateParallelLine() {
    int midCount = count() / 2;
    double ATAN = atan(1.0);
    int lineDist = 10;
    //get  1/8(M) and 7/8(T) point
    TQPoint a = getPoint( midCount - 1 );
    TQPoint b = getPoint( midCount );
    int mx = ( a.x() + b.x() ) / 2;
    int my = ( a.y() + b.y() ) / 2;
    int tx = ( mx + b.x() ) / 2;
    int ty = ( my + b.y() ) / 2;
    //find dist between M and T points
    int distX = ( mx - tx );
    distX *= distX;
    int distY = ( my - ty );
    distY *= distY;
    double dist = sqrt( double(distX + distY) );
    double angle = atan2( double(ty - my), double(tx - mx) ) + ( ATAN * 2 );
    //find point from M to start line from.
    double cosx = cos( angle ) * lineDist;
    double siny = sin( angle ) * lineDist;
    TQPoint pointM( mx + (int)cosx, my + (int)siny );
    //find dist between P(xb, yb)
    distX = ( tx - b.x() );
    distX *= distX;
    distY = ( ty - b.y() );
    distY *= distY;
    dist = sqrt( double(distX + distY) );
    //find point from T to end line
    cosx = cos( angle ) * lineDist;
    siny = sin( angle ) * lineDist;
    TQPoint pointT( tx + (int)cosx, ty + (int)siny );
    m_ParallelLines[ 1 ] = pointM;
    m_ParallelLines[ 0 ] = pointT;

    int arrowDist = 5;
    angle = atan2( double(pointT.y() - pointM.y()),
                   double(pointT.x() - pointM.x()) );
    double arrowSlope = angle + ATAN;
    cosx = ( cos( arrowSlope ) ) * arrowDist;
    siny = ( sin( arrowSlope ) ) * arrowDist;
    m_ParallelLines[ 2 ] = TQPoint( pointT.x() - (int)cosx, pointT.y() - (int)siny );
    arrowSlope = angle - ATAN;
    cosx = ( cos( arrowSlope )  ) * arrowDist;
    siny = ( sin( arrowSlope ) ) * arrowDist;
    m_ParallelLines[ 3 ] = TQPoint( pointT.x() - (int)cosx, pointT.y() - (int)siny );
}

void LinePath::setupParallelLine() {
    m_ParallelList.clear();
    growList(m_ParallelList, 3);
    m_bParallelLineCreated = true;
}

void LinePath::updateParallelLine() {
    if( !m_bParallelLineCreated )
        return;
    TQCanvasLine * line = 0;
    TQPoint common = m_ParallelLines.at( 0 );
    TQPoint p = m_ParallelLines.at( 1 );
    line = m_ParallelList.at( 0 );
    line -> setPoints( common.x(), common.y(), p.x(), p.y() );

    p = m_ParallelLines.at( 2 );
    line = m_ParallelList.at( 1 );
    line -> setPoints( common.x(), common.y(), p.x(), p.y() );

    p = m_ParallelLines.at( 3 );
    line = m_ParallelList.at( 2 );
    line -> setPoints( common.x(), common.y(), p.x(), p.y() );
}

bool LinePath::operator==( LinePath & rhs ) {
    if( this -> m_LineList.count() != rhs.m_LineList.count() )
        return false;

    //Check to see if all points at the same position
    for( int i = 0; i< rhs.count() ; i++ ) {
        if( this -> getPoint( i ) != rhs.getPoint( i ) )
            return false;
    }
    return true;
}

LinePath & LinePath::operator=( LinePath & rhs ) {
    if( this == &rhs )
        return *this;
    //clear out the old canvas objects
    this -> m_LineList.clear();
    this -> m_ParallelList.clear();
    this -> m_RectList.clear();
    this -> m_HeadList.clear();
    int count = rhs.m_LineList.count();
    //setup start end points
    this -> setStartEndPoints( rhs.getPoint( 0 ), rhs.getPoint( count) );
    //now insert the rest
    for( int i = 1; i < count ; i++ ) {
        this -> insertPoint( i, rhs.getPoint ( i ) );
    }
    this -> setAssocType( rhs.getAssocType() );

    return *this;
}

TQCanvas * LinePath::getCanvas() {
    if( !m_pAssociation )
        return 0;
    const UMLView * view =  m_pAssociation->getUMLView();
    return view -> canvas();
}

Uml::Association_Type LinePath::getAssocType() {
    if( m_pAssociation )
        return m_pAssociation -> getAssocType();
    return Uml::at_Association;
}

TQColor LinePath::getLineColor() {
    if( !m_pAssociation )
        return TQt::black;
    return m_pAssociation -> getLineColor();
}

uint LinePath::getLineWidth() {
    if( !m_pAssociation )
        return 0;
    int viewLineWidth = m_pAssociation -> getLineWidth();
    if ( viewLineWidth >= 0 && viewLineWidth <= 10 )
        return viewLineWidth;
    else {
        kWarning() << "Ignore wrong LineWidth of " << viewLineWidth
        << " in LinePath::getLineWidth" << endl;
        return 0;
    }
}

void LinePath::cleanup() {
    if (m_pAssociation)
        m_LineList.clear();
    m_HeadList.clear();
    m_RectList.clear();
    m_ParallelList.clear();

    if( m_pClearPoly )
        delete m_pClearPoly;
    if( m_pCircle )
        delete m_pCircle;
    m_pCircle = 0;
    m_pClearPoly = 0;
    m_bHeadCreated = m_bParallelLineCreated = false;
    if( m_pAssociation ) {
        UMLView * view =  (UMLView *)m_pAssociation -> parent();
        if(view) {
            disconnect( view, TQ_SIGNAL( sigColorChanged( Uml::IDType ) ), this, TQ_SLOT( slotLineColorChanged( Uml::IDType ) ) );
            disconnect( view, TQ_SIGNAL( sigLineWidthChanged( Uml::IDType ) ), this, TQ_SLOT( slotLineWidthChanged( Uml::IDType ) ) );
        }
        m_pAssociation = NULL;
    }
}

void LinePath::setDockRegion( Region region ) {
    m_DockRegion = region;
}

bool LinePath::hasPoints () {
    int count = m_LineList.count();
    if (count>1)
        return true;
    return false;
}
void LinePath::dumpPoints () {
    int count = m_LineList.count();
    for( int i = 1; i < count; i++ ) {
        TQPoint point = getPoint( i );
        kDebug()<<" * point x:"<<point.x()<<" y:"<<point.y()<<endl;
    }
}

void LinePath::saveToXMI( TQDomDocument & qDoc, TQDomElement & qElement ) {
    int count = m_LineList.count();
    TQPoint point = getPoint( 0 );
    TQDomElement lineElement = qDoc.createElement( "linepath" );
    TQDomElement startElement = qDoc.createElement( "startpoint" );
    startElement.setAttribute( "startx", point.x() );
    startElement.setAttribute( "starty", point.y() );
    lineElement.appendChild( startElement );
    TQDomElement endElement = qDoc.createElement( "endpoint" );
    point = getPoint( count );
    endElement.setAttribute( "endx", point.x() );
    endElement.setAttribute( "endy", point.y() );
    lineElement.appendChild( endElement );
    for( int i = 1; i < count; i++ ) {
        TQDomElement pointElement = qDoc.createElement( "point" );
        point = getPoint( i );
        pointElement.setAttribute( "x", point.x() );
        pointElement.setAttribute( "y", point.y() );
        lineElement.appendChild( pointElement );
    }
    qElement.appendChild( lineElement );
}

bool LinePath::loadFromXMI( TQDomElement & qElement ) {
    TQDomNode node = qElement.firstChild();
    TQDomElement startElement = node.toElement();
    if( startElement.isNull() || startElement.tagName() != "startpoint" )
        return false;
    TQString x = startElement.attribute( "startx", "0" );
    int nX = x.toInt();
    TQString y = startElement.attribute( "starty", "0" );
    int nY = y.toInt();
    TQPoint startPoint( nX, nY );

    node = startElement.nextSibling();
    TQDomElement endElement = node.toElement();
    if( endElement.isNull() || endElement.tagName() != "endpoint" )
        return false;
    x = endElement.attribute( "endx", "0" );
    nX = x.toInt();
    y = endElement.attribute( "endy", "0" );
    nY = y.toInt();
    TQPoint endPoint( nX, nY );
    setStartEndPoints( startPoint, endPoint );
    TQPoint point;
    node = endElement.nextSibling();
    TQDomElement element = node.toElement();
    int i = 1;
    while( !element.isNull() ) {
        if( element.tagName() == "point" ) {
            x = element.attribute( "x", "0" );
            y = element.attribute( "y", "0" );
            point.setX( x.toInt() );
            point.setY( y.toInt() );
            insertPoint( i++, point );
        }
        node = element.nextSibling();
        element = node.toElement();
    }

    return true;
}


void LinePath::activate() {
    int count = m_LineList.count();
    if (count == 0)
        return;
    TQCanvas * canvas = getCanvas();
    if (canvas == NULL)
        return;
    for (int i = 0; i < count ; i++) {
        TQCanvasLine *line = m_LineList.at(i);
        line -> setCanvas( canvas );
        line -> setPen( getPen() );
    }
}



#include "linepath.moc"
