/*     Bed a menu-driven multi dataformat binary editor for Linux            */
/*     Copyright (C) 1998 Jaap Korthals Altes <jkaltes@hetnet.nl>       */
/*                                                                           */
/*     This program is free software; you can redistribute it and/or modify  */
/*     it under the terms of the GNU General Public License as published by  */
/*     the Free Software Foundation; either version 2 of the License, or     */
/*     (at your option) any later version.                                   */
/*                                                                           */
/*     This program is distributed in the hope that it will be useful,       */
/*     but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
/*     GNU General Public License for more details.                          */
/*                                                                           */
/*     You should have received a copy of the GNU General Public License     */
/*     along with this program; if not, write to the Free Software           */
/*     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             */
/* Fri Dec  8 22:14:27 2000                                                  */

#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
#include "../hassub.h"
#ifdef EDITOR
BEDPLUGIN
class Editor {
public:
#include "faked.h"
int Editor::getanswer(const char *question,char *answ,int anslen,int ansroom=5);
int bitfield(void) ;
int changebitfield(void) ;
};
#else
#endif
#include "bitfields.h"
class BitCon;
	Bitfield::Bitfield(int bits,int bytes,ScreenPart *pa):  Filter(bytes,bytes) {
		label=name=filtername(Bitfield);
		number=1;
		bitnr=new Bitnr[1];
		bytenr=new Bytenr[1];
		bitnr[0]=bits;
		bytenr[0]=bytes;
		calcsizes();
		wantsaway=1;
		part=pa;
		maxlen=0;
		}
	int Bitfield::wantaway(void) {
		return wantsaway;
		}
	Bitfield::Bitfield(int bytes):  Filter(bytes,bytes) {
		maxlen=0;
		label=name=filtername(Bitfield);
		wantsaway=1;
		number=0;	
		calcsizes();
		part=NULL;
		}

//Format: want:fbits:number:b;by,b;by,
	Bitfield::Bitfield(int fb,int sb,char *conv,ScreenPart *pa) : Filter(fb,sb) {
		part=pa;
		maxlen=0;
		char *ptr=conv,*endptr;
		wantsaway=(*ptr++=='0')?0:1;
		if(*ptr++!=':')
			err=1;
		filebits=strtol(ptr, &endptr,16);
		if(ptr==endptr)
			err=2;
		ptr=endptr;
		if(*ptr++!=':')
			err=3;
		number=strtol(ptr, &endptr,16);
		if(ptr==endptr)
			err=4;
		ptr=endptr;
		if(*ptr++!=':')
			err=5;
		bitnr=new Bitnr[number];
		bytenr=new Bytenr[number];
		for(int i=0;i<number;i++) {
			bitnr[i]=strtol(ptr, &endptr,16);
			if(ptr==endptr)
				err=6;
			ptr=endptr;
			if(*ptr++!=';')
				err=7;
			bytenr[i]=strtol(ptr, &endptr,16);
			if(ptr==endptr)
				err=8;
			ptr=endptr;
			ptr++;
			}
		label=name=filtername(Bitfield);
		calcsizes();
		}
	Bitfield::Bitfield(Bitfield &rev): Filter(rev),number(rev.number),filebits(rev.filebits) {

		maxlen=0;
		part=NULL;
		label=rev.label;
		wantsaway=rev.wantsaway;
		bitnr=new Bitnr[number];
		bytenr=new Bytenr[number];
		memcpy(bitnr,rev.bitnr,sizeof(Bitnr)*number);
		memcpy(bytenr,rev.bytenr,sizeof(Bytenr)*number);
		}
/*
beg: bit position of begin (bits)
len: length in bits
tobytes: number of bytes to translate to
returns number of filebytes used
	int Bitfield::addrange(short beg,short len,short tobytes) {
		int frombit;
		int uit;
		Bitnr *newbit ;
		Bytenr *newbyte;
		for(int i=0;i<number;i++) {
			frombit+=bitnr[i];
			if(beg<frombit) {
				int under=frombit-bitnr[i];
				if(bytenr[i])
					return -1;
				uit=2;
				newbit= new Bitnr[number+uit];
				newbyte=new Bytenr[number+uit];
				memmove(newbit,bitnr,sizeof(Bitnr)*i);
				memmove(newbyte,bytenr,sizeof(Bytenr)*i);
				newbit[i++]=beg-under;
				newbit[i]=len;
				newbyte[i]=tobytes;
				memcpy(newbyte+i+1,bytenr+i,sizeof(Bytenr)*(number-i));
				memcpy(newbit+i+1,bitnr+i,sizeof(Bitnr)*(number-i));
				number+=uit;
				bytesscreen+=tobytes;
				return bytesfile;
				}
			}
		if(frombit<beg) {
			uit=2;
			newbit= new Bitnr[number+uit];
			newbyte=new Bytenr[number+uit];
			newbit[number]=beg-frombit;
			newbyte[number]=0;
			}
		else {
			uit=1;
			newbit= new Bitnr[number+uit];
			newbyte=new Bytenr[number+uit];
			}
		memmove(newbit,bitnr,sizeof(Bitnr)*number);
		memmove(newbyte,bytenr,sizeof(Bytenr)*number);
		delete bitnr;
		delete bytenr;
		newbyte[number+uit-1]=tobytes;
		newbit[number+uit-1]=len;
		number+=uit;
		bitnr=newbit;
		bytenr=newbyte;
		bytesscreen+=tobytes;
		filebits=beg+len;
		bytesfile=(filebits+7)/8;
		return bytesfile; 
		}

*/
	char *Bitfield::convstr(void) {
//Format: want:fbits:number:b;by,b;by,
// space bitnr and bytenr times number, space number, space filebits, space spec times count + space zero;
int newlen= 2*(((sizeof(Bitnr)+sizeof(Bytenr))*number)+sizeof(number)+sizeof(filebits))+ number*2+4+1+1;
		if(newlen>maxlen) {
			if(maxlen) {
				delete convstring;
				}
			maxlen=newlen;
			convstring=new char[maxlen];
			}
		int len=snprintf(convstring,maxlen,"%c:%lx:%hx:",(wantsaway?'1':'0'),filebits,number);
		for(int i=0;i<number;i++)
			len+=snprintf(convstring+len,maxlen-len,"%hx;%hx,",bitnr[i],bytenr[i]);
		return convstring;
		}
	Bitfield::~Bitfield() {
		if(number) {
			delete bitnr;
			delete bytenr;
			}
		if(maxlen)
			delete convstring;
		}
	Filter *Bitfield::newone(void) {
		calcsizes();
		return new Bitfield(*this);
		}
	int Bitfield::chbytes(int b) {
//		calcsizes();
		return bytesscreen;
		}
	int Bitfield::chbytesback(int b) {
//			calcsizes();
		return bytesfile;
		}
/*
What to do if pos>number?
*/
	int Bitfield::extendtolen(int newnumber) {
		if(newnumber<=number)
			return number;
		Bitnr *bittmp=new Bitnr[newnumber];
		Bitnr *bytetmp=new Bytenr[newnumber];
		if(number>0) {
			memcpy(bittmp,bitnr,number*sizeof(Bitnr));
			memcpy(bytetmp,bytenr,number*sizeof(Bytenr));
			delete bitnr;delete bytenr;
			}
		else
			number=0;
		memset(bittmp+number,'\0',sizeof(Bitnr)*(newnumber-number));
		memset(bytetmp+number,'\0',sizeof(Bytenr)*(newnumber-number));
		bytenr=bytetmp;
		bitnr=bittmp;
		number=newnumber;
		return number;
		}
		
	int Bitfield::addposition(int pos,int bytes,int max) {
		if(max>=number) {
			Bitnr *bittmp=new Bitnr[number+1];
			Bitnr *bytetmp=new Bytenr[number+1];
			if(pos) {
				memcpy(bittmp,bitnr,pos*sizeof(Bitnr));
				memcpy(bytetmp,bytenr,pos*sizeof(Bytenr));
				}
			if(number>pos) {
				memcpy(bittmp+pos+1,bitnr+pos,(number-pos)*sizeof(Bitnr));
				memcpy(bytetmp+pos+1,bytenr+pos,(number-pos)*sizeof(Bytenr));
				}
			if(number>0) {
				delete bitnr;
				delete bytenr;
				}
			bitnr=bittmp;
			bytenr=bytetmp;
			number++;
			bitnr[pos]=bytes*8;
			bytenr[pos]=bytes;
			calcsizes();	
			}
		return 0;
		}
	int Bitfield::rmpos(int pos) { 
		if(pos<0||pos>=number)
			return -1;
		number--;
		if(number>pos) {
			memmove(bitnr+pos,bitnr+pos+1,number-pos);
			memmove(bytenr+pos,bytenr+pos+1,number-pos);
			}
		calcsizes();
		return pos;
		}
	int Bitfield::getbit(int pos) {
			if(pos>=0&&pos<number)
				return bitnr[pos];
			return -1;
			};
	int Bitfield::chbitspos(int pos,int bits) { 
		if(pos>=0&&pos<number) {
			int byt=(bits+7)/8;
			if(bytenr[pos]<byt)
				bitnr[pos]=bytenr[pos]*8;
			else
				bitnr[pos]=bits;
			calcsizes();
			return pos;
			}
		return -1;
		}
	int Bitfield::chbytespos(int pos,int bytes) { 
		if(pos>=0&&pos<number) {
			int bitin=bytes*8;
			bytenr[pos]=bytes;
			if(bitnr[pos]>bitin)
				bitnr[pos]=bitin;	
			calcsizes();
			return pos;
			}

		return -1;
		}
	int Bitfield::fromfile(unsigned char *input,unsigned char *output) {
		int startbit=0;
		unsigned char *outptr=output;
		unsigned char *inptr=input;
		memset(output,'\0',bytesscreen);
		for(int i=0;i<number;i++) {
			if(bytenr[i]) {
				bitcpy(outptr,inptr,0,startbit,bitnr[i]);
				outptr+=bytenr[i];
				}
			int bij=startbit+bitnr[i];
			inptr+=bij/8;
			startbit=bij%8;
			}
		return bytesscreen;
		}
	int Bitfield::tofile(unsigned char *input,unsigned char *output) {
		int startbit=0;
		unsigned char *outptr=output;
		unsigned char *inptr=input;
		for(int i=0;i<number;i++) {
			if(bytenr[i]) {
				bitcpy(outptr,inptr,startbit,0,bitnr[i]);
				inptr+=bytenr[i];
				}
			int bij=startbit+bitnr[i];
			outptr+=bij/8;
			startbit=bij%8;
			}
		return bytesfile;
		}




	int Bitfield::calcsizes(void) {
/*
		if(part) {
			if(has_super(part,HasSub)) {
			HasSub *has=(HasSub*)part;
			  if(!has_super(part,BitContain)) {
				if(number<has->nrsubparts) {
					int num=number;
					extendtolen(has->nrsubparts);
					for(int i=0;i<num;i++)
						if(bytenr[i]!=has->subpart(i)->getbytes()) {
						 int dif=i;
						 for(;i<number;i++)
							 if(bytenr[dif]==has->subpart(i)->getbytes()) {
									memmove(bitnr+i,bitnr+dif,sizeof(Bitnr)*(num-dif));
									break;
									}
						break;
						}
				  }
				 else {
				 	if(number>has->nrsubparts) {
				 		for(int i=0;i<has->nrsubparts;i++)
				 			if(bytenr[i]!=has->subpart(i)->getbytes()) {
				 				int dif=i;
				 				int get=has->subpart(i)->getbytes(); 
				 				for(;i<number;i++)
				 					if(bytenr[i]==get) {
				 						memmove(bitnr+dif,bitnr+i,sizeof(Bitnr)*(number-i));
				 						break;
				 						}
								number=has->nrsubparts;
				 				break;
				 				}
				 		}
				 	}
					}
				for(int i=0;i<has->nrsubparts;i++) {
					bytenr[i]=has->subpart(i)->getbytes();
					int bits=bytenr[i]*8;
					if(bitnr[i]>bits)
						bitnr[i]=bits;
			
					}
				}
			else {
					if(number<1) {
						extendtolen(1);
						}
					bytenr[0]=screenbytes;
					}
			}
*/
		bytesscreen=0;
		filebits=0;
		for(int i=0;i<number;i++) {
			filebits+=bitnr[i];
			bytesscreen+=bytenr[i]; 
			}
		bytesfile=(filebits+7)/8;
		}









	int  Bitfield::equal(Filter *fil) {
		if(!Filter::equal(fil))
			return 0;
		Bitfield *bitf=(Bitfield *)fil;
		if(number!=bitf->number)
			return 0;
		if(memcmp(bitnr,bitf->bitnr,number*sizeof(Bitnr)))
			return 0;
		if(memcmp(bytenr,bitf->bytenr,number*sizeof(Bytenr)))
			return 0;
		return 1;
		};
	int Bitfield::screen2filepos(int pos) {
		int scpos=0,fipos=0;
		for(int i=0;i<number&&scpos<pos;i++){
			scpos+=bytenr[i];
			fipos+=bitnr[i];
			}
		return fipos/8;	
		}

	int Bitfield::file2screenpos(int pos) {
		int scpos=0,fipos=0,bitpos=pos*8;
		for(int i=0;i<number&&fipos<bitpos;i++){
			scpos+=bytenr[i];
			fipos+=bitnr[i];
			}
		return scpos;	
		}

registerfilterclasspart(Bitfield) 
#ifdef EDITOR
int Editor::bitfield(void) {
	ScreenPart *super=activemode(),*act;
	act=super->getsubpart();
	Bitfield *ad=new Bitfield(1);
	if(ad->error()) {
		delete ad;
		toscreen();	
		message("Error adding add");
		return -1;
		}
	if(addfilter(ad)<0) {
		toscreen();	
		message("Added filter failted!!");
		}
	else {
		toscreen();	
		message("Filter added!!");
		}
	return 0;
	}

int Editor::changebitfield(void) {
	ScreenPart *cur=activemode(),*act=NULL,*par=NULL;
	while(act=cur->getchild()) {
		par=cur;
		cur=act;
		}
	if(par)	{
		for(int i=par->firstfilter;i<par->lastfilter;i++) {
			if(par->filters[i]->name==filtername(Bitfield)) {
				Bitfield *bit=(Bitfield *)par->filters[i];
				char ans[100];
				int act=((HasSub *)par)->activesubpart();

				sprintf(ans,"%d",bit->bitnr[act]);
				if(getanswer("Bits: ",ans,40) <0)  {
					message("Wrong answer");return -1;
					}
					
				bit->resizeitem(act,atoi(ans));
				par->calcsizes();
				activemode()->calcsizes();
				modechanged();
				return 0;

				}
			}
		message("No bitfield filter in %s",par->getname());
		return -1;
		}
	message("No parent of %s",cur->getname());
	return -1;
	}
call(bitfield)
call(bitfielder)

#endif
/*
How to manage bitfields?
Possibilities:
	1. transform Contains to bitfield

Add 	bitfield filter, which shows everything the same as usual
Add function to modify every element of this
Probleem: chbytes etc. Needs change filter
*/
