#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "triangle.h"
#include "xym.h"
#include "file.h"
#include "gauss.h"

struct _triangle{
   double ratio, metric;
   xym a, b, c;
};

static void set_triangle(triangle p, double ratio, double metric, xym a, xym b, xym c);
static void check_null_triangle(triangle p, char *func);
static double *calc_xym_xshift(int ndata, xym *data);
static double *calc_xym_yshift(int ndata, xym *data);
static void calc_triangle_param(int ndata, int *point, double *xshift,double *yshift,double *ratio,double *metric,int *order);
static int judge_triangle_order(double *r, int *order);
static void calc_triangles_ratio_metric(int mode, double *x, double *y, double *r, double *ratio, double *metric);
static int triangle_ratio_comp(const void *tri1, const void *tri2);
static int triangle_ratiometric_comp(const void *tri1, const void *tri2);

triangle make_new_triangle(double ratio, double metric, xym a, xym b, xym c){
   triangle p;
   p=malloc(sizeof(struct _triangle));
   set_triangle(p,ratio,metric,a,b,c);
   return p;
}

void destroy_triangle(triangle p){
   free(p); p=NULL;
}

void destroy_triangle_array(int narray, triangle *p){
   int i;
   for(i=0;i<narray;i++) destroy_triangle(p[i]);
   free(p); p=NULL;
}

void print_triangle(FILE *fp, triangle p){
   int i;
   double ratio, metric;
   double x[3], y[3];
   xym q;
   char abc[]="abc";
   fprintf(stderr,"\ntriangle=%p\n",p);
   ratio=get_triangle_ratio(p);
   metric=get_triangle_metric(p);
   fprintf(stderr,"ratio=%.3f  metric=%.5f\n",ratio,metric);
   q=get_triangle_a(p); x[0]=get_xym_x(q); y[0]=get_xym_y(q);
   q=get_triangle_b(p); x[1]=get_xym_x(q); y[1]=get_xym_y(q);
   q=get_triangle_c(p); x[2]=get_xym_x(q); y[2]=get_xym_y(q);
   for(i=0;i<3;i++) fprintf(stderr,"  %c=(%.2f %.2f)",abc[i],x[i],y[i]);
   fprintf(stderr,"\n\n");
}

static void set_triangle(triangle p, double ratio, double metric, xym a, xym b, xym c){
   check_null_triangle(p,"set_triangle");
   p->ratio=ratio; p->metric=metric; p->a=a; p->b=b; p->c=c;
   //fprintf(stdout,"%8.2f %8.2f\n",ratio,metric);
}

double get_triangle_ratio(triangle p){
   check_null_triangle(p,"get_triangle_ratio");
   return (p->ratio);
}

double get_triangle_metric(triangle p){
   check_null_triangle(p,"get_triangle_metric");
   return (p->metric);
}

xym get_triangle_a(triangle p){
   check_null_triangle(p,"get_triangle_a");
   return (p->a);
}

xym get_triangle_b(triangle p){
   check_null_triangle(p,"get_triangle_b");
   return (p->b);
}

xym get_triangle_c(triangle p){
   check_null_triangle(p,"get_triangle_c");
   return (p->c);
}

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

triangle *make_triangle_array(int ndata, xym *data){
   int i, j, k, num, order[3], point[3], narray;
   triangle *array;
   double *xshift, *yshift, ratio, metric;
   xym a, b, c;
   xshift=calc_xym_xshift(ndata,data);
   yshift=calc_xym_yshift(ndata,data);
   narray=ndata*(ndata-1)*(ndata-2)/6;
   array=malloc(narray*sizeof(triangle));
   for(i=0,num=0;i<ndata;i++){
      point[0]=i;
      for(j=i+1;j<ndata;j++){
         point[1]=j;
         for(k=j+1;k<ndata;k++){
            point[2]=k;
            calc_triangle_param(ndata,point,xshift,yshift,&ratio,&metric,order);
            a=data[point[order[0]]];
            b=data[point[order[1]]];
            c=data[point[order[2]]];
            array[num]=make_new_triangle(ratio,metric,a,b,c);
            ++num;
            if(num>narray){
               fprintf(stderr,"!!! Unexpected number of triangles were created !!!\n"); exit(1);
            }
         }
      }
   }
   free(xshift); free(yshift);
   if(num!=narray) exit(1);
   return array;
}

static double *calc_xym_xshift(int ndata, xym *data){
   int i, j, pos, narray;
   double *array;
   xym p, q;
   narray=pow(ndata,2);
   array=malloc(narray*sizeof(double));
   for(i=0;i<narray;i++) array[i]=0;
   for(i=0;i<ndata;i++){
      p=data[i];
      for(j=i+1;j<ndata;j++){
         q=data[j];
 if((pos=i*ndata+j)>=narray) exit(1);
         array[pos]=get_xym_x(p)-get_xym_x(q);
      }
   }
   return array;
}

static double *calc_xym_yshift(int ndata, xym *data){
   int i, j, pos, narray;
   double *array;
   xym p, q;
   narray=pow(ndata,2);
   array=malloc(narray*sizeof(double));
   for(i=0;i<narray;i++) array[i]=0;
   for(i=0;i<ndata;i++){
      p=data[i];
      for(j=i+1;j<ndata;j++){
         q=data[j];
 if((pos=i*ndata+j)>=narray) exit(1);
         array[pos]=get_xym_y(p)-get_xym_y(q);
      }
   }
   return array;
}

static void calc_triangle_param(int ndata, int *point, double *xshift,double *yshift,double *ratio,double *metric,int *order){
   int mode, i, j, k;
   double x[3], y[3], r[3];
   i=point[0]; j=point[1]; k=point[2];
   if(i>=j||j>=k) exit(1);
   x[0]=xshift[j*ndata+k]; y[0]=yshift[j*ndata+k];
   x[1]=xshift[i*ndata+k]; y[1]=yshift[i*ndata+k];
   x[2]=xshift[i*ndata+j]; y[2]=yshift[i*ndata+j];
   //fprintf(stderr,"i=%d j=%d k=%d  %d-%d=(%.2f %.2f)  %d-%d=(%.2f %.2f)  %d-%d=(%.2f %.2f)\n",i,j,k,j,k,x[0],y[0],i,k,x[1],y[1],i,j,x[2],y[2]);
   for(i=0;i<3;i++) r[i]=sqrt(pow(x[i],2)+pow(y[i],2));
   mode=judge_triangle_order(r,order);
   calc_triangles_ratio_metric(mode,x,y,r,ratio,metric);
}

static int judge_triangle_order(double *r, int *order){
   int longest, shortest, mode, mid;
   if(r[0]>r[1]&&r[0]>r[2]) longest=0;
   else if(r[1]>r[2]) longest=1;
   else longest=2;
   if(r[(longest+1)%3]>r[(longest+2)%3]) shortest=(longest+2)%3;
   else shortest=(longest+1)%3;
   mid=-(longest-1)*(shortest-1)+(longest-2)*(shortest-2);
   order[0]=shortest; order[1]=mid; order[2]=longest;
   mode=longest*3+shortest;
   if(mode<0||mode>=9){
      fprintf(stderr,"!!! judge_triangle_order : Unexpected mode=%d obtained !!!\n",mode); exit(1);
   }
   //fprintf(stderr,"judge_triangle_order : r[0]=%.2f r[1]=%.2f r[2]=%.2f long=%d short=%d mode=%d\n",r[0],r[1],r[2],longest,shortest,mode);
   return mode;
}

/*
static void set_triangle_order(int mode, int *order){
   int longest, shortest, mid;
   longest=mode/3; shortest=mode%3;
   mid=(longest-1)*(shortest-1)+(longest-2)*(shortest-2);
   order[0]=shortest; order[1]=mid; order[2]=longest;
}
*/

static void calc_triangles_ratio_metric(int mode, double *x, double *y, double *r, double *ratio, double *metric){
   int longest, shortest, mid;
   longest=mode/3; shortest=mode%3;
   mid=-(longest-1)*(shortest-1)+(longest-2)*(shortest-2);
   *ratio=r[longest]/r[shortest];
   if(shortest==1) *metric=fabs(x[longest]*x[mid]-y[mid]*y[longest]);
   else *metric=x[longest]*x[mid]+y[mid]*y[longest];
   //fprintf(stderr," 0=(%.1f %.1f) 1=(%.1f %.1f) 2=(%.1f %.1f)\n",x[0],y[0],x[1],y[1],x[2],y[2]);
   //fprintf(stderr,"# calc_triangles_ratio_metric : mode=%d long=%d mid=%d short=%d ratio=%.2f metric=%.2f\n",mode,mid,longest,shortest,*ratio,*metric);
}

void sort_triangles_ratio(int narray, triangle *array){
   extern int triangle_metric_comp(const void *tri1, const void *tri2);
   qsort(array,narray,sizeof(triangle),*triangle_ratio_comp);
}

static int triangle_ratio_comp(const void *tri1, const void *tri2){
   double a, b;
   a=get_triangle_ratio(*(triangle *)tri1);
   b=get_triangle_ratio(*(triangle *)tri2);
   if(a<b) return 1;
   else if(a==b) return 0;
   return -1;
}

void sort_triangles_ratiometric(int narray, triangle *array){
   extern int triangle_ratiometric_comp(const void *tri1, const void *tri2);
   qsort(array,narray,sizeof(triangle),*triangle_ratiometric_comp);
}

static int triangle_ratiometric_comp(const void *tri1, const void *tri2){
   double a, b;
   triangle p;
   p=*(triangle *)tri1; a=p->metric*p->ratio;
   p=*(triangle *)tri2; b=p->metric*p->ratio;
   if(a<b) return 1;
   else if(a==b) return 0;
   return -1;
}

int binsearch_triangles_ratio(double ratio, int narray, triangle *array){
   int mid, left, right;
   left=0; right=narray-1;
   while(left<right){
      mid=(left+right)/2;
      if(get_triangle_ratio(array[mid])>ratio) left=mid+1;
      else right=mid;
   }
   if(get_triangle_ratio(array[left])<=ratio) return left;
   return -1;
}

