#include <stdio.h>
#include <stdlib.h>
#include "xym.h"
#include "file.h"

struct _xym{
   int id;
   double xorg, yorg, morg;
   double x, y, m;
   void *data;
};

static xym read_new_xym(char *line, int id);
static void check_null_xym(xym p, char *func);
static int xym_m_comp(const void *xym1, const void *xym2);
static int xym_x_comp(const void *xym1, const void *xym2);

xym *load_xym(char *file, int *num){
   FILE *fp;
   int nline, iline, ndata;
   char line[LINE_LENGTH];
   xym *data;
   nline=file_nline(file);
   if(nline<1){
      *num=0; return NULL;
   }
   data=malloc(nline*sizeof(xym));
   fp=fileopen(file,"r");
   ndata=0; iline=0;
   while(!feof(fp)){
      fgets(line,sizeof(line),fp);
      ++iline;
      if(comment_line(line)==TRUE||feof(fp)) continue;
      if((data[ndata]=read_new_xym(line,iline))!=NULL) ++ndata;
   }
   fclose(fp);
   *num=ndata;
   return data;
}

static xym read_new_xym(char *line, int id){
   double x, y, m;
   xym p;
   if(sscanf(line,FORMAT_XYM_IN,&x,&y,&m)!=3) return NULL;
   if((p=malloc_xym())==NULL) exit(1);
   set_xym(p,id,x,y,m,NULL);
   return p;
}

xym malloc_xym(void){
   xym p;
   if((p=malloc(sizeof(struct _xym)))==NULL){
      fprintf(stderr,"malloc_xym failed!\n"); exit(1);
   }
   return p;
}

void destroy_xym(int ndata, xym *p){
   int i;
   for(i=0;i<ndata;i++) free(p[i]);
   free(p); p=NULL;
}

void print_xym(FILE *fp, xym p){
   double x, y, m;
   x=get_xym_x(p); y=get_xym_y(p); m=get_xym_m(p);
   fprintf(fp,FORMAT_XYM_OUT,x,y,m);
}

void set_xym(xym p, int id, double x, double y, double m, void *data){
   check_null_xym(p,"set_xym");
   p->id=id; p->x=x; p->y=y; p->m=m; p->data=data;
   p->xorg=x; p->yorg=y; p->morg=m;
}

int get_xym_id(xym p){
   check_null_xym(p,"get_xym_id");
   return (p->id);
}

double get_xym_x(xym p){
   check_null_xym(p,"get_xym_x");
   return (p->x);
}

double get_xym_y(xym p){
   check_null_xym(p,"get_xym_y");
   return (p->y);
}

double get_xym_xorg(xym p){
   check_null_xym(p,"get_xym_xorg");
   return (p->xorg);
}

double get_xym_yorg(xym p){
   check_null_xym(p,"get_xym_yorg");
   return (p->yorg);
}

double get_xym_morg(xym p){
   check_null_xym(p,"get_xym_morg");
   return (p->morg);
}

double get_xym_m(xym p){
   check_null_xym(p,"get_xym_m");
   return (p->m);
}

void *get_xym_data(xym p){
   check_null_xym(p,"get_xym_data");
   return (p->data);
}

static void check_null_xym(xym p, char *func){
   if(p==NULL){
      fprintf(stderr,"%s : Null xym was given!\n",func); exit(1);
   }
}

void scale_xym(int ndata, xym *data, double scale){
   int i;
   for(i=0;i<ndata;i++){
      data[i]->x *= scale;
      data[i]->y *= scale;
   }
}

void mirror_xym_x(int ndata, xym *data){
   int i;
   for(i=0;i<ndata;i++) data[i]->x = -(data[i]->x);
}

void mirror_xym_y(int ndata, xym *data){
   int i;
   for(i=0;i<ndata;i++) data[i]->y = -(data[i]->y);
}

void xym_tran(int ndata, xym *data, double *coef){
   int i;
   double xold, yold;
   xym p;
   for(i=0;i<ndata;i++){
      p=data[i];
      xold=p->x; yold=p->y;
      p->x=coef[0]*xold+coef[1]*yold+coef[2];
      p->y=coef[3]*xold+coef[4]*yold+coef[5];
   }
}

void sort_xym_m(int ndata, xym *data){
   extern int xym_m_comp(const void *xym1, const void *xym2);
   qsort(data,ndata,sizeof(xym),*xym_m_comp);
}

static int xym_m_comp(const void *xym1, const void *xym2){
   double a, b;
   a=get_xym_m(*(xym *)xym1);
   b=get_xym_m(*(xym *)xym2);
   if(a>b) return 1;
   else if(a==b) return 0;
   return -1;
}

void sort_xym_x(int ndata, xym *data){
   extern int xym_x_comp(const void *xym1, const void *xym2);
   qsort(data,ndata,sizeof(xym),*xym_x_comp);
}

static int xym_x_comp(const void *xym1, const void *xym2){
   double a, b;
   a=get_xym_x(*(xym *)xym1);
   b=get_xym_x(*(xym *)xym2);
   if(a>b) return 1;
   else if(a==b) return 0;
   return -1;
}

/* data should be sorted. find the brightest among fainter than m */
int binsearch_xym_m(double m, int ndata, xym *data){
   int mid, left, right;
   left=0; right=ndata-1;
   while(left<right){
      mid=(left+right)/2;
      //fprintf(stderr,"binsearch : left=%d mid=%d right=%d  ",left,mid,right);
      //fprintf(stderr,"%.1f  %.1f  %.1f\n",get_xym_m(data[left]),get_xym_m(data[mid]),get_xym_m(data[right]));
      if(get_xym_m(data[mid])<m) left=mid+1;
      else right=mid;
   }
   if(get_xym_m(data[left])>=m) return left;
   return -1;
}
