#include "contigEditor.h"
#include <stdio.h>
#include <stdlib.h>
#include "tagUtils.h"
#include "fort.h"

#define COMMENT_HEAD_ID (1)
#define TAG_HEAD_ID (devils_.idbsiz)

int read_tag(tag_id n, tagRecord *t)
{
    readtg_(&devils_.idevt,&n,&t->position,&t->length,&t->comment,&t->type.i,&t->next);
    return 0;
}
int write_tag(tag_id n, tagRecord t)
{
    writtg_(&devils_.idevt,&n,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
    return 0;
}
int read_comment(comment_id n, int *cnt, commentStruct *c)
{
    readcc_(&devils_.idevc,&n,cnt,&c->next,&c->comment[0],40);
    return 0;
}
int write_comment(comment_id n, int cnt, commentStruct c)
{
    writcc_(&devils_.idevc,&n,&cnt,&c.next,&c.comment[0],40);
    return 0;
}
/* comment interface */
tag_id get_free_tag()
{
    tagRecord head;
    tagRecord freerec;
    tag_id free_id;
    (void) read_tag(TAG_HEAD_ID,&head);
    if (head.next != 0) {
	/*
	** if a free slot somewhere, use it
	*/
	free_id = head.next;
	(void) read_tag(free_id,&freerec);
	head.next = freerec.next;
	(void) write_tag(TAG_HEAD_ID,head);
    } else {
	/*
	** extend comment list file
	*/
	free_id = ++head.position;
	(void) write_tag(TAG_HEAD_ID,head);
	(void) write_tag(free_id,freerec);
    }

    return free_id;
}

void delete_comment (comment_id cp)
{
    int dummy;
    commentStruct head;
    commentStruct freerec;
    comment_id free_id;
    if (!cp) return;
    free_id = cp;
    (void) read_comment(free_id,&dummy,&freerec);
    while (freerec.next) {
	free_id = freerec.next;
        (void) read_comment(free_id,&dummy,&freerec);
    }
    (void) read_comment(COMMENT_HEAD_ID,&dummy,&head);
    freerec.next = head.next;
    (void) write_comment(free_id,dummy,freerec);
    head.next = cp;
    (void) write_comment(COMMENT_HEAD_ID,dummy,head);
}





void blank_tag_rec(tag_id t)
/*
** Blank out fields in tag record t
*/
{

    tagRecord r;

    (void) read_tag(t, &r);

    r.position = 0;
    r.length = 0;
    r.type.i = 0x20202020;
    r.comment = 0;
    r.next = 0;
    
    (void) write_tag(t, r);

}




void delete_tag_rec(tag_id t)
/*
** remove t from file, discarding comment if necessary
*/
{
    tagRecord head;
    tagRecord freerec;

    (void) read_tag(t,&freerec);

    if (freerec.comment)
	delete_comment(freerec.comment);

    (void) read_tag(TAG_HEAD_ID,&head);
    freerec.next = head.next;
    (void) write_tag(t,freerec);
    head.next = t;
    (void) write_tag(TAG_HEAD_ID,head);
    
}

static comment_id get_free_comment()
{
    commentStruct head;
    commentStruct freerec;
    comment_id free_id;
    int count;
    (void) read_comment(COMMENT_HEAD_ID,&count,&head);
    if (head.next != 0) {
	/*
	** if a free slot somewhere, use it
	*/
	free_id = head.next;
	(void) read_comment(free_id,&count,&freerec);
	head.next = freerec.next;
	(void) write_comment(COMMENT_HEAD_ID,count,head);
    } else {
	/*
	** extend comment list file
	*/
	free_id = ++count;
	(void) write_comment(COMMENT_HEAD_ID,free_id,head);
	(void) write_comment(free_id,0,freerec);
    }

    return free_id;
}

comment_id put_comment(char *c)
{
    commentStruct com;
    comment_id cur,next,this_comment;
    size_t clen = strlen(c);
    size_t piece;

    /* write out first block of COMMENT_LENGTH */
    this_comment=cur=get_free_comment();
    if (clen>COMMENT_LENGTH)
	piece = COMMENT_LENGTH;
    else
	piece = clen;

    {int i; for(i=0;i<COMMENT_LENGTH;i++)com.comment[i]=' ';}
    strncpy(com.comment,c,piece);

    c+= piece;
    clen -= piece;
    while (clen > 0) {
	next = get_free_comment();
	com.next = next;
	write_comment(cur,0,com);
	cur = next;
	if (clen<COMMENT_LENGTH)
	    piece = clen;

	{int i; for(i=0;i<COMMENT_LENGTH;i++)com.comment[i]=' ';}
	strncpy(com.comment,c,piece);

	c+= piece;
	clen -= piece;
    }
    com.next = 0;
    if (piece!=COMMENT_LENGTH)
	com.comment[piece]='\0';
    write_comment(cur,0,com);

    return this_comment;
}

static void add_RD_comment(tag_id t_id, char *comment)
{
    tagRecord t;

    readtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
    t.comment = put_comment(comment);
    writtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

}

static void insert_CUT_tags(tag_id t_id, int pos, int length, char *type, char *comment)
{
    tagRecord t,new_t;
    tag_id next_id,new_id;

    new_t.length = 1;
    strncpy(new_t.type.c,type,4);
    new_t.comment = put_comment(comment);

    readtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
    next_id = t.next;
    new_id = get_free_tag();
    new_t.position = pos;
    new_t.next = next_id;
    writtg_(&devils_.idevt,&new_id,&new_t.position,&new_t.length,&new_t.comment,&new_t.type.i,&new_t.next);
    next_id = new_id;
    t.next=next_id;
    writtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

}

static void insert_INS_tags(tag_id t_id, int pos, int length)
{
    tagRecord t,new_t;
    tag_id next_id,new_id;
    int i;

    new_t.length = 1;
    strncpy(new_t.type.c,TAG_TYPE_INSERT,3);
    new_t.type.c[3]='*';
    new_t.comment = 0;

    readtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
    next_id = t.next;
    for (i=length-1;i>=0;i--) {
	new_id = get_free_tag();
	new_t.position = pos+i;
	new_t.next = next_id;
	writtg_(&devils_.idevt,&new_id,&new_t.position,&new_t.length,&new_t.comment,&new_t.type.i,&new_t.next);
	next_id = new_id;
    }
    t.next=next_id;
    writtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

}

static void myFstr2Cstr(char *Fstr, int_fl Flen,
	       char *Cstr, int_fl Clen)
/*
    Copy the significant characters of a blank padded Fortran string
    to a '\0' terminated C string, ignoring excess characters.

    This function works if the strings are distinct or coincident, but
    not if they overlap in any other way.
*/
{   int_fl FsigLen, i;

    /* Find the significant length of Fstr */
    FsigLen=Flen;
    while ((FsigLen > 0) && (Fstr[FsigLen-1] == ' '))
    {   FsigLen--;
    }

    /* Copy up to (Clen-1) significant characters */
    i=0;
    while ((i < FsigLen) && (i < (Clen-1)))
    {   Cstr[i] = Fstr[i];
        i++;
    }

    Cstr[i] = '\0';
}

/* C version of ENTRD */
void entrd_(int_f *IDEVG,int_f *IDEVT,int_f *IDEVC,char *NAMARC,int_f *NGEL,
       int_f *IOK, int_fl l_NAMARC)
{
/*
    char NAMFIL[FILE_NAME_LENGTH], MTYPE[4];
    char namfil[FILE_NAME_LENGTH+1], mtype[5];
*/
    int LENR,LCUT,LENW;
    int RCUT;

#define fn_len 100
    char fn[fn_len];
#define l_line 100
    char line[l_line];
    char *result;
    char *cutbuff,*cb,*l;

    FILE *fp;

    /* blank tag rec */
    blank_tag_rec((tag_id) *NGEL);

    myFstr2Cstr(NAMARC,l_NAMARC,fn,(int_fl)fn_len);

    *IOK = 0;
    /* don't need this anymore
    saveState.idevt  = &devils_.idevt;
    saveState.idevc  = &devils_.idevc;
    saveState.idbsiz = &devils_.idbsiz;
    */
    /* Enters raw data info into database */
    if ( (fp = fopen(fn,"r")) != NULL ) {
	result = fgets(line,l_line,fp);
	if (result != NULL && line[0] == ';') { 
	    /* process header */
	    sscanf(line,";%6d%6d%6d%*s\n",&LENR,&LCUT,&LENW);
	    RCUT = LENR - LCUT - LENW;
            add_RD_comment(*NGEL, &line[1]);
	    result = fgets(line,l_line,fp);
	}
	if (result != NULL && strlen(line)>1 && strncmp(line,";<",2)==0) {
	    /* process left cut off */
	    /* starting with this line we need to read LCUT ditties */
	    cutbuff = (char *) TAG_MALLOC(LCUT+1);
	    cb = cutbuff;
	    while (result!=NULL && strlen(line)>1 && strncmp(line,";<",2)==0){
		for(l=&line[2];*l;l++)if(strchr(" \n\t",*l)==NULL)*cb++=*l;
		result = fgets(line,l_line,fp);
	    }
	    *cb='\0';
            insert_CUT_tags(*NGEL, 0, 0, TAG_TYPE_LCUT, cutbuff);
	    TAG_FREE(cutbuff);
	}

	if (result != NULL && strlen(line)>1 && strncmp(line,";>",2)==0) {
	    /* process right cut off */
	    /* starting with this line we need to read RCUT ditties */
	    cutbuff = (char *) TAG_MALLOC(RCUT+1);
	    cb = cutbuff;
	    while (result != NULL && strlen(line)>1 && strncmp(line,";>",2)==0) {
		for(l=&line[2];*l;l++)if(strchr(" \n\t",*l)==NULL)*cb++=*l;
		result = fgets(line,l_line,fp);
	    }
	    *cb='\0';
            insert_CUT_tags(*NGEL, 0, 0, TAG_TYPE_RCUT, cutbuff);
	    TAG_FREE(cutbuff);
	}

	fclose(fp);
    }
}

int_f freecc_ (int_f *IDEVC)
{
    return get_free_comment();
}

void padtag_(int_f *llino_, int_f *k_, int_f *nc_, int_f *lngthg_)
/*
** Play around with tags when auto assembling
*/
{
    int seq;
    int pos;
    int length;
    int seq_length;
    tagRecord t;
    tag_id last,next;
    int last_pos;
    if (devils_.idevt>0) {
	/* fudge */
	/* don't need this anymore
	saveState.idevt  = &devils_.idevt;
	saveState.idbsiz = &devils_.idbsiz;
	*/
	seq = *llino_;
	length = *nc_;
	if (*lngthg_ < 0) {
	    seq_length = - *lngthg_;
	    pos = seq_length - *k_ - *nc_ + 2;
	} else {
	    seq_length = *lngthg_;
	    pos = *k_;
	}

	next = seq;
	last = 0;
	last_pos = 0;
	while (next) {
	    readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    /*
	    ** Move tags accordingly
	    */
	    if (t.position >= pos)
	    {
		t.position+=length;
		writtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    }
	    else if (t.position + t.length > pos)
	    {
		t.length+=length;
		writtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    }

	    /*
	    ** Insert *INS tags
	    */
	    if (last_pos < pos && t.position >= pos)
		/*
		** Insert after last `length' tags
		*/
		insert_INS_tags(last,pos,length);
	    else if (t.position < pos && t.next==0)
		/*
		** Insert after next `length' tags
		*/
		insert_INS_tags(next,pos,length);

            last = next;
	    last_pos = t.position;
	    next = t.next;
	}

    }
}

void taggel_(int_f *ngels_, int_f *lngthg_, char *gel_, int_fl gel_length)
{
    int_f length = abs(*lngthg_);
    int_f one=1;
    int_f i,j;
    
    if (*lngthg_ > 0) {
	for (i=0,j=1; i<length; i++,j++)
	    if (gel_[i]=='*')
		padtag_(ngels_,&j,&one,lngthg_);
    } else {
	for (i=length-1,j=length; i>=0; i--,j--)
	    if (gel_[i]=='*')
		padtag_(ngels_,&j,&one,lngthg_);
    }

}

void movtag_ (int_f *from, int_f *to )
/*
** Move tag information of gel ``from'' to gel ``to'',
** and perform garbage collection on old gel ``to''
*/
{

    tagRecord freerec;
    tag_id this,next;

    /* don't do anything if tag files aren't open */
    if ( devils_.idevc < 0 || devils_.idevt < 0 ) return;

    /* Throw away ``to'' tag records */
    (void) read_tag((tag_id) *to,&freerec);

    if (freerec.comment)
	delete_comment(freerec.comment);

    next = freerec.next;
    while ( next ) {
        this = next;
	(void) read_tag( this , &freerec );
        next = freerec.next;
        delete_tag_rec (this);
    }

    /* copy ``from'' record to ``to'' record */
    (void) read_tag((tag_id) *from,&freerec);
    (void) write_tag((tag_id) *to,freerec);

    /* blank out old tag rec */
    blank_tag_rec(*from);

}

char *get_comment(comment_id cp)
{
    int dummy;
    commentStruct c;
    int count;
    comment_id nc;
    char *com,*comptr;

    if (!cp) return NULL;
    /* determine how long string is */
    count = 1;
    nc=cp;
    read_comment(nc, &dummy, &c);
    while (c.next != 0) {
	nc = c.next;
	count++;
        read_comment(nc, &dummy, &c);
    }

    com = comptr = (char *)TAG_MALLOC(count * COMMENT_LENGTH+1);
    nc=cp;
    read_comment(nc, &dummy, &c);
    strncpy(com,c.comment,COMMENT_LENGTH); com+=COMMENT_LENGTH;
    while (c.next != 0) {
	nc = c.next;
	count++;
        read_comment(nc, &dummy, &c);
        strncpy(com,c.comment,COMMENT_LENGTH); com+=COMMENT_LENGTH;
    }
    *com = '\0';

    return comptr;
    
}

void getext_(int_f *gel, char *cutoff, int_f *lcutoff, int_f *ok,
	     int_fl l_cutoff)
/*
** Get right cutoff for lowly Fortran Users
*/
{

    tagRecord rec;
    tag_id next;

    (void) read_tag((tag_id) *gel,&rec);

    next = rec.next;

    while ( next && ! rec.position ) {
	(void) read_tag( next , &rec );
        next = rec.next;

	if (strncmp(rec.type.c,TAG_TYPE_RCUT,4) == 0) {
	    if (rec.comment) {
	        char *c;
		int i;
		/* use this */
		c = get_comment(rec.comment);
		/* copy to fortran array */
		for (i=0; i<*lcutoff && c[i]; i++) cutoff[i] = c[i];
		/* set length of returned string */
		*lcutoff = i;
		for (; i<*lcutoff; i++) cutoff[i] = ' ';

		/* rec temporary string space */
		TAG_FREE(c);
		*ok = 0;
		return;
	    } else {
		*ok = 1;
		return;
	    }

	}
    }

    *ok = 1;
}











/*************************************************************/
/**      Routines for Rodger's pad shuffling                 */
/*************************************************************/

static void insert_edit_tag(tag_id t_id, int pos, char base, char *type)
{
    tagRecord t,new_t;
    tag_id new_id;

    /* read previous tag */
    readtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,
	    &t.type.i,&t.next);

    /* set up new record */
    strncpy(new_t.type.c,type,3);
    new_t.type.c[3]=base;
    new_t.length = 1;
    new_t.comment = 0;
    new_t.position = pos;
    new_t.next = t.next;

    /* write new record */
    new_id = get_free_tag();
    writtg_(&devils_.idevt,&new_id,&new_t.position,&new_t.length,&new_t.comment,&new_t.type.i,&new_t.next);

    /* update previous tag */
    t.next=new_id;
    writtg_(&devils_.idevt,&t_id,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

}



void insedt_(int_f *seq_, int_f *pos_, char *base_, int_fl base_l)
/*
** Insert a tag for a base at position pos in gel
*/
{
    int seq;
    int pos;
    char base;

    tagRecord t;
    tag_id last,next;

    if (devils_.idevt>0) {

	seq = (int) *seq_;
	pos = (int) *pos_;
	base = base_[0];


	/* skip over header */
	last = next = seq;
	readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	next = t.next;
	if (next) readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

	/* adjust lengths of all tags staring before pos */
	while (next && t.position < pos) {
	    if (t.position + t.length - 1 >= pos &&
		t.type.c[0] != '*') {
		t.length++;
		writtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    }
            last = next;
	    next = t.next;
	    if (next) readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

	}

	/* insert a tag after last and before next */
	insert_edit_tag(last,pos,base,TAG_TYPE_INSERT);

	/* shift everything else right */
	while (next) {
	    t.position++;
	    writtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    next = t.next;
	    if (next) readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	}

    }
}





void deledt_(int_f *seq_, int_f *pos_, char *base_, int_fl base_l)
/*
** Delete a base at position pos in gel
*/
{
    int_f seq;
    int_f pos;
    char base;

    tagRecord t;
    tag_id last,next;

    if (devils_.idevt>0) {

	seq = *seq_;
	pos = *pos_;
	base = base_[0];


	/* skip over header */
	last = next = (tag_id)seq;
	readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	next = t.next;
	if (next) readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

	/* adjust lengths of all tags staring before pos */
	while (next && t.position <= pos) {
	    if (t.position + t.length - 1 >= pos &&
		t.type.c[0] != '*') {
		t.length--;
		writtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    }
            last = next;
	    next = t.next;
	    if (next) readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);

	}

	/* insert a tag after last and before next */
	insert_edit_tag(last,pos,base,TAG_TYPE_DELETE);

	/* shift everything else right */
	while (next) {
	    t.position--;
	    writtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	    next = t.next;
	    if (next) readtg_(&devils_.idevt,&next,&t.position,&t.length,&t.comment,&t.type.i,&t.next);
	}

    }
}
