#include "replc.h"
#include "dfnode.h"
#include "network.h"
#include "netlnk.h"
#include "yacintfc.h"
#include "portable.h"
#include "cgidbg.h"
#include "netcnt.h"

// void TestAlloc(const char * msg);

ReplaceNodeLinks::ReplaceNodeLinks(DfNode& nd):
	node(nd),
	in(nd.GetIn()),
	out(nd.GetOut()),
	inputs(0),
	outputs(0)
{
	if (in) inputs = new ReplaceInLink[in] ;
	if (out) outputs = new ReplaceOutLink[out] ;
	for (int i = 0 ; i < in ; i++) {
		inputs[i].node = 0 ;
		inputs[i].channel_from = -1 ;

		DfNodeOutLink * out_link = node.GetDriverOutputChannel(i);
		if (!out_link) continue ;
		DfNode * driver = out_link->GetDriverNode();
		if (!driver) continue ;

		DfNodeLink * link = out_link->GetOutLink();
		if (!link) continue ;

		DfNodeIn * in_link = link->GetInputLink();
		
		inputs[i].node = driver ;
		inputs[i].channel_from = in_link->GetIndex();
	}
	for (i = 0 ; i < out ; i++) {
		outputs[i].channel_from = -1 ;
		outputs[i].size = 0 ;
		outputs[i].buf_links = 0 ;
		DfNodeOutLink * out_link = node.GetOutLink(i);
		if (!out_link) continue ;
		DfNodeLink * link = out_link->GetOutLink();
		if (!link) continue ;
		int32 size = link->Size();
		if (!size) continue ;
		outputs[i].size = size ;
		outputs[i].buf_links = new ReplaceOutBufferLink[size] ;
		// LogOut << "outputs[" << i << "] is size " << size << "\n" ;
		DfNodeOutList * links = link->GetOutputLinks();
		if (!links) DbgError("ReplaceNodeLinks::ctor",
			"size and links inconsistent");

		DfNodeOut * out ;
		DfNodeOutIterator Next(*links) ;
		for (int j = 0 ; out=Next() ; j++) {
/*
 *			LogOut << "setting output channel " << i << ", buffer channel "
 *				<< j << "\n" ;
 */
			ReplaceOutBufferLink& buf_link = outputs[i].buf_links[j] ;
			buf_link.node = 0 ;
			buf_link.channel_in = -1 ;
			DfNode * driven_node = out->GetNode();
			if (!driven_node) {
				continue ;
			}
			buf_link.node = driven_node ;
			buf_link.channel_in = out->GetIndex();
			// LogOut << "this channel is " << buf_link.channel_in << "\n" ;
		}
		if (j != size) DbgError("ReplaceNodeLinks::ctor",
            "size and links inconsistent end");
	}
}

ReplaceNodeLinks::~ReplaceNodeLinks()
{
	delete inputs ;
	if (outputs) {
		delete outputs->buf_links ;
		delete outputs ;
	}
}

ErrCode ReplaceNodeLinks::LinkNewNode(ProcessNet &Net, DfNode & new_node)
{
	// TestAlloc("ReplaceNodeLinks::LinkNewNode");
/*
 *	LogOut << "Net is " << Net.GetName() << ", node to replace is " <<
 *		node.GetName() << ", new node is " << new_node.GetName() << "\n" ;
 */
	if (new_node.GetIn() != in) {
		State.Error("the replacement has ",
			in > new_node.GetIn() ? "more" : "fewer", " input channels");
		return FatalError ;
	}
	if (new_node.GetOut() != out) {
		State.Error("the replacement has ",
			out > new_node.GetOut() ? "more": "fewer", " output channels");
		return FatalError ;
	}
	if (Net.GetTheController())
		Net.GetTheController()->EditNetwork("replacement node");
	if (State.IsError()) return FatalError ;
	if (!in) Net.remove_signal_node(node);
	if (State.IsError()) return FatalError ;
	node.RemoveAllLinks();
	if (State.IsError()) return FatalError ;
	for (int i = 0 ; i < in ; i++) {
		DfNode * the_node = inputs[i].node ;
		if (!the_node) continue ;
		if (the_node == &node) the_node = &new_node ;
/*
 *		LogOut << "linking " << the_node->GetName() <<
 *				" " << inputs[i].channel_from << " to " << i << "\n" ;
 */
		Net.Link(*the_node,inputs[i].channel_from) >>
			new_node.LinkIn(i);
		// TestAlloc("after_link");
		if (State.IsError()) return FatalError ;
	}
	for (i = 0 ; i < out ; i++) {
		// LogOut << "linking outputs from channel " << i << "\n" ;
		for (int j = 0 ; j <outputs[i].size; j++) {
			if (!outputs[i].buf_links) continue ;
			ReplaceOutBufferLink& buf_link = outputs[i].buf_links[j] ;
			DfNode * the_node = buf_link.node ;
			if (!the_node) continue ;
			if (the_node == &node) the_node = &new_node ;
/*
 *			LogOut << "Linking " << i << " to " << the_node->GetName()
 *				<< " " << buf_link.channel_in << "\n" ;
 */
			Net.Link(new_node,i) >> (*the_node).LinkIn(buf_link.channel_in);
			if (State.IsError()) return FatalError ;
		}
	}
	return OK ;
}
