/***************************************************************************
 *   Copyright (C) 2003-2005 by David Saxton                               *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#include "canvasitemparts.h"
#include "connector.h"
#include "flowcodedocument.h"
#include "flowcode.h"
#include "flowpart.h"
#include "fpnode.h"
#include "itemdocument.h"
#include "itemdocumentdata.h"
#include "microsettings.h"
#include "micropackage.h"
#include "picinfo.h"
#include "pinmapping.h"
#include "variant.h"

#include <kdebug.h>

#include <qbitarray.h>
#include <qbitmap.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <qregexp.h>

#include <assert.h>
#include <algorithm>
#include <cmath>

// The following arrays of numbers represent the positions of nodes in different configurations,
// with the numbers as NodeInfo::Position.

Node::node_dir diamondNodePositioning[8][3] = {
	{Node::dir_up,	Node::dir_down,	Node::dir_right},
	{Node::dir_up,	Node::dir_down,	Node::dir_left},
	{Node::dir_up,	Node::dir_right,Node::dir_down},
	{Node::dir_up,	Node::dir_right,Node::dir_left},
	{Node::dir_left,Node::dir_right,Node::dir_down},
	{Node::dir_left,Node::dir_right,Node::dir_up},
	{Node::dir_left,Node::dir_down,	Node::dir_right},
	{Node::dir_left,Node::dir_down,	Node::dir_up} };

Node::node_dir inOutNodePositioning[8][2] = {
	{Node::dir_up,Node::dir_down},
	{Node::dir_up,Node::dir_right},
	{Node::dir_up,Node::dir_left},
	{Node::dir_right,Node::dir_right}, // (invalid)
	{Node::dir_left,Node::dir_right},
	{Node::dir_left,Node::dir_down},
	{Node::dir_left,Node::dir_up},
	{Node::dir_right,Node::dir_right} }; // (invalid)

Node::node_dir inNodePositioning[4] = {Node::dir_up,Node::dir_right,Node::dir_down,Node::dir_left};

Node::node_dir outNodePositioning[4] = {Node::dir_down,Node::dir_left,Node::dir_up,Node::dir_right};

FlowPart::FlowPart( ICNDocument *icnDocument, bool newItem, const QString &id )
	: CNItem( icnDocument, newItem, id )
{
	icnDocument->registerItem(this);
	m_pFlowCodeDocument = dynamic_cast<FlowCodeDocument*>(icnDocument);
	assert( m_pFlowCodeDocument );
	
	m_flowSymbol = FlowPart::ps_other;
	m_orientation = 0;
	m_stdInput = 0l;
	m_stdOutput = 0l;
	m_altOutput = 0l;
	
	connect( m_pFlowCodeDocument, SIGNAL(picTypeChanged()), this, SLOT(slotUpdateFlowPartVariables()) );
	connect( m_pFlowCodeDocument, SIGNAL(pinMappingsChanged()), this, SLOT(slotUpdateFlowPartVariables()) );
}


FlowPart::~FlowPart()
{
	// We have to check view, as if the item is deleted before the CNItem constructor
	// is called, then there will be no view
	if (m_pFlowCodeDocument)
	{
		const VariantDataMap::iterator end = m_variantData.end();
		for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it )
		{
			Variant *v = it.data();
			if (v)
				m_pFlowCodeDocument->varNameChanged( "", v->value().toString() );
		}
	}
}


void FlowPart::setCaption( const QString &caption )
{
	if ( m_flowSymbol == FlowPart::ps_other )
	{
		m_caption = caption;
		return;
	}
	
	QWidget *w = new QWidget();
	QPainter p(w);
	p.setFont( font() );
	const int text_width = p.boundingRect( boundingRect(), (Qt::SingleLine | Qt::AlignHCenter | Qt::AlignVCenter), caption ).width();
	p.end();
	delete w;
	int width = std::max( ((int)(text_width/16))*16, 48 );
	
	switch(m_flowSymbol)
	{
		case FlowPart::ps_call:
		{
			width += 48;
			break;
		}
		case FlowPart::ps_io:
		case FlowPart::ps_round:
		{
			width += 32;
			break;
		}
		case FlowPart::ps_decision:
		{
			width += 64;
			break;
		}
		case FlowPart::ps_process:
		default:
		{
			width += 32;
			break;
		}
	}
	
	bool hasSideConnectors = m_flowSymbol == FlowPart::ps_decision;
	if ( hasSideConnectors && (width != this->width()) )
		p_icnDocument->requestRerouteInvalidatedConnectors();
	
	initSymbol( m_flowSymbol, width );
	m_caption = caption;
}
void FlowPart::postResize()
{
	updateNodePositions();
	CNItem::postResize();
}

void FlowPart::createStdInput()
{
	m_stdInput = (FPNode*)createNode( 0, 0, Node::dir_up, "stdinput", Node::fp_in );
	updateNodePositions();
}
void FlowPart::createStdOutput()
{
	m_stdOutput = (FPNode*)createNode( 0, 0, Node::dir_down, "stdoutput", Node::fp_out );
	updateNodePositions();
}
void FlowPart::createAltOutput()
{
	m_altOutput = (FPNode*)createNode( 0, 0, Node::dir_right, "altoutput", Node::fp_out );
	updateNodePositions();
}

void FlowPart::initSymbol( FlowPart::FlowSymbol symbol, int width )
{
	m_flowSymbol = symbol;
	
	switch(symbol)
	{
		case FlowPart::ps_other:
		{
			return;
		}
		case FlowPart::ps_call:
		case FlowPart::ps_process:
		{	
			setItemPoints( QRect( -width/2, -16, width, 24 ) );
			break;
		}
		
		case FlowPart::ps_io:
		{
			// define parallelogram shape
			QPointArray pa(4);
			pa[0] = QPoint( -(width-10)/2, -16 );
			pa[1] = QPoint( width/2, -16 );
			pa[2] = QPoint( (width-10)/2, 8 );
			pa[3] = QPoint( -width/2, 8 );
			setItemPoints(pa);
			break;
		}
		
		case FlowPart::ps_round:
		{
			// define rounded rectangles as two semicricles with RP_NUM/2 points with gap inbetween
			// These points are not used for drawing; merely for passing to qcanvaspolygonitem for collision detection
			// If there is a better way for a rounder rectangle + collision detection, please let me know...
 		
			int halfHeight = 12;
	
			// Draw semicircle
			double x;
			const int RP_NUM = 48;
			QPointArray pa(RP_NUM);
			int point = 0;
			for ( double y = -1.0; y <= 1.0; y+= 4.0/(RP_NUM-2) )
			{
			    x = sqrt(1-y*y)*halfHeight;
				pa[point] = QPoint( (int)(width+x)-halfHeight, (int)(halfHeight*y) );
				pa[RP_NUM-1-point] = QPoint ( (int)(halfHeight-x), (int)(halfHeight*y) );
				point++;
			}
 		
			pa.translate( -width/2, 4 );
			setItemPoints(pa);
			break;
		}
		
		case FlowPart::ps_decision:
		{
			// define rhombus
			QPointArray pa(6);
			pa[0] = QPoint( 0, -24 );
			pa[1] = QPoint( width/2, -6 );
			pa[2] = QPoint( width/2, 6 );
			pa[3] = QPoint( 0, 24 );
			pa[4] = QPoint( -width/2, 6 );
			pa[5] = QPoint( -width/2, -6 );
			setItemPoints(pa);
			break;
		}
		default: kdError() << k_funcinfo << "Unknown flowSymbol: "<<symbol<<endl;
	}
}

void FlowPart::drawShape( QPainter &p )
{
	initPainter(p);
	
	const double _x = int( x() + offsetX() );
	const double _y = int( y() + offsetY() );
	const double w = width();
	double h = height();
	
	switch (m_flowSymbol)
	{
		case FlowPart::ps_other:
		{	
			CNItem::drawShape(p);
			break;
		}
		
		case FlowPart::ps_io:
		{
			h--;
			double roundSize = 8;
			double slantIndent = 5;
			
			const double pi = 3.1415926536;
			const double DPR = 180./pi;
// 			CNItem::drawShape(p);
			double inner = std::atan(h/slantIndent);
			double outer = pi-inner;
			
			int inner16 = int(16*inner*DPR);
			int outer16 = int(16*outer*DPR);
			
			p.save();
			p.setPen( Qt::NoPen );
			p.drawPolygon( areaPoints() );
			p.restore();
			
			p.drawLine( int(_x+slantIndent+roundSize/2),	int(_y),	int(_x+w-roundSize/2),	int(_y) );
			p.drawLine( int(_x-slantIndent+w-roundSize/2),	int(_y+h),	int(_x+roundSize/2),	int(_y+h) );
			p.drawLine( int(_x+w+(std::sin(outer)-1)*(roundSize/2)),				int(_y+(1-std::cos(outer))*(roundSize/2)),
						int(_x+w-slantIndent+(std::sin(inner)-1)*(roundSize/2)),	int(_y+h+(std::cos(inner)-1)*(roundSize/2)) );
			p.drawLine( int(_x+(1-std::sin(outer))*(roundSize/2)),					int(_y+h+(std::cos(outer)-1)*(roundSize/2)),
						int(_x+slantIndent+(1-std::sin(inner))*(roundSize/2)),		int(_y+(1-std::cos(inner))*(roundSize/2)) );
			
			p.drawArc( int(_x+slantIndent),				int(_y),				int(roundSize), int(roundSize), 90*16,			inner16 );
			p.drawArc( int(_x+w-roundSize),				int(_y),				int(roundSize), int(roundSize), 270*16+inner16,	outer16 );
			p.drawArc( int(_x-slantIndent+w-roundSize),	int(_y+h-roundSize),	int(roundSize), int(roundSize), 270*16,			inner16 );
			p.drawArc( int(_x),							int(_y+h-roundSize),	int(roundSize), int(roundSize), 90*16+inner16,	outer16) ;
			break;
		}
		
		case FlowPart::ps_decision:
		{
			// TODO Make the shape nice and pretty with rounded corners
			CNItem::drawShape(p);
			break;
		}
		
		case FlowPart::ps_call:
		{
			p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), int(1000./w), int(1000./h) );
			p.drawLine( int(_x+8), int(_y), int(_x+8), int(_y+h) );
			p.drawLine( int(_x+w-8), int(_y), int(_x+w-8), int(_y+h) );
			break;
		}
		case FlowPart::ps_process:
		{
			p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), int(1000./w), int(1000./h) );
			break;
		}
		
		case FlowPart::ps_round:
		{
			p.drawRoundRect( int(_x), int(_y), int(w), int(h+1), 30, 100 );
			break;
		}
	}
	
	p.setPen( Qt::black );
	p.setFont( font() );
	p.drawText( boundingRect(), (Qt::WordBreak | Qt::AlignHCenter | Qt::AlignVCenter), m_caption );
}

QString FlowPart::gotoCode( const QString& internalNodeId )
{
	FlowPart *end = outputPart(internalNodeId);
	if (!end) return "";	
	return "goto "+end->id();
}

FlowPart* FlowPart::outputPart( const QString& internalNodeId )
{
	Node *node = p_icnDocument->nodeWithID( nodeId(internalNodeId) );
	
	FPNode *fpnode = dynamic_cast<FPNode*>(node);
	if ( !fpnode || fpnode->type() == Node::fp_in )
		return 0l;
	
	return fpnode->outputFlowPart();
}

FlowPartList FlowPart::inputParts( const QString& id )
{
	Node *node = p_icnDocument->nodeWithID(id);
	
	if ( FPNode *fpNode = dynamic_cast<FPNode*>(node) )
		return fpNode->inputFlowParts();
	
	return FlowPartList();
}
	
FlowPartList FlowPart::inputParts()
{
	FlowPartList list;
	
	const NodeMap::iterator nEnd = m_nodeMap.end();
	for ( NodeMap::iterator it = m_nodeMap.begin(); it != nEnd; ++it )
	{
		Node *node = p_icnDocument->nodeWithID( it.data().id );
		FlowPartList newList;
		
		if ( FPNode *fpNode = dynamic_cast<FPNode*>(node) )
			newList = fpNode->inputFlowParts();
		
		const FlowPartList::iterator nlEnd = newList.end();
		for ( FlowPartList::iterator it = newList.begin(); it != nlEnd; ++it )
		{
			if (*it) list.append(*it);
		}
	}
	
	return list;
}

FlowPartList FlowPart::outputParts()
{
	FlowPartList list;
	
	const NodeMap::iterator end = m_nodeMap.end();
	for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
	{
		FlowPart *part = outputPart( it.key() );
		if (part) list.append(part);
	}
	
	return list;
}


FlowPart* FlowPart::endPart( QStringList ids, FlowPartList *previousParts )
{
	if ( ids.empty() )
	{
		const NodeMap::iterator end = m_nodeMap.end();
		for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
		{
			ids.append( it.key() );
		}
		filterEndPartIDs( &ids );
	}
	
	const bool createdList = (!previousParts);
	if (createdList) {
		previousParts = new FlowPartList;
	} else if ( previousParts->contains(this) ) {
		return 0l;
	}
	previousParts->append(this);
	
	if ( ids.empty() ) {
		return 0l;
	}
	if ( ids.size() == 1 ) {
		return outputPart( *(ids.begin()) );
	}
	
	typedef QValueList<FlowPartList>  ValidPartsList;
	ValidPartsList validPartsList;
	
	const QStringList::iterator idsEnd = ids.end();
	for ( QStringList::iterator it = ids.begin(); it != idsEnd; ++it )
	{
		int prevLevel = level();
		FlowPartList validParts;
		FlowPart *part = outputPart(*it);
		while (part)
		{
			if ( !validParts.contains(part) )
			{
				validParts.append(part);
// 				if ( part->level() >= level() ) {
				const int _l = part->level();
				part = part->endPart( QStringList(), previousParts );
				prevLevel = _l;
// 				} else {
// 					part = 0l;
// 				}
			}
			else {
				part = 0l;
			}
		}
		if ( !validParts.empty() ) {
			validPartsList.append(validParts);
		}
	}
	
	if (createdList)
	{
		delete previousParts;
		previousParts = 0l;
	}
	
	if ( validPartsList.empty() ) return 0l;
	
	FlowPartList firstList = *(validPartsList.begin());
	const FlowPartList::iterator flEnd = firstList.end();
	const ValidPartsList::iterator vplEnd = validPartsList.end();
	for ( FlowPartList::iterator it = firstList.begin(); it != flEnd; ++it )
	{
		bool ok = true;
		for ( ValidPartsList::iterator vplit = validPartsList.begin(); vplit != vplEnd; ++vplit )
		{
			if ( !(*vplit).contains(*it) ) ok = false;
		}
		if (ok) return *it;
	}
	
	return 0l;
}


void FlowPart::handleIfElse( FlowCode *code, const QString &case1Statement, const QString &case2Statement,
				 			 const QString &case1, const QString &case2 )
{
	if (!code) return;
	
	FlowPart *stop = 0l;
	FlowPart *part1 = outputPart(case1);
	FlowPart *part2 = outputPart(case2);
	
	if ( part1 && part2 ) stop = endPart( QStringList::split( ',', case1+","+case2 ) );
	
	if ( (!part1 && !part2) || (part1 == stop && part2 == stop) ) return;
	
	code->addStopPart(stop);
	
	if ( part1 && part1 != stop && code->isValidBranch(part1) )
	{
		// Use the case1 statement
		code->addCode( "if "+case1Statement+" then "+"\n{" );
		code->addCodeBranch(part1);
		code->addCode("}");
		
		if ( part2 && part2 != stop && code->isValidBranch(part2) )
		{
			code->addCode( "else\n{" );
			code->addCodeBranch(part2);
			code->addCode("}");
		}
	}
	else if ( code->isValidBranch(part2) )
	{
		// Use the case2 statement
		code->addCode( "if "+case2Statement+" then "+"\n{" );
		code->addCodeBranch(part2);
		code->addCode("}");
	}
	
	code->removeStopPart(stop);
	code->addCodeBranch(stop);
}


Variant * FlowPart::createProperty( const QString & id, Variant::Type::Value type )
{
	if ( type != Variant::Type::Port
			&& type != Variant::Type::Pin
			&& type != Variant::Type::VarName
			&& type != Variant::Type::SevenSegment
			&& type != Variant::Type::KeyPad )
		return CNItem::createProperty( id, type );
	
	Variant * v = createProperty( id, Variant::Type::String );
	v->setType(type);
	
	if ( type == Variant::Type::VarName )
	{
		if ( MicroSettings * settings = m_pFlowCodeDocument->microSettings() )
			v->setAllowed( settings->variableNames() );
		connect( property(id), SIGNAL(valueChanged(QVariant, QVariant )), this, SLOT(varNameChanged(QVariant, QVariant )) );
	}
	else
		slotUpdateFlowPartVariables();
	
	return v;
}


void FlowPart::slotUpdateFlowPartVariables()
{
	if (!m_pFlowCodeDocument)
		return;
	
	MicroSettings *s = m_pFlowCodeDocument->microSettings();
	if (!s)
		return;
	
	const PinMappingMap pinMappings = s->pinMappings();
	QStringList sevenSegMaps;
	QStringList keyPadMaps;
	PinMappingMap::const_iterator pEnd = pinMappings.end();
	for ( PinMappingMap::const_iterator it = pinMappings.begin(); it != pEnd; ++it )
	{
		switch ( it.data().type() )
		{
			case PinMapping::SevenSegment:
				sevenSegMaps << it.key();
				break;
				
			case PinMapping::Keypad_4x3:
			case PinMapping::Keypad_4x4:
				keyPadMaps << it.key();
				break;
				
			case PinMapping::Invalid:
				break;
		}
	}
	
	QStringList ports = s->microInfo()->package()->portNames();
	ports.sort();
	
	QStringList pins = s->microInfo()->package()->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open);
	pins.sort();
	
	const VariantDataMap::iterator vEnd = m_variantData.end();
	for ( VariantDataMap::iterator it = m_variantData.begin(); it != vEnd; ++it )
	{
		Variant * v = it.data();
		if ( !v )
			continue;
		
		if ( v->type() == Variant::Type::Port )
			v->setAllowed( ports );
		
		else if ( v->type() == Variant::Type::Pin )
			v->setAllowed( pins );
		
		else if ( v->type() == Variant::Type::SevenSegment )
		{
			v->setAllowed( sevenSegMaps );
			if ( !sevenSegMaps.isEmpty() && !sevenSegMaps.contains( v->value().toString() ) )
				v->setValue( sevenSegMaps.first() );
		}
		
		else if ( v->type() == Variant::Type::KeyPad )
		{
			v->setAllowed( keyPadMaps );
			if ( !keyPadMaps.isEmpty() && !keyPadMaps.contains( v->value().toString() ) )
				v->setValue( keyPadMaps.first() );
		}
	}
}


void FlowPart::updateVarNames()
{
	if (!m_pFlowCodeDocument)
		return;
	
	MicroSettings *s = m_pFlowCodeDocument->microSettings();
	if (!s)
		return;
	
	const QStringList names = s->variableNames();
	const VariantDataMap::iterator end = m_variantData.end();
	for ( VariantDataMap::iterator it = m_variantData.begin(); it != end; ++it )
	{
		Variant *v = it.data();
		if ( v && v->type() == Variant::Type::VarName )
			v->setAllowed(names);
	}
}


void FlowPart::varNameChanged( QVariant newValue, QVariant oldValue )
{
	if (!m_pFlowCodeDocument)
		return;
	m_pFlowCodeDocument->varNameChanged( newValue.asString(), oldValue.asString() );
}


inline int nodeDirToPos( Node::node_dir dir )
{
	switch (dir)
	{
		case Node::dir_right:
			return 0;
		case Node::dir_up:
			return 1;
		case Node::dir_left:
			return 2;
		case Node::dir_down:
			return 3;
	}
	return 0;
}


void FlowPart::updateAttachedPositioning( )
{
	if (b_deleted)
		return;
	
	//BEGIN Rearrange text if appropriate
	const QRect textPos[4] = {
		QRect( offsetX()+width(),	6,					40, 16 ), 
		QRect( 0,					offsetY()-16,		40, 16 ),
		QRect( offsetX()-40,		6,					40, 16 ),
		QRect( 0,					offsetY()+height(),	40, 16 ) };
	
	NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : 0;
	NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : 0l;
	
	Text *outputTrueText = m_textMap.contains("output_true") ? m_textMap["output_true"] : 0l;
	Text *outputFalseText = m_textMap.contains("output_false") ? m_textMap["output_false"] : 0l;
	
	if ( stdOutputInfo && outputTrueText )
		outputTrueText->setOriginalRect( textPos[ nodeDirToPos( (Node::node_dir)stdOutputInfo->orientation ) ] );
	
	if ( altOutputInfo && outputFalseText )
		outputFalseText->setOriginalRect( textPos[ nodeDirToPos( (Node::node_dir)altOutputInfo->orientation ) ] );
	
	const TextMap::iterator textMapEnd = m_textMap.end();
	for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it )
	{
		QRect pos = it.data()->recommendedRect();
		it.data()->move( pos.x() + x(), pos.y() + y() );
		it.data()->setGuiPartSize( pos.width(), pos.height() );
	}
	//END Rearrange text if appropriate
	
	const NodeMap::iterator end = m_nodeMap.end();
	for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
	{
		if ( !it.data().node )
		{
			kdError() << k_funcinfo << "Node in nodemap is null" << endl;
			continue;
		}
		
		double nx = it.data().x;
		double ny = it.data().y;
			
#define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8)
		nx = round_8(nx);
		ny = round_8(ny);
#undef round_8
			
		it.data().node->move( int(nx+x()), int(ny+y()) );
		it.data().node->setOrientation( (Node::node_dir)it.data().orientation );
	}
}


ItemData FlowPart::itemData( ) const
{
	ItemData itemData = CNItem::itemData();
	itemData.orientation = m_orientation;
	return itemData;
}


void FlowPart::restoreFromItemData( const ItemData & itemData )
{
	CNItem::restoreFromItemData(itemData);
	if ( itemData.orientation >= 0 )
		setOrientation( uint(itemData.orientation) );
}


void FlowPart::updateNodePositions()
{
	if ( m_orientation > 7 )
	{
		kdWarning() << k_funcinfo << "Invalid orientation: "<<m_orientation<<endl;
		return;
	}
	
	NodeInfo * stdInputInfo = m_stdInput ? &m_nodeMap["stdinput"] : 0l;
	NodeInfo * stdOutputInfo = m_stdOutput ? &m_nodeMap["stdoutput"] : 0;
	NodeInfo * altOutputInfo = m_altOutput ? &m_nodeMap["altoutput"] : 0l;
	
	if ( m_stdInput && m_stdOutput && m_altOutput )
	{
		stdInputInfo->orientation = diamondNodePositioning[m_orientation][0];
		stdOutputInfo->orientation = diamondNodePositioning[m_orientation][1];
		altOutputInfo->orientation = diamondNodePositioning[m_orientation][2];
	}
	else if ( m_stdInput && m_stdOutput )
	{
		stdInputInfo->orientation = inOutNodePositioning[m_orientation][0];
		stdOutputInfo->orientation = inOutNodePositioning[m_orientation][1];
	}
	else if ( m_orientation < 4 )
	{
		if (stdInputInfo)
			stdInputInfo->orientation = inNodePositioning[m_orientation];
		else if (stdOutputInfo)
			stdOutputInfo->orientation = outNodePositioning[m_orientation];
	}
	else
	{
		kdWarning() << k_funcinfo << "Invalid orientation: "<<m_orientation<<endl;
		return;
	}
	
	const NodeMap::iterator end = m_nodeMap.end();
	for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it )
	{
		if ( !it.data().node )
			kdError() << k_funcinfo << "Node in nodemap is null" << endl;
		
		else
		{
			switch ( it.data().orientation )
			{
				case Node::dir_right:
					it.data().x = offsetX()+width()+8;
					it.data().y = 0;
					break;
				case Node::dir_up:
					it.data().x = 0;
					it.data().y = offsetY()-8;
					break;
				case Node::dir_left:
					it.data().x = offsetX()-8;
					it.data().y = 0;
					break;
				case Node::dir_down:
					it.data().x = 0;
					it.data().y = offsetY()+height()+8;;
					break;
			}
		}
	}
	
	updateAttachedPositioning();
}


void FlowPart::setOrientation( uint orientation )
{
	if ( orientation == m_orientation )
		return;
	
	m_orientation = orientation;
	updateNodePositions();
	p_icnDocument->requestRerouteInvalidatedConnectors();
}


uint FlowPart::allowedOrientations( ) const
{
	// The bit positions shown here represent whether or not that orientation is allowed, the orientation being
	// what is displayed in the i'th position (0 to 3 on top, 4 to 7 on bottom) of orientation widget
	
	if ( m_stdInput && m_stdOutput && m_altOutput )
		return 255;
	
	if ( m_stdInput && m_stdOutput )
		return 119;
	
	if ( m_stdInput || m_stdOutput )
		return 15;
	
	return 0;
}

void FlowPart::orientationPixmap( uint orientation, QPixmap & pm ) const
{
	const QSize size = pm.size();
	
	if ( ! ( allowedOrientations() & ( 1 << orientation ) ) )
	{
		kdWarning() << k_funcinfo << "Requesting invalid orientation of " << orientation << endl;
		return;
	}
	
	QBitmap mask( 50, 50 );
	QPainter maskPainter(&mask);
	mask.fill( Qt::color0 );
	maskPainter.setBrush(Qt::color1);
	maskPainter.setPen(Qt::color1);
	
	QPainter p(&pm);
	p.setBrush(m_brushCol);
	p.setPen( Qt::black );

	// In order: right corner, top corner, left corner, bottom corner
	
	QPoint c[4] = {
		QPoint( int(0.7*size.width()), int(0.5*size.height()) ),
		QPoint( int(0.5*size.width()), int(0.4*size.height()) ),
		QPoint( int(0.3*size.width()), int(0.5*size.height()) ),
		QPoint( int(0.5*size.width()), int(0.6*size.height()) ) };
	
	QPoint d[4];
	d[0] = c[0] + QPoint( 7, 0 );
	d[1] = c[1] + QPoint( 0, -7 );
	d[2] = c[2] + QPoint( -7, 0 );
	d[3] = c[3] + QPoint( 0, 7 );
	
	if ( m_stdInput && m_stdOutput && m_altOutput )
	{
		//BEGIN Draw diamond outline
		QPointArray diamond(4);
		for ( uint i=0; i<4; ++i )
			diamond[i] = c[i];
		
		p.drawPolygon(diamond);
		maskPainter.drawPolygon(diamond);
		//END Draw diamond outline
		
		
		//BEGIN Draw input
		int pos0 = nodeDirToPos( diamondNodePositioning[orientation][0] );
		p.drawLine( c[pos0], d[pos0] );
		maskPainter.drawLine( c[pos0], d[pos0] );
		//END Draw input
		
		
		//BEGIN Draw "true" output as a tick
		QPointArray tick(4);
		tick[0] = QPoint( -3, 0 );
		tick[1] = QPoint( 0, 2 );
		tick[2] = QPoint( 0, 2 );
		tick[3] = QPoint( 4, -2 );
		
		int pos1 = nodeDirToPos( diamondNodePositioning[orientation][1] );
		tick.translate( d[pos1].x(), d[pos1].y() );
		p.drawLineSegments(tick);
		maskPainter.drawLineSegments(tick);
		//END Draw "true" output as a tick
		
		
		//BEGIN Draw "false" output as a cross
		QPointArray cross(4);
		cross[0] = QPoint( -2, -2 );
		cross[1] = QPoint( 2, 2 );
		cross[2] = QPoint( -2, 2 );
		cross[3] = QPoint( 2, -2 );
		
		int pos2 = nodeDirToPos( diamondNodePositioning[orientation][2] );
		cross.translate( d[pos2].x(), d[pos2].y() );
		p.drawLineSegments(cross);
		maskPainter.drawLineSegments(cross);
		//END Draw "false" output as a cross
	}
	
	else if ( m_stdInput || m_stdOutput )
	{
		p.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) );
		maskPainter.drawRoundRect( int(0.3*size.width()), int(0.4*size.height()), int(0.4*size.width()), int(0.2*size.height()) );
		
		int hal = 5; // half arrow length
		int haw = 3; // half arrow width
		
		QPoint arrows[4][6] = {
			{ QPoint( hal, 0 ), QPoint( 0, -haw ),
			  QPoint( hal, 0 ), QPoint( -hal, 0 ),
			  QPoint( hal, 0 ), QPoint( 0, haw ) },
			
			{ QPoint( 0, -hal ), QPoint( -haw, 0 ),
			  QPoint( 0, -hal ), QPoint( 0, hal ),
			  QPoint( 0, -hal ), QPoint( haw, 0 ) },
			  
			{ QPoint( -hal, 0 ), QPoint( 0, -haw ),
			  QPoint( -hal, 0 ), QPoint( hal, 0 ),
			  QPoint( -hal, 0 ), QPoint( 0, haw ) },
			 
			{ QPoint( 0, hal ), QPoint( -haw, 0 ),
			  QPoint( 0, hal ), QPoint( 0, -hal ),
			  QPoint( 0, hal ), QPoint( haw, 0 ) } };
		
		int inPos = -1;
		int outPos = -1;
		
		if ( m_stdInput && m_stdOutput )
		{
			inPos = nodeDirToPos( inOutNodePositioning[orientation][0] );
			outPos = nodeDirToPos( inOutNodePositioning[orientation][1] );
		}
		else if ( m_stdInput )
		{
			inPos = nodeDirToPos( inNodePositioning[orientation] );
		}
		else if ( m_stdOutput )
		{
			outPos = nodeDirToPos( outNodePositioning[orientation] );
		}
		
		if ( inPos != -1 )
		{
			QPointArray inArrow(6);
			for ( int i=0; i<6; ++i )
			{
				inArrow[i] = arrows[(inPos+2)%4][i];
			}
			inArrow.translate( d[inPos].x(), d[inPos].y() );
			p.drawPolygon(inArrow);
			maskPainter.drawPolygon(inArrow);
		}
		
		if ( outPos != -1 )
		{
			QPointArray outArrow(6);
			for ( int i=0; i<6; ++i )
			{
				outArrow[i] = arrows[outPos][i];
			}
			outArrow.translate( d[outPos].x(), d[outPos].y() );
			p.drawPolygon(outArrow);
			maskPainter.drawPolygon(outArrow);
		}
	}
	
	pm.setMask(mask);
}


#include "flowpart.moc"


