#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>

#include "oidseq.h"
#include "structfill.h"

Assoc::Assoc(const char *oidstr,Assoc *nxt):next(nxt){
  assert(oid=new BerOid(NOCONSTCAST oidstr,strlen(oidstr)));
  assert(str=strdup(oidstr));
}

Assoc::Assoc(const char *oidstr,BerOid *newone,Assoc *nxt):next(nxt),
  oid(newone){
  assert(str=strdup(oidstr));
  assert(oid);
}

OidSeq::OidSeq(TableEntry *table):index(NULL),seq(NULL){
  for(;table;table=table->next)
    append(table->oidstr);
}

void OidSeq::remove(const char *oidstr){
  assert(index); /* trying to delete from an empty list */
  if(!strcmp(oidstr,index->str)){
    Assoc *nextone=index->next;
    delete index;
    index=nextone;
  } else {
    char test=0;
    for(Assoc *cur=index;cur->next;cur=cur->next){
      if(*cur->next==oidstr){
	Assoc *doomed=cur->next;
	cur->next=doomed->next;
	delete doomed;
	test=1;
	break;
      }
    }
    assert(test); /* item being deleted doesn't exist on list */
  }
  assert(seq); /* should never happen -- problem with seq and 
		  index being out of sync */

//   char buf[10240];
//   print(buf,10240);
//   fprintf(stderr,"%s\n",buf);
  
  BerOid standard(NOCONSTCAST oidstr,strlen(oidstr));
  BerBase *testcase;
  unsigned int i=0;
  while(testcase=seq->peek(i)){
    testcase=((BerSequence*)testcase)->peek(0); //move into the oid
    if(*(BerOid*)testcase==standard){
      seq->extract(i);
//       memset(buf,0,10240);
//       print(buf,10240);
//       fprintf(stderr,"i=%d\n%s\n",i,buf);
      return;
    }
    i++;
  }
  assert(0); /* oid wasn't found */
}

void OidSeq::append(const char *oidstr){
    assert(index=new Assoc(oidstr,index));
    if(seq==NULL)
      seq=new BerSequence(SEQUENCE_TAG,1,
			  new BerSequence(SEQUENCE_TAG,2,index->oid,
					  new BerNull()));
    else 
      seq->append(1,new BerSequence(SEQUENCE_TAG,2,index->oid,new BerNull()));
}

void OidSeq::append(CONST char *oidstr, Tags type, void *data,
		    unsigned int len){
  assert(index=new Assoc(oidstr,index));

  BerBase *newone;
  switch(type){
  case STRING_TAG:
    assert(newone=new BerString((char*)data,len));
    break;
  case OID_TAG:
    assert(newone=new BerOid((char*)data,len));
    break;
  case IPADDR_TAG:
    assert(newone=new BerIPAddr((char*)data,len));
    break;
  default:
    assert(0); //bad type passed into append
  }

  if(seq==NULL)
      seq=new BerSequence(SEQUENCE_TAG,1,
			  new BerSequence(SEQUENCE_TAG,2,index->oid,newone));
    else 
      seq->append(1,new BerSequence(SEQUENCE_TAG,2,index->oid,newone));

}



void OidSeq::append(const char *oidstr, Tags type, long data){
  assert(index=new Assoc(oidstr,index));
 
  BerBase *newone;
  assert(type==INT_TAG || type==COUNTER_TAG); /* used the wrong version 
						 of append */
  assert(newone=new BerInt(data));

  // printf("val=%ld\n",((BerInt*)newone)->value());

  if(seq==NULL)
      seq=new BerSequence(SEQUENCE_TAG,1,
			  new BerSequence(SEQUENCE_TAG,2,index->oid,newone));
    else 
      seq->append(1,new BerSequence(SEQUENCE_TAG,2,index->oid,newone));

}

OidSeq::OidSeq(unsigned int num...):index(NULL),seq(NULL){
  va_list oidstrs;
  va_start(oidstrs,num);

  for(;num>0;num--)
    append(va_arg(oidstrs,char*));

  va_end(oidstrs);
}

OidSeq::~OidSeq(){
  delete seq;
  while(index){
    Assoc *tmp=index->next;
    delete index;
    index=tmp;
  }
}

/* the fact that these are implemented using serial searches 
   should not be a problem due to the fact that the list of oids 
   should be rather small. */
BerBase *OidSeq::value(const char *oid){
  for(Assoc *cur=index;cur;cur=cur->next)
    if(!strcmp(oid,cur->str))
      return cur->oid->next;
  return NULL;
}

BerBase *OidSeq::value(BerOid &oid){
  for(Assoc *cur=index;cur;cur=cur->next)
    if(oid==*(cur->oid)) 
      return cur->oid->next;
  return NULL;
}

BerBase *OidSeq::child(const char *oid){ 
  for(Assoc *cur=index;cur;cur=cur->next)
    if(!strncmp(oid,cur->str,strlen(oid))) 
      return cur->oid->next;
  return NULL;
}

BerOid *OidSeq::key(const char *oid){
  for(Assoc *cur=index;cur;cur=cur->next)
    if(!strcmp(oid,cur->str)) 
      return cur->oid;
  return NULL;
}

OidSeq::OidSeq(BerSequence *valseq):seq(valseq),index(NULL){
  for(BerBase *cur=valseq->peek(0);cur!=NULL;cur=cur->next){
    char buf[256]; //max oid length
    assert(cur->type()==SEQUENCE_TAG);
    BerSequence *curseq=(BerSequence*)cur;
    int len;
    assert(curseq->peek(0)->type()==OID_TAG);
    assert((len=curseq->peek(0)->print(buf,256))!=-1);
    buf[len]=0;
    assert(index=new Assoc(buf,(BerOid*)curseq->peek(0),index));
  }
}


syntax highlighted by Code2HTML, v. 0.9.1