////////////// Demonstration of Typed Data Values //////////////////
//Compile as a project with TYPEDEMO.CPP and TYPES.CPP.  The file 
//TYPES.H must be in the include directory.

#include <string.h>
#include <stdio.h>
#include <types.h>
#include <ctype.h>

class node{ 
	friend class database;
	node* next; value* val;
	node(){next=NULL;val=NULL;}
	node(node* nxt,value* v){next=nxt;val=v;}
	~node(){delete val;}};

class database{
 protected:
	node root;
	int insert(value* val);
 public:
	database();
	~database();
	int assert(char* namestr);
	int assert(char* namestr,int val);
	int assert(char* namestr,long val);
	int assert(char* namestr,char* val);
	void retract(char* namestr);
	value* valueof(char* namestr);
	int count();
	void show();};

class nusval:public sval{ //build on the basic types
  public:
	nusval(char* name,char* val):sval(name,val){};
	datatype type(){return DERIVED;};
	const char* typename(){return "NEW STRING";};
	int asint(){
		if(isdigit(data[0])) return sval::asint();
		else return strcmp(data,"THREE")? 0:3;
		};
	};


///////Code for the database
//enter values into the database
database::database(){root=node();};
database::~database(){
	node *dad,*son=root.next;
	while(son){dad=son;son=son->next;delete dad;}
	}

int database::assert(char* namestr){
	retract(namestr);
	value* tempptr=(value*)new value(namestr);
	if (!tempptr) return 0;
	if (!tempptr->valid()) {delete tempptr; return 0;}
	insert(tempptr); return 1;}
int database::assert(char* namestr,int val){
	retract(namestr);
	value* tempptr=(value*)new ival(namestr,val);
	if (!tempptr) return 0;
	if (!tempptr->valid()) {
		delete tempptr; 
		return 0;}
	insert(tempptr); return 1;}
int database::assert(char* namestr,long val){
	retract(namestr);
	value* tempptr=(value*)new lval(namestr,val);
	if (!tempptr) return 0;
	if (!tempptr->valid()) {delete tempptr; return 0;}
	insert(tempptr); return 1;}
int database::assert(char* namestr,char* val){
	retract(namestr);
	value* tempptr=(value*)new nusval(namestr,val);
	if (!tempptr) return 0;
	if (!tempptr->valid()) {delete tempptr; return 0;}
	insert(tempptr); return 1;}

int database::insert(value* newval){
	node *n=&root;
	while(n->next && strcmp(newval->name(),n->next->val->name())>0){
		n=n->next;}
	node* newnode=new node(n->next,newval); if(!newnode) return 0;
	n->next=newnode;
	return 1;}

//remove a specific value from the database
void database::retract(char* namestr){	if(!namestr) return;
	node *vdead,*v=&root;
	while(v->next && strcmp(namestr,v->next->val->name())){
		v=v->next;}
	vdead=v->next;
	if(vdead){v->next=vdead->next; delete vdead;}}

//retrieve pointer to value associated with a specific nametag
value* database::valueof(char* namestr){
	node *v=root.next;
	while(v && strcmp(namestr,v->val->name())){v=v->next;};
	return v->val;}

//return count of values asserted into the database
int database::count(){
	int cnt=0;
	for(node* v=root.next;v;v=v->next){cnt++;};
	return cnt;};

//display the database
void database::show(){
	int N=count();
	printf("Database contains %d value%s:\n",N,(N==1)? "":"s");
	for(node* v=root.next;v;v=v->next){
		printf("\t%s=\t%s[%s]\n",
				v->val->name(),v->val->asstring(),v->val->typename());}
	printf("\n");
	};

database* db;
int dbchg=0;
int N=12345;
long L=1234567890;
char S[]="Now is the time for all good programmers...";

void addsamples(){
	db->assert("int",N);
	db->assert("long",L);
	db->assert("string",S);
	db->assert("number","THREE");
	db->assert("username","Superman");
	db->assert("weakness","Kryptonite");
	dbchg=1;
	printf("Sample values added.\n");}

void reportval(char* name){
	value* val=db->valueof(name);
	if (val){printf("%s=%s (as int=%d)\n",name,val->asstring(),val->asint());}
	else {printf("No value has been entered for <%s>.\n",name);}}

void deleteval(char* name){
	value* val=db->valueof(name);
	if (val){
		db->retract(name);dbchg=1;
		printf("<%s> deleted.\n",name);}
	else {printf("No value has been entered for <%s>.\n",name);}}

int qmark(char* name){//Is there a question mark? If so, removes it.
	int lastch=strlen(name)-1;
	if(name[lastch]!='?') return 0;
	name[lastch]=0; return 1;}

//The following function returns args like the command line
//global values to ease conversion to argc and argv[] later
char argline[80], *argptr, arg[3][80], delims[]=" \t,";
int argcnt;
int getargs(){
	printf("Action?  "); gets(argline);	//get user input
	argptr=strtok(argline,delims);	//initialize parsing function
	for(argcnt=1; argcnt<3&&argptr; argcnt++) {
		strcpy(arg[argcnt],argptr);
		argptr=strtok(NULL,delims);}
	return argcnt>1;}

void main(){
	printf("Database test begins-----\n");
	db=new database();


	while(getargs()){
		char* name=arg[1]; char* valstr=(argcnt>2)? arg[2]:"";
		if(strcmp(name,"/samples")==0)	{addsamples();}
		else if(strcmp(name,"/show")==0) {db->show();}
		else if(qmark(name))					{reportval(name);}
		else if(name[0]=='-')				{deleteval(name+1);}
		else if(strlen(valstr)==0)			{reportval(name);}
		else {db->assert(name,valstr); dbchg=1;
			printf("<%s> added.\n",name);};
		if(dbchg) {db->show(); dbchg=0;}
		}
	printf("Test complete.\n\n");}
