#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "tfitshdr.h"

int isFits(char *mem){
 char tmp[10];
 int i=0;
 memmove(tmp,mem,9); tmp[9]=0;
 if(tmp==strstr(tmp,"SIMPLE  ="))i++;
 if(tmp==strstr(tmp,"XTENSION="))i++;
 return i;
}

short fitsnum(short x){
 short out=x;
 char z,*c=(char*)&out;
 z=c[0];c[0]=c[1];c[1]=z;
 return out;
}

int fitsnum(int x){
 int out=x;
 char z,*c=(char*)&out;
 z=c[0];c[0]=c[3];c[3]=z;
 z=c[1];c[1]=c[2];c[2]=z;
 return out;
}

float fitsnum(float x){
 float out=x;
 char z,*c=(char*)&out;
 z=c[0];c[0]=c[3];c[3]=z;
 z=c[1];c[1]=c[2];c[2]=z;
 return out;
}

double fitsnum(double x){
 double out=x;
 char z,*c=(char*)&out;
 z=c[0];c[0]=c[7];c[7]=z;
 z=c[1];c[1]=c[6];c[6]=z;
 z=c[2];c[2]=c[5];c[5]=z;
 z=c[3];c[3]=c[4];c[4]=z;
 return out;
}

int TFitsHdr::Assign(TFitsHdr &fhdr){
 int i=0;
 char *c;
 Clear();
 loop: c=fhdr.GetLine(i);
 if(c)i=AddLine(c); else return i;
 if(i<nlines) return nlines;
 goto loop;
}

int TFitsHdr::AddLine(char *param){
 if(nlines>1020)return 0;
 char *c;
 c=new char[81]; memset(c,' ',80); c[80]=0;
 int i=0;
 while((i<80)&&(param[i]>=' ')){c[i]=param[i];i++;}
 hdr[nlines]=c;
 nlines++;
 return nlines;
}

int TFitsHdr::FindParam(char *param){
 int i,j=-1;
 for(i=0;i<nlines;i++) if(hdr[i]==strstr(hdr[i],param)){j=i;break;};
 return j;
}

char* TFitsHdr::str2upper(char *x){
 int i,j=strlen(x);
 for(i=0;i<j;i++)name[i]=toupper(x[i]);name[j]=0;
 return name;
}

void TFitsHdr::makeHdr(char* line){
  nlines=0;
  if(*line==0) {
  PutParam("SIMPLE",'T',"Written by TFitsHdr (GVI 2005)");
  PutParam("BITPIX",0);
  PutParam("EXTEND",'T',"FITS data may contain extensions");
  PutParam("NAXIS",0);
  }
  else {
  PutParam("XTENSION",line,"Written by TFitsHdr (GVI 2005)");
  PutParam("BITPIX",8);
  }
}

char * TFitsHdr::WriteToMem(int &size){
 if(nlines==0 || nlines>1022){size=0; return NULL;}
 char *c;
 int i,j;
 c= new char[81];
 memset(c,' ',80); c[0]='E'; c[1]='N'; c[2]='D';
 hdr[nlines]=c; nlines++;
 i=nlines;
 j=i/36; if(i%36) j++; j*=2880;
 c=new char[j]; size=j;
 memset(c,' ',j);
 for(i=0;i<nlines;i++){
  j=i*80; memmove(c+j,hdr[i],80);
 }
 return c;
}

int TFitsHdr::PutComment(char *com){
 if(nlines>1021){return 0;}
 char *c;
 int i;
 c= new char[81];
 memset(c,' ',80);c[80]=0;
 memmove(c,"COMMENT",7);
 hdr[nlines]=c; nlines++;
 if(com){
  i=strlen(com);
  if(i>70) i=70;
  if(i>0) memmove(c+10,com,i);
 }
 return 1;
}

char *TFitsHdr::ParamLine(char *iname, char *com){
 str2upper(iname);
 char *c;
 int i=FindParam(name);
 if (i<0){c=new char[81]; hdr[nlines]=c; nlines++;} else c=hdr[i];
 memset(c,' ',80);  c[80]=0; c[8]='='; c[31]='/';
 i=strlen(name); if(i>8)i=8;
 memmove(c,name,i);
 if(com){
  i=strlen(com); if(i>40)i=40;
  memmove(c+33,com,i);
 }
 return c;
}

int TFitsHdr::PutParam(char *iname, char param, char *com){
 if(nlines>1021) return 0;
 char *c=ParamLine(iname,com);
 c[29]=param;
 return 1;
}

int TFitsHdr::PutParam(char *iname, char *param, char *com){
 if(nlines>1021) return 0;
 char *c=ParamLine(iname,com);
 char *x="'";
 int i;
 c[10]=x[0];
 i=strlen(param);if(i>20) i=20;
 memmove(c+11,param,i);
 if(i<8)i=8; c[11+i]=x[0];
 return 1;
}

int TFitsHdr::PutParam(char *iname, int param, char *com){
 if(nlines>1021) return 0;
 char *c,x[30];
 int i;
 c=ParamLine(iname,com);
 sprintf(x,"%d",param);
 i=strlen(x); memmove(c+30-i,x,i);
 return 1;
}

int TFitsHdr::PutParam(char *iname, float param, char *com){
 if(nlines>1021) return 0;
 char *c,x[30];
 int i;
 c=ParamLine(iname,com);
 sprintf(x,"%f",param);
 i=strlen(x); memmove(c+30-i,x,i);
 return 1;
}

int TFitsHdr::PutParam(char *iname, double param, char *com){
 if(nlines>1021) return 0;
 char *c,x[30];
 int i;
 c=ParamLine(iname,com);
 sprintf(x,"%.12E",param);
 i=strlen(x); memmove(c+30-i,x,i);
 return 1;
}

int TFitsHdr::PutHistory(char * com)
{
 if(nlines>1021){return 0;}
 char *c;
 int i;
 c= new char[81];
 memset(c,' ',80);c[80]=0;
 memmove(c,"HISTORY",7);
 hdr[nlines]=c; nlines++;
 if(com){
  i=strlen(com);
  if(i>70) i=70;
  if(i>0) memmove(c+10,com,i);
 }
 return 1;
}

int TFitsHdr::GetParam(char *iname, char &param){
 str2upper(iname);
 char *c;
 int j,i=FindParam(name);
 if(i<0) return 1;
 c=hdr[i];
 j=9; while((j<32)&&(c[j]==' '))j++;
 param=c[j];
 return 0;
}

int TFitsHdr::GetParam(char *iname, char *param){
 str2upper(iname);
 char *c,x[]="''";
 int j,i=FindParam(name);
 if(i<0) return 1;
 c=hdr[i];i=9;
 while((i<32)&&(c[i]!=x[0]))i++;
 if(i==32)return 1;
 j=0;i++;while(c[i]!=x[0]){param[j]=c[i];j++;i++;};j--;
 while(j&&(param[j]<=' '))j--;
 param[j+1]=0;
 return 0;
}

int TFitsHdr::GetParam(char *iname, int &param){
 str2upper(iname);
 char *c;
 int j,i=FindParam(name);
 if(i<0) return 1;
 c=hdr[i];
 i=sscanf(c+9,"%d",&j); param=j;
 if(i) return 0; else return 1;
}

int TFitsHdr::GetParam(char *iname, float &param){
 str2upper(iname);
 char *c;
 float j;
 int i=FindParam(name);
 if(i<0) return 1;
 c=hdr[i];
 i=sscanf(c+9,"%e",&j); param=j;
 if(i) return 0; else return 1;
}

int TFitsHdr::GetParam(char *iname, double &param){
 str2upper(iname);
 char *c;
 double j;
 int i=FindParam(name);
 if(i<0) return 1;
 c=hdr[i];
 i=sscanf(c+9,"%le",&j); param=j;
 if(i) return 0; else return 1;
}

int TFitsHdr::LoadFromMem(char *mem){
 char *c;
 int j;
 Clear();
 while(nlines<1020){
 c=new char[81];memmove(c,mem+nlines*80,80); c[80]=0;
 hdr[nlines]=c; nlines++;
 if(c==strstr(c,"END")) break;
 }
 j=nlines/36; if(nlines%36) j++; j*=2880;
 return j; 
}

int TFitsHdr::GetDataSize(void){
 if(nlines<1) return -1;
 int i,j,n,k;
 char stmp[32];
 if(GetParam("naxis",n)) return -1;
 if(!n) return 0;
 for(k=1,i=0;i<n;i++){
  sprintf(stmp,"NAXIS%d",i+1);
  GetParam(stmp,j);k*=j;
 }
 GetParam("bitpix",j);if(j<0)j=-j; j/=8;
 k*=j;
 j=k/2880; if(k%2880)j++; j*=2880;
 return j;
}

//--------TBinExt---------------
int TBinExt::FindParam(char *param){
 int i,k,j=-1;
 char par[16];
 k=strlen(param)+1;
 if(k>16) k=16;
 for(i=0;i<k;i++)par[i]=toupper(param[i]);
 for(i=0;i<nfield;i++) if(type[i]==strstr(type[i],par)){j=i;break;};
 return j;
}

int TBinExt::ReadData(char *buf, int i){
 if(!nfield || !data) return 0;
 memmove(data,buf+i*size,size);
 return 1;
}

inline int TBinExt::GetFieldSize(int x){
 if(!nfield ||(x>=nfield)) return 0;
 return form[x][1];
}

int TBinExt::GetFieldSize(char *name){
 int x=FindParam(name);
 if(x>=0) return form[x][1]; else return 0;
}

char *TBinExt::GetField(int ii, char *c){
 char *x=data+shift[ii];
 if(!nfield || !data ||(ii>=nfield)) return 0;
 if(!c&& form[ii][0]=='A') return 0;
 if(c&& form[ii][0]=='A'){
 memmove(c,x,form[ii][1]);
 c[form[ii][1]]=0;
 }
 if(form[ii][1]>1)return data+shift[ii];
 if(c) c[0]=form[ii][0];
 switch(form[ii][0]){
  case 'B' : out[0]=x[0]; break;
  case 'I' : out[0]=x[1];out[1]=x[0]; break;
  case 'J' :
  case 'E' : out[0]=x[3];out[1]=x[2];out[2]=x[1];out[3]=x[0]; break;
  case 'D' : out[0]=x[7];out[7]=x[0];out[1]=x[6];out[6]=x[1];
             out[2]=x[5];out[5]=x[2];out[3]=x[4];out[4]=x[3]; break;
 }
 return out;
}

char *TBinExt::GetField(char *name, char *c){
 int x=FindParam(name);
 if(x>=0) return GetField(x,c); else return 0;
}

char *TBinExt::GetNextField(char *c){
 if(cursor>=nfield) return 0;
 char *x=GetField(cursor,c);
 cursor++;
 return x;
}

int TBinExt::LoadNextData(void){
 if(!bigdata||(dcur>=nrows)) return 0;
 ReadData(bigdata,dcur); dcur++;
 return dcur;
}

int TBinExt::LoadForms(TFitsHdr &hdr){
 int i,j,n;
 char stmp[32],stmp1[32];
 i=hdr.GetParam("tfields",n);
 if(i) return 0;
 i=hdr.GetParam("naxis",j);
 if(i||(j!=2)) return 0;
 i=hdr.GetParam("naxis2",j);
 if(i||(j<1)) return 0;
 nrows=j;shift[0]=0;
 i=hdr.GetParam("naxis1",j);
 if(i) return 0;
 if(data) delete[] data;
 size=j; data=new char[j];
 nfield=n;
 for(i=0;i<n;i++){
  sprintf(stmp,"ttype%d",i+1);
  j=hdr.GetParam(stmp,stmp1);//memmove(type[i],stmp1,16);
  for(int ii=0;ii<16;ii++)type[i][ii]=toupper(stmp1[ii]);
  sprintf(stmp,"tform%d",i+1);
  j=hdr.GetParam(stmp,stmp1);
  if(stmp1[0]>'9'){form[i][0]=stmp1[0];form[i][1]=1;}
  else{sscanf(stmp1,"%d%s",&j,stmp);form[i][0]=stmp[0];form[i][1]=j;}
 }
 for(i=1;i<n;i++){
  switch(form[i-1][0]){
   case 'B' :
   case 'A' : j=1;break;
   case 'I' : j=2;break;
   case 'J' :
   case 'E' : j=4;break;
   case 'D' : j=8;break;
  }
  shift[i]=form[i-1][1]*j+shift[i-1];
 }
 return nfield;
}

char *TBinExt::ClassInfo(void){
 char tmp[256];
 int i,j;
 sprintf(str,"struct has %d fields, total size is %d\n",nfield,size);
 for(i=0;i<nfield;i++){
  j=sprintf(tmp,"field %2d :name='%s' type=%c size=%d shift=%d\n",i+1,type[i],
    form[i][0],form[i][1],shift[i]);
  strcat(str,tmp);
  j=strlen(str); if(j>3800) break;
 }
 return str;
}

int TBinExt::SetField(char *name, char *value, int vsize){
 char *c;
 int ss,x=FindParam(name);
 if(x>=0){
  c=data+shift[x]; ss=vsize;
  memmove(c,value,ss);
 }
return x;
}

int TBinExt::SetField(char *name, short value){
 char *c;
 short z=value;
 int x=FindParam(name);
 if(x>=0){
  c=data+shift[x];
  memmove(c,&z,2);
 }
return x;
}

int TBinExt::SetField(char *name, int value){
 char *c;
 int z=value;
 int x=FindParam(name);
 if(x>=0){
  c=data+shift[x];
  memmove(c,&z,4);
 }
return x;
}

int TBinExt::SetField(char *name, float value){
 char *c;
 float z=value;
 int x=FindParam(name);
 if(x>=0){
  c=data+shift[x];
  memmove(c,&z,4);
 }
return x;
}

int TBinExt::SetField(char *name, double value){
 char *c;
 double z=value;
 int x=FindParam(name);
 if(x>=0){
  c=data+shift[x];
  memmove(c,&z,8);
 }
return x;
}
