#include <string.h>
#include <stream.h>
#include <ctype.h>
#include "shared.h"
#include "dspe_app.h"
#include "cgidbg.h"
#include "editnet.h"
#include "intfc.h"
#include "net.h"
#include "mpacket.h"
#include "shared.h"
#include "remcom.h"
#include "outtok.h"
#include "netcnt.h"
#include "baseio.h"
#include "dspguicom.h"
#include "yacintfc.h"
#include "mkstr.h"

const int DspApplication::max_name_length = 60 ;

static void do_create_network()
{
	// create network
	InteractiveEntity * net_class = TheNodes->Find("Network");
	if (!net_class) DbgError("::do_create_network","no network class");
	OutTokens Out(OutputPrompt,0,""," ","",78);
	Network * net = 0 ;
	net = (Network *) net_class->InteractiveCreateDefault(Out);
	if (!net) DbgError("::do_create_network","cannot create network");
	
	// create controller
	net->CreateController();
	NetControl * controller = net->GetTheController();
	if (!controller) DbgError("::do_create_network","no controller");

	// check total length
	const char * net_name = net->GetName();

	const char * controller_name = controller->GetName();
	int controller_length = strlen(controller_name) ;
	int buf_size = strlen(net_name) + controller_length + 2 ;
	if (buf_size > MaxPacketSize) DbgError("::do_create_network",
		"names too long");

	// transmit message
	char * buf = new char[buf_size];
	strcpy(buf,controller_name);
	strcpy(buf + controller_length + 1, net_name);
	PacketHeader head(PacketNetworkEdit,CppEnums::created_network,buf_size);
	WriteSeg->WritePacket(head,buf);
	net->set_displayed();
	delete buf ;
}

static InteractiveEntity * is_object_in_class(const char * obj,
	const char * cls)
{
	InteractiveEntity * TheEntity =
		AllEntityLists->GetInteractiveEntityFromObj(obj);
	if (!TheEntity) {
		// LogOut << "is_object_in_class - not an interactive entity\n" ;
		return 0 ;
	}
	int in_class = TheEntity->IsClassMember(cls);
/*
 *	if (!in_class) LogOut << "is_object_in_class - `" << obj << "' is not a `"
 *		<< cls << "'.\n" ;
 */
	if (!in_class) return 0 ;
	return TheEntity ;
}

static void add_unlinked_node(const char * data, int size)
{
	const char * net_name = data ;
	const char * node_name = data + strlen(data) + 1 ;
	// LogOut<<"add_unlinked_node(" << net_name << ", " << node_name << ")\n" ;
	if (strlen(net_name) + strlen(node_name) + 2 != size) {
		TheLog << "add_unlinked_node, bad size\n"  ;
		return ;
	}
	InteractiveEntity * ent = is_object_in_class(net_name,"Network");
	// LogOut << "is_object_in_class returned " << (void *) ent << "\n" ;
	if (!ent) return ;
	Network * net = (Network *) ent->GetObject(net_name) ;
	if (!net) {
		// LogOut << "Cannot find network `" << net_name << "'.\n" ;
		return ;
	}
	ent = is_object_in_class(node_name,"Node");
	// LogOut << "is_object_in_class  2 returned " << (void *) ent << "\n" ;
	if (!ent) return ;
	Node * node = (Node *) ent->GetObject(node_name) ; 
	if (!node) {
		// LogOut << "Cannot find node `" << node_name << "'.\n" ;
		return ;
	}
	net->add_unlinked_node(node);
	// LogOut << "added node\n" ;
}

static void do_clear_active_net(const char * data, int size)
{
	if (size != strlen(data) + 1) {
		// LogOut << "do_clear_active_net, bad size\n" ;
		return ;
	}
	InteractiveEntity * ent = is_object_in_class(data,"Node");
	if(!ent) {
		// LogOut << "do_clear_active_net, bad size, not a node\n" ;
		return ;
	}
	Node * node = (Node *) ent->GetObject(data);
	if (!node)  {
		// LogOut << "do_clear_active_net, bad size, cannot get object\n" ;
		return ;
	}
	if (!node->CheckLinked()) {
		ProcessNet * net = node->GetActiveNet();
		if (!net) return ;
		node->clear_thread();
		net->remove_unlinked_node(node);
		node->SetActiveNet(0);
	}
	// else LogOut << "do_clear_active_net, node is linked\n" ;
}

void DspApplication::network_edit(int id, const char * data, int size)
{
	switch(id) {
case CppEnums::clear_active_net:
		do_clear_active_net(data,size);
		break ;
case CppEnums::add_unlinked_node_to_network:
		add_unlinked_node(data,size);
		break ;
case CppEnums::create_network:
		do_create_network();
		break ;
default:
		DbgError("DspApplication::network_edit","bad case");
	}
	
}

static int make_target_mode = 0 ;

int DspApplication::make_target_mode() {return ::make_target_mode;}

void DspApplication::set_make_target_mode()
{
	::make_target_mode = 1 ;
}

void DspApplication::clear_make_target_mode()
{
	::make_target_mode = 0 ;
}

static node_trace = 0 ;

void DspApplication::node_trace_on()
{
	node_trace = 1 ;
}

void DspApplication::node_trace_off()
{
	node_trace = 0 ;
}

int DspApplication::is_trace()
{
	return node_trace ;
}

static int heap_check = 0 ;

void DspApplication::heap_check_on()
{
	heap_check = 1 ;
	node_trace_on();
}

void DspApplication::heap_check_off()
{
	heap_check = 0 ;
}

int DspApplication::is_heap()
{
	return heap_check ;
}

static int report_limit = 400 ;

void DspApplication::set_report_overflow_limit()
{
	for(;;) {
		*Output + OutputPrompt << DspGuiCommon::default_prefix <<
			report_limit << ") or a new limit on the numbe\nr" <<
			"of overflow  before reporting them in the information window:\n" ;
		char * RawBuf = GetRawBufLine(InputPrompt) ;
		if (RawBuf[0] == '\03') {
			State.Error("user aborted input");
			return ;
		}
		istrstream Temp(RawBuf,BufSize-1);
		int32 limit = -1 ;
		Temp >> limit ;
		if (Temp.bad() || limit < 1) {
			*Output + OutputPrompt <<
				"Invalid integer value `" <<
				RawBuf << "'.\n" ;
		} else {
			report_limit = limit ;
			return ;
		}

	}
}

int DspApplication::overflow_check_limit()
{
	return report_limit ;
}


const char * DspApplication::explain_legal_object_name()
{
	static const char * ret = 0 ;
	char buf[32];
	sprintf(buf,"%d", max_name_length+1);
	if (!ret) ret = Concatenate(
		"A name must start with a letter, contain only letters,\n",
		"digits or `_' and be shorter then ",
		buf, " characters");
	return ret ;
}
static int myisalpha(int c)
{
	if (c < 'A') return 0 ;
	if (c > 'z') return 0 ;
	if (c > 'Z' && c < 'a') return 0 ;
	return 1 ;
}

static int myisalpha_(int c)
{
	if (c == '_') return 1 ;
	return myisalpha(c);
}

static myisalnum(int c)
{
	if (c >= '0' && c <= '9') return 1 ;
	return myisalpha_(c);
}

int DspApplication::valid_object_name(const char * name)
{
    if (!name) return 0 ;
    if (!*name) return 0 ;
    if (!myisalpha(*name)) return 0 ;
	int length = 0 ;
    for (const char * pt = name+1; *pt;pt++) {
		if (++length > max_name_length) return 0 ;
        if (myisalnum(*pt)) continue ;
        return 0 ;
    }
    return 1 ;
}



ErrCode DspApplication::send_gui(CppEnums::edit_network cmd,
	const char * a_name,
	const char *b_name, int a_length)
{
	if (!State.IsX())  return OK ;
	if (State.IsError()) return OK ;

	int a_error = 0 ;

	if (!a_name) a_error = 1;
	else if (a_length < 0) a_length = strlen(a_name) + 1;

	if (a_length < 2) a_error = 1 ;
	else if (a_name[a_length-1]) a_error = 1 ;

	if (a_error) DbgError("DspApplication::send_gui","null a");
	
	int b_length = 0 ;
	if (b_name) b_length = strlen(b_name) + 1 ;
	int buf_size = a_length + b_length ;

	if (buf_size > MaxPacketSize) return FatalError ;
	char * buf = new char[buf_size];
	for (int i = 0 ; i < a_length; i++) buf[i] = a_name[i] ;
	strcpy(buf + a_length,b_name);
	PacketHeader head(PacketNetworkEdit,cmd, buf_size);
	WriteSeg->WritePacket(head,buf);
	delete buf ;
	return OK;
}

int DspApplication::read_variable(const char * buf, double &value)
{
    char Buf[BufSize] ;
    istrstream Temp((char *)buf,BufSize-1);
    Temp >> Buf;
    if ( Temp.bad() ) {
        *Output + OutputPrompt << "Invalid C++ string input: `" <<
            buf << "'.\n" ;
        return -1 ;
    } 
	if(!myisalpha(Buf[0])) return 0 ;
    // UserEntity * entity = AllEntityLists->GetObject(Buf);
    ValueType * value_type = AllEntityLists->find_obj(Buf);
    if (!value_type) {
        *Output + OutputPrompt << "There is no variable `" << Buf <<
            "'.\n";
        return -1 ;
    }
	DataValue& Val = value_type->Value ;
	switch (value_type->Type) {
case DecFloat:
    	value = Val.ValFloat ;
		return 1 ;
case DecInt:
    	value = Val.ValInt ;
		return 1 ;
case DecString:
case DecName:
case DecDspPP:
case DecParam:
case DecEnt:
case DecProcedure:
case DecComplex:
case DecCxMachWord:
case DecCxAccMachWord:
case DecInvalid:
default:
		*Output + OutputPrompt << "Variable `" << Buf <<
			"' is not a scalar numeric value.\n" ;
     	return -1 ;
case DecMachWord:
    	value = (double) *Val.GetMachWord();
		return 1 ;
case DecAccMachWord:
    	value = (double) *Val.GetAccMachWord();
		return 1 ;
	} 
}

static int32 max_report = 1000 ;
int32 DspApplication::compare_disk_max_report()
{
	return max_report ;
}

void DspApplication::compare_disk_max_report(int32 n)
{
	max_report = n ;
}

static double tolerance = 0.0 ;
double DspApplication::compare_disk_tolerance()
{
	return tolerance ;
}

void DspApplication::compare_disk_tolerance(double t)
{
	tolerance = t ;
}

static const char * error_file = 0 ;
const char * DspApplication::compare_disk_error_file()
{
	return error_file ;
}


void DspApplication::compare_disk_error_file(const char * err)
{
	error_file = err ;
}

static const char * the_OutputNode_replace_directory = 0 ;

void DspApplication::OutputNode_replace_directory(const char * dir)
{
	the_OutputNode_replace_directory = dir ;
}

const char * DspApplication::OutputNode_replace_directory()
{
	return the_OutputNode_replace_directory ;
}



// #define CHECK_NAMES
#ifdef CHECK_NAMES
// Test code that should not normally be compiled in
static void check_names()
{
	static int did_check = 0 ;
	if (did_check) return ;
	did_check = 1 ;
	static const char * test_names[] = {
		"A",
		"&^%$^^%%#@#$%%%#@@",
		"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa",
		"________________________AAAAAAAAAAAAAAAA",
		"Pi",
		"4444444444!!!!!!!!!FFFFFFFFF",
		"dsffdsafidsfjddsfjdsi",
		"abcdef",
		0
	};
	for (const char ** ck = test_names; *ck ;ck++) {
		TheLog << "Orig:`" << *ck << "'\n" ;
		TheLog << "Legal = " << DspApplication::valid_object_name(*ck) << "\n";
		const char * converted = DspApplication::force_to_legal_name(*ck);
		if (converted) TheLog << converted << ", Legal = " << 
			DspApplication::valid_object_name(converted) << "\n" ;
		else TheLog << "NO CHANGE\n" ;
	}
}

#endif


// deletes `existing' if a new name is returned
char * DspApplication::force_to_legal_name_del(char * existing, int force_not_used)
{
	char * ret = force_to_legal_name((const char *) existing, force_not_used) ;
	if (ret) delete existing ; else ret = existing ;
	return ret ;
}

static char * make_new_name(const char * existing)
{
		if (!existing) DbgError("make_new_name","null ptr");
		if (!*existing) DbgError("make_new_name","null base");
		int length = strlen(existing) ;
		const min_length = 8 ;
		int times = 1 ;
		if (length < min_length) times = 1 + ((min_length - 1) / length) ;
		length = times * length ;
		if (length > DspApplication::max_name_length) length =
			DspApplication::max_name_length ;
		char * new_name = new char[length+1] ;
		char * end = new_name + length ;
		for (char * pt = new_name ; pt < end; pt+=length) {
			if (end - pt < length) length = end - pt ;
			strncpy(pt,existing,length);
		}
		*pt = '\0' ;
		return new_name ;
}

static char next_char(char& base)
{
	if (base < 'A') base = 'A' - 1 ;
	if (base <  'Z') return ++base ;
	if (base == 'Z') return '_' ;
	if (base == '_') return 'a' ;
	if (base < 'a') base = 'a' - 1 ;
	if (base < 'z') return ++base ;
	return base = 'A' ;
}


// returns 0 if current name is OK otherwise new name is allocated

char * DspApplication::force_to_legal_name(const char * existing,
	int force_not_used)
{
#ifdef CHECK_NAMES
	check_names();
#endif
    if (!existing) DbgError("DspApplication::force_to_legal_name", "null ptr");
    if (!*existing) DbgError("DspApplication::force_to_legal_name", "null base");
	char * new_name = 0 ;
	const char * to_check = existing ;
	char nxt = 'z' ;
	if (strlen(existing) > max_name_length) to_check = new_name = 
		make_new_name(existing);
	if (!myisalpha(*to_check)) {
		if (!new_name) to_check = new_name = make_new_name(existing);
		*new_name = next_char(nxt) ;
	}
    for (int i = 1 ; to_check[i]; i++) {
		char c = to_check[i]  ;
        if (myisalnum(c)) continue ;
		if (!new_name) to_check = new_name = make_new_name(existing);
		new_name[i] = next_char(nxt) ;
    }
	if (force_not_used) {
		char changing = strlen(to_check)-1;
		char start = to_check[changing] ;
		while(AllEntityLists->CheckNameInUse(to_check) != InUseNo) {
			if (!new_name) to_check = new_name = make_new_name(existing);
			next_char((char&) to_check[changing]);
			if (new_name[changing] == start) if (changing < 1)
				DbgError(" DspApplication::force_to_legal_name",
					"this cannot be");
				else start=new_name[--changing] ;
		}
	}
	if (new_name) return new_name ;
	return 0 ;
}

ProcessNet * to_redisplay = 0 ;

void DspApplication::set_redisplay(ProcessNet * to_display)
{
	if (to_redisplay) if (to_redisplay!= to_display)
		DbgError("DspApplication::set_redisplay","already set");
	to_redisplay = to_display ;
}

void DspApplication::check_redisplay()
{
	if (to_redisplay) if (to_redisplay->need_redisplay())
		to_redisplay->GraphDisplay();
	to_redisplay = 0 ;
}

const char * DspApplication::err_code_names[] = {
	"OK",
	"OutputBuffersFull",
	"Warning",
	"EndOfData",
	"ExecutionComplete",
	"FatalError",
	0
};



static ArithType::ArithTypes this_array[] = {
	ArithType::ArithInt16,
	ArithType::ArithInt32,
	ArithType::ArithFloat,
	ArithType::ArithDouble
};

ArithType::ArithTypes * DspApplication::convert_code = this_array ;

