// ChildNodeView.cpp
// e.moon 7jun99

#include "ChildNodeView.h"

#include "MediaRoutingContainerView.h"
#include "MediaRoutingView.h"
#include "Wire.h"

#include "Transport.h"
#include "LayoutDefs.h"

#include <MediaRoster.h>
#include <MenuItem.h>
#include <Region.h>

#include "debug_tools.h"

// -------------------------------------------------------- //
// ctor/dtor
// -------------------------------------------------------- //

ChildNodeView::~ChildNodeView() {
	// release (though at this point it's likely too late -- 
	// the routing view is needed)
	releaseNode();
		
	// release transport
	if(m_pActiveTransport)
		m_pActiveTransport->release();
}

ChildNodeView::ChildNodeView(
	MediaRoutingView* pRoutingView, const media_node& node) :
	LiveNodeView(pRoutingView, node),
	m_nodeReleased(false),
	m_pActiveTransport(0) {}

// -------------------------------------------------------- //
// hooks
// -------------------------------------------------------- //

void ChildNodeView::populateMenu() {
	_inherited::populateMenu();
	BMessage* pMsg;
	
	pMsg = new BMessage(MediaRoutingView::M_RELEASE_NODE);
	pMsg->AddInt32("nodeID", node().node);
	m_pMenu->AddItem(new BMenuItem("Release", pMsg));
}

uint32 ChildNodeView::gridRowsNeeded() const {
	return _inherited::gridRowsNeeded();
}

// handle resize
void ChildNodeView::FrameResized(float width, float height) {
	_inherited::FrameResized(width, height);

	// calc transport-icon frame
	// (currently not dependant on frame height, but it may
	//  be someday)
	
	BRect leftR, rightR;
	BPoint offset;
	getJackPositions(leftR, rightR, offset);
	
	m_transportIconFrame = rightR;
	m_transportIconFrame.left -=
		(TRANSPORT_ICON_WIDTH + TRANSPORT_ICON_PAD_LEFT);
	m_transportIconFrame.right = m_transportIconFrame.left +
		TRANSPORT_ICON_WIDTH;
}


// transport-icon click handler
void ChildNodeView::MouseDown(BPoint point) {

	uint32 buttons;
	GetMouse(&point, &buttons, false);

	if(buttons & B_PRIMARY_MOUSE_BUTTON &&
		m_transportIconFrame.Contains(point)) {
		handleTransportClick(point);
	}
	else
		_inherited::MouseDown(point);
}

void ChildNodeView::handleTransportClick(BPoint point) {
	if(m_pActiveTransport) {
		// toggle transport
		if(m_pActiveTransport->isRunning())
			m_pActiveTransport->stop();
		else
			m_pActiveTransport->start();
		
		// invalidate all nodes controlled by this transport
		m_pRoutingView->invalidateTransportIcons(m_pActiveTransport);
	}
}

void ChildNodeView::drawCell(
	BView* v, BRect updateRect, bool bDrawBackground) {

	// mask out the area covered by the transport icon
	v->PushState();
	BRegion region;
	v->GetClippingRegion(&region);
	region.Exclude(m_transportIconFrame);
	v->ConstrainClippingRegion(&region);
	
	// draw the cell
	_inherited::drawCell(v, updateRect, bDrawBackground);
	
	// draw the transport icon
	v->PopState();
	drawTransportIcon(v, m_transportIconFrame);
	
	// released? gray it out:
	if(m_nodeReleased) {
		v->SetHighColor(64, 64, 64, 255);
		v->SetDrawingMode(B_OP_SUBTRACT);
		v->FillRect(updateRect);
	}
}

// draw the transport icon
// [empty implementation in the base class]
void ChildNodeView::drawTransportIcon(BView* v, BRect frame) {
	v->SetHighColor(m_borderColor);
	v->StrokeRect(frame);

	if(m_pActiveTransport)
		v->SetLowColor(m_backColorActive);
	else
		v->SetLowColor(m_backColorInactive);
	frame.InsetBy(1.0,1.0);
	v->FillRect(frame, B_SOLID_LOW);
	
	// draw icon contents
	if(m_pActiveTransport) {
		BRect iconR = frame;
		
		if(m_pActiveTransport->isRunning()) {
			// 'stop'
			iconR.InsetBy(2.0, 2.0);
			v->FillRect(iconR);
		} else {
			// 'play'
			iconR.top += 1;
			iconR.bottom -= 1;
			iconR.left += 2;
			iconR.right -= 2;
//			PRINT(( "play icon (%.1f,%.1f,%.1f,%.1f)\n",
//				iconR.left, iconR.top, iconR.right, iconR.bottom));
			float mid_y_floor = iconR.top + floor(iconR.Height()/2.0);
			float mid_y_ceil = iconR.top + ceil(iconR.Height()/2.0);
//			BPoint deltaP(0.0, mid_y_ceil - mid_y_floor);
			
			v->FillTriangle(
				iconR.LeftTop(),
				BPoint(iconR.right, mid_y_floor),
				BPoint(iconR.left, mid_y_floor));
			v->FillTriangle(
				BPoint(iconR.left, mid_y_ceil),
				BPoint(iconR.right, mid_y_ceil),
				iconR.LeftBottom());
		}
	}
	
	// add a very slight 3D highlight
	rgb_color delta = {16, 16, 16, 255};
	v->SetHighColor(delta);

	v->SetDrawingMode(B_OP_SUBTRACT);
	v->StrokeLine(frame.LeftTop(), frame.RightTop()+BPoint(-1.0, 0.0));
	v->StrokeLine(frame.LeftTop(), frame.LeftBottom()+BPoint(0.0, -1.0));

	v->SetDrawingMode(B_OP_ADD);
	v->StrokeLine(frame.RightTop()+BPoint(0.0,1.0),
		frame.RightBottom());
	v->StrokeLine(frame.LeftBottom()+BPoint(1.0,0.0),
		frame.RightBottom());

	v->SetDrawingMode(B_OP_COPY);
}

// figure colors
void ChildNodeView::prepareColors() {
	_inherited::prepareColors();

	m_backColorInactive = m_cellColorD1;
	m_backColorActive = m_cellColorL2;
	m_borderColor = m_cellColorD2;
}


// -------------------------------------------------------- //
// operations
// -------------------------------------------------------- //

void ChildNodeView::releaseNode() {
	PRINT((
		"ChildNodeView(%s)::releaseNode()\n", nodeInfo().name));
	if(m_nodeReleased) {
		PRINT(("* already released\n"));
		return;
	}

	if(m_controlPanel.IsValid()) {
		// ask control panel to close
		// +++++ should this be synchronous?
		m_controlPanel.SendMessage(B_QUIT_REQUESTED);
		
		// set to default (invalid) messenger instance
		m_controlPanel = BMessenger();
	}

//	// tear down transport
//	vector<media_node_id> nodesInTransport;
//	if(m_pActiveTransport) {
//		m_pActiveTransport->listNodes(nodesInTransport);
//		m_pRoutingView->removeTransportRefs(m_pActiveTransport);
//		ASSERT(!m_pActiveTransport);
//	}
	
	BMediaRoster* pR = BMediaRoster::Roster();
	status_t err;

	// disconnect inputs
	vector<media_input>& inv = connectedInputs();
	for(int n = 0; n < inv.size(); n++) {
		// get source node from routing view
		Wire* pWire = m_pRoutingView->findWire(inv[n].source, inv[n].destination);
		if(!pWire) {
			PRINT((
				"- no wire for connected input '%s'\n", inv[n].name));
			continue;
		}
		ASSERT(pWire->input().destination == inv[n].destination);
		
		// disconnect
		err = pR->Disconnect(
			pWire->output().node.node,
			pWire->output().source,
			node().node,
			inv[n].destination);
		if(err < B_OK) {
			PRINT((
				"! input Disconnect() failed: %s\n", strerror(err)));
		}
	}
	
	// disconnect outputs
	vector<media_output>& outv = connectedOutputs();
	for(int n = 0; n < outv.size(); n++) {
		// get source node from routing view
		Wire* pWire = m_pRoutingView->findWire(outv[n].source, outv[n].destination);
		if(!pWire) {
			PRINT((
				"- no wire for connected input '%s'\n", outv[n].name));
			continue;
		}
		ASSERT(pWire->output().source == outv[n].source);
		
		// disconnect
		err = pR->Disconnect(
			node().node,
			outv[n].source,
			pWire->input().node.node,
			pWire->input().destination);
		if(err < B_OK) {
			PRINT((
				"! output Disconnect() failed: %s\n", strerror(err)));
		}
	}

	pR->ReleaseNode(node());
	m_nodeReleased = true;

//	// reconstruct transport(s)
//	for(int n = 0; n < nodesInTransport.size(); n++) {
//	
//		// skip my ex-node
//		if(nodesInTransport[n] == m_node.node)
//			continue;
//			
//		// fetch separate transport for each node, since the removal
//		// of my node may have 'fractured' the transport
//		
//		ChildNodeView* pChild = dynamic_cast<ChildNodeView*>(
//			m_pRoutingView->nodeViewFor(nodesInTransport[n]));
//		ASSERT(pChild);
//		
//		Transport* pTransport = Transport::GetTransportFor(
//			m_pRoutingView, nodesInTransport[n]);
//		ASSERT(pTransport);
//		
//		ASSERT(!pChild->activeTransport());
//		pChild->setActiveTransport(pTransport);
//		
//		// release implicit reference to the transport
//		pTransport->release();
//	}
	
	PRINT((
		"DONE: ChildNodeView(%s)::releaseNode()\n", nodeInfo().name));
	Transport::DumpAll();
}

void ChildNodeView::setActiveTransport(Transport* pTransport) {
	PRINT((
		"ChildNodeView (%s) ::setActiveTransport()\n",
		nodeInfo().name));

	if(m_pActiveTransport == pTransport)
		return;
	m_pActiveTransport = pTransport;
	Invalidate(m_transportIconFrame);
}

// 8jun99: redraw transport-icon
void ChildNodeView::invalidateTransportIcon() {
	Invalidate(m_transportIconFrame);
}

// -------------------------------------------------------- //
// BView impl
// -------------------------------------------------------- //

void ChildNodeView::DetachedFromWindow() {
	// release
	releaseNode();
	_inherited::DetachedFromWindow();
}

// END -- ChildNodeView.cpp --