/*******************************************************************************
*
* McXtrace, x-ray tracing package
*         Copyright, All rights reserved
*         DTU Physics, Kgs. Lyngby, Denmark
*         Synchrotron SOLEIL, Saint-Aubin, France
*
* Component: Mirror_toroid_pothole
*
* %Identification
*
* Written by: Erik B Knudsen 
* Date: Jul 2016
* Version: 1.0
* Origin: DTU Physics
* Modified by: Padovani Antoine, 5th August 2022.
*
* Toroidal shape mirror (in XZ)
*
* %Description
* This is an implementation of a toroidal mirror which may be curved in two dimensions.
* To avoid solving quartic equations, the intersection is computed as a combination of 
* two intersections. First, the ray is intersected with a cylinder to catch (almost) the small
* radius curvature. Secondly, the ray is the intersected with an ellipsoid, with the curvatures
* matching that of the torus. 
*
* The first incarnation (Mirror_toroid.comp) the mirror curves outwards (a bump), but this incarnation (Mirror_toroid_pothole) curves inwards (a pothole).
*
* Example: Mirror_toroid_pothole( radius=0.1, radius_o=1000, xwidth=5e-2, zdepth=2e-1,R0=1, coating="")
*
* %Parameters
* R0:      [1]  Reflectivity of mirror.
* xwidth:  [m]  Width of mirror.
* zdepth:  [m]  Length of mirror.
* coating: [str] Datafile containing either mirror material constants or reflectivity  numbers.
* radius:  [m] Curvature radius
* radius_o:[m] Curvature radius, outwards
*
* %End
*******************************************************************************/

DEFINE COMPONENT Mirror_toroid_pothole
SETTING PARAMETERS (zdepth=0.1, xwidth=0.01, radius, radius_o,R0=0,string coating="")

SHARE
%{
  #include <complex.h>
  %include "read_table-lib" 
  %include "reflectivity-lib"
  struct potholestruct {
        double e_min,e_max,e_step,theta_min,theta_max,theta_step;
        int use_reflec_table;
  };
%}

DECLARE
%{  
    struct potholestruct prms;
    t_Table reflec_table;
    t_Reflec re;
%}

INITIALIZE
%{
    if (coating && strlen(coating)){
        char **header_parsed;
        t_Table *tp=&reflec_table;
        /* read 1st block data from file into tp */
        if (Table_Read(tp, coating, 1) <= 0)
        {
            exit(fprintf(stderr,"Error: %s: cannot read file %s\n",NAME_CURRENT_COMP, coating));
        }
        header_parsed = Table_ParseHeader(tp->header,
                "e_min=","e_max=","e_step=","theta_min=","theta_max=","theta_step=",NULL);
        if (header_parsed[0] && header_parsed[1] && header_parsed[2] &&
                header_parsed[3] && header_parsed[4] && header_parsed[5])
        {
            prms.e_min=strtod(header_parsed[0],NULL);
            prms.e_max=strtod(header_parsed[1],NULL);
            prms.e_step=strtod(header_parsed[2],NULL);
            prms.theta_min=strtod(header_parsed[3],NULL);
            prms.theta_max=strtod(header_parsed[4],NULL);
            prms.theta_step=strtod(header_parsed[5],NULL);
        } else {
            exit(fprintf(stderr,"Error: %s: wrong/missing header line(s) in file %s\n", NAME_CURRENT_COMP, coating));
        }	
        if (((tp->rows-2)*prms.e_step) > (prms.e_max-prms.e_min))
        {   
            exit(fprintf(stderr,"Error: %s: e_step does not match e_min and e_max in file %s (<)\n",NAME_CURRENT_COMP, coating));    
        }
        if ((prms.e_max-prms.e_min) > ((tp->rows)*prms.e_step))
        {
            exit(fprintf(stderr,"Error: %s: e_step does not match e_min and e_max in file %s (>)\n",NAME_CURRENT_COMP, coating));
        }        
        if (((tp->columns-2)*prms.theta_step) > (prms.theta_max-prms.theta_min))  
        {
            exit(fprintf(stderr,"Error: %s: theta_step does not match theta_min and theta_max in file %s (<)\n",NAME_CURRENT_COMP,coating));
        }
        if ((prms.theta_max-prms.theta_min) > ((tp->columns)*prms.theta_step))
        {
            exit(fprintf(stderr,"Error: %s: theta_step does not match theta_min and theta_max in file %s (>)\n",NAME_CURRENT_COMP,coating));
        }
        prms.use_reflec_table=1;
    }else{
        prms.use_reflec_table=0;
    }
%}

TRACE
%{
    int status;
    double l0,l1,l2,l3,tx,ty,tz;
    
    do {
        /*TODO check the permutation of coordinates and the actual position of the cylinder.*/
        status= cylinder_intersect(&l0,&l1,x,z,y-radius,kx,kz,ky, radius, zdepth);
        if (!status) break; /* no interaction with the cylinder*/
        //if(status & (02|04)) break; /*we exit the top/bottom of the cylinder*/        
        if(status & (24)) break;
        //if(status & (8|16)) break; /*we exit the top/bottom of the cylinder*/ 
        /*if the first intersection is behind the particle - this means the ray is on the wrong side of the mirror*/
        if(l1<0) break; 
        do {
            /*this to do a test propagation*/
            double op[12];
	    op[0]=x; op[1]=y; op[2]=z; op[3]=kx; op[4]=ky; op[5]=kz; op[6]=phi; op[7]=t; op[8]=Ex; op[9]=Ey; op[10]=Ez; op[12]=p;
            mcPROP_DL(l1);
            (tx)=x;(ty)=y; (tz)=z;
	    x=op[0]; y=op[1]; z=op[2]; kx=op[3]; ky=op[4]; kz=op[5]; phi=op[6]; t=op[7]; Ex=op[8]; Ey=op[9]; Ez=op[10]; p=op[12];
        }while(0);
        /*check mirror limits of intersectio point.*/
        if(tz<-zdepth/2.0 || tz>zdepth/2.0) break;
        /*check if the width is OK*/
        double xmax=acos(xwidth/(2.0*radius))*radius;
        if(tx<-xmax || tx>xmax) break;
        
        status=ellipsoid_intersect(&l2,&l3,x,y-radius,z,kx,ky,kz,radius,radius,radius_o+radius,NULL);
        if (!status) break;
        if(l3<0) break; /*This shouldn't be possible*/        
        /*the mirror is indeed hit*/
        PROP_DL(l3);
        SCATTER;
        
        double nx,ny,nz;
        nx=2*x/(radius*radius);
        ny=2*(y-radius)/(radius*radius);/*ellipsoid is displaced to put Origin on the surface*/
        nz=2*z/((radius+radius_o)*(radius+radius_o));
        NORM(nx,ny,nz);

        /*By definition the normal vector points out from the ellipsoid*/
        double s=scalar_prod(kx,ky,kz,nx,ny,nz);
        double k=sqrt(scalar_prod(kx,ky,kz,kx,ky,kz)); 
         
        kx=kx-2*s*nx;
        ky=ky-2*s*ny;
        kz=kz-2*s*nz;

        /*find energy, and glancing angle*/ 
        if (prms.use_reflec_table){
            /*the get ref. by call to Table_value2d*/
            double R;
            double theta=RAD2DEG*(acos(s/k)-M_PI_2);
            double e=K2E*k;
            R=Table_Value2d(reflec_table,fabs((e-prms.e_min)/prms.e_step), fabs(((theta-prms.theta_min)/prms.theta_step)));
            p*=R;
            /*update phase - as an approximation turn by 180 deg.*/
	    phi+=M_PI;
        }else{
            p*=R0;
        }
    }while(0);
%}




MCDISPLAY
%{
  //See parametric equation used for the 3D view here: https://en.wikipedia.org/wiki/Torus#Geometry or here:https://web.maths.unsw.edu.au/~rsw/Torus/index.php
  //Because the code above doesn't actually reproduce a torus exactly but uses a cylinder and an ellipsoid to construct the toroidal mirror (to avoid solving quartic equations),
  //there's a slight mismatch between the 3D view (exact torus) and the actual toroidal mirror coded above.
  
  double x0,y0,z0,x1,y1,z1,theta,phi,phi_total,theta_total,theta_beginning,phi_beginning,theta_end,phi_end;
  //int N=50; too many points, makes the 3D matlab viewer bug
  int N=10;     
  
  phi_total = RAD2DEG*(zdepth/(radius+radius_o));//degrees
  theta_total = RAD2DEG*(xwidth/radius);
  
  //fix theta=0 and phi=180 degrees respectively
  theta=0;
  phi=180;
  
  //then start at the beginning of the theta or phi range in order to map the flat surface (xwidth, zdepth) on the torus.
  theta_beginning=theta-(theta_total/2);
  phi_beginning=phi-(phi_total/2);
   
  theta_end = theta+(theta_total/2);
  phi_end = phi+(phi_total/2);
  
  theta = theta_beginning;
  phi = phi_beginning;  
  
  while (theta<=theta_end){
      phi=180-(phi_total/2);
      x0=radius*sin(DEG2RAD*theta);
      y0=(radius_o+radius*cos(DEG2RAD*theta))*cos(DEG2RAD*phi)+(radius_o+radius);
      z0=(radius_o+radius*cos(DEG2RAD*theta))*sin(DEG2RAD*phi);
      while (phi<=phi_end){
          x1=radius*sin(DEG2RAD*theta);
          y1=(radius_o+radius*cos(DEG2RAD*theta))*cos(DEG2RAD*phi)+(radius_o+radius);
          z1=(radius_o+radius*cos(DEG2RAD*theta))*sin(DEG2RAD*phi);      
          line(x0,y0,z0,x1,y1,z1);   
          x0=x1;
          y0=y1; 
          z0=z1; 
          phi+=phi_total/N;
          
      }
      theta+=theta_total/N;
  }  
   
  theta = theta_beginning;
  phi = phi_beginning;  
  
  while (phi<=phi_end){
      theta=-(theta_total/2);
      x0=radius*sin(DEG2RAD*theta);
      y0=(radius_o+radius*cos(DEG2RAD*theta))*cos(DEG2RAD*phi)+(radius_o+radius);
      z0=(radius_o+radius*cos(DEG2RAD*theta))*sin(DEG2RAD*phi);
      while (theta<=theta_end){
          x1=radius*sin(DEG2RAD*theta);
          y1=(radius_o+radius*cos(DEG2RAD*theta))*cos(DEG2RAD*phi)+(radius_o+radius);
          z1=(radius_o+radius*cos(DEG2RAD*theta))*sin(DEG2RAD*phi);      
          line(x0,y0,z0,x1,y1,z1);   
          x0=x1;
          y0=y1; 
          z0=z1; 
          theta+=theta_total/N;
      }
      phi+=phi_total/N;
  }
   
%}


END
