/* See mtv_valve_helical_body_make.h */
/* Last edited on 2016-04-22 18:53:10 by stolfilocal */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>

#include <bool.h>
#include <r3.h>
#include <r3_path.h>
#include <affirm.h>
#include <ppv_array.h>

#include <voxm_splat.h>
#include <voxm_splat_tube.h>

#include <mtv_valve_helical_body_make.h>
#include <mtv_valve_backloop_make.h>

void mtv_valve_helical_body_make
  ( ppv_array_t *a, 
    r3_t *ctr, 
    double hxbotZ, 
    double hxtopZ, 
    double helixN,  /* Number of turns of the helical part of main channel. */
    double helixR,  /* Radius of midline of helical part of main channel (vx). */
    int32_t stageN, /* Number of stages. */
    double mcA,     /* Nominal angular extent of each stage (rad). */
    double bfD,     /* Angular distance between ends of backflow loop (rad). */
    double bfR,     /* Radius of of final part (the turn) of the backflow loop (vx). */ 
    double bmW,     /* Torsion angle of backflow loop at beginning of the turn (rad). */
    double beW,     /* Torsion angle of backflow loop at end of the turn (rad). */
    double inR, 
    double otR, 
    double fuzzR, 
    bool_t sub,
    r3_path_state_t *spS0, /* (OUT) State at beginning of helical tube. */
    r3_path_state_t *spS1  /* (OUT) State at end of helical tube. */
  )
  {
    bool_t debug = FALSE;
    
    demand(hxbotZ < hxtopZ, "helix should be ascending");
    
    /* Unless said otherwise, all angles are in radians, measured in {XY} projection. */
    /* Variables ending in "{D}" are angular measures. */
    /* Variables ending in "{A}" are angular positions, measured CCW from {X} axis. */
    
    /* Compute the parameters of the main channel helix (parametrized by turn count {t}): */ 
    double hxmidZ = (hxbotZ + hxtopZ)/2; /* {Z} coordinate of mid-helix. */
    double helixZ = hxtopZ - hxbotZ;     /* Total height of helix proper. */
    double helixH = helixZ/helixN;   /* Vertical displacement per turn. */
    double helixL = hypot(2*M_PI*helixR, helixH); /* Length of one full turn. */ 
    double helixD = 2*M_PI; /* Angle of one full turn (radians). */ 
    double helixE = atan2(helixH, helixD*helixR);  /* Elevation angle of helix. */
    fprintf(stderr, "helix axis at ( %.2f %.2f ) vx\n", ctr->c[0],ctr->c[1]);
    fprintf(stderr, "helix radius = %.2f vx  total height = %.2f vx\n", helixR, helixZ);
    fprintf(stderr, "rise per turn = %.2f vx  length per turn = %.2f", helixH, helixL);
    fprintf(stderr, "  elevation angle = %.3f deg\n", helixE*180/M_PI);

    /* Compute the placement of valve stages {nstages}.  Note that {bfD} may be larger than {mcA}: */
    double mxD = fmax(mcA,bfD);  /* Tot angular span of a single stage (main and backflow channels) in {XY} projection. */ 
    double spD = (stageN == 0 ? 0.0 : mcA*(stageN - 1) + mxD);  /* Angular span of all stages (incl. backflow channels). */
    double mcA0 = -0.5*mcA*stageN;        /* Angular arg of start of main channel of first stage. */
    fprintf(stderr, "valve has %d stages spaced %.2f deg apart\n", stageN, mcA*180/M_PI);
    fprintf(stderr, "each stage (including backloop) spans %.2f deg\n", mxD*180/M_PI);
    fprintf(stderr, "all stages (including backloops) span %.2f deg\n", spD*180/M_PI);
    fprintf(stderr, "main channel of first stage starts at %.2f deg\n", mcA0*180/M_PI);
    demand(spD <= helixN*(2*M_PI) + 1.0e-6, "main channel is too short for all stages");

    /* Compute the parameters relative to a single stage: */
    double bbD = 0.5*(mcA - bfD); /* Angle from start of stage to start of backflow channel (may be negative). */
    double beD = bbD + bfD;       /* Angle from start of stage to end of backflow channel. */
    
    /* Estimate the angular measure of the turn part of the backloop: */
    double trD = M_PI + 0.5*bfD;
   
    /* Define the placement state {hxmidS} at {t=0}: */
    r3_path_state_t hxmidS;
    hxmidS.p.c[0] = ctr->c[0] + helixR;
    hxmidS.p.c[1] = ctr->c[1];
    hxmidS.p.c[2] = hxmidZ;
    { r3_t u, v, w; 
      r3_axis(1, &u); 
      r3_axis(0, &v); r3_neg(&v, &v);
      r3_axis(2, &w);
      r3x3_from_rows(&u, &v, &w, &(hxmidS.M));
    } 
    r3_path_state_debug(stderr, &hxmidS, "", "helix midpoint");
    
    auto r3_path_state_t helix_state(double t);
      /* Returns a state {S=S(t)} along the helix at time {t} (in full turns, from mid-helix). 
        The vector {S.u} is tangent to the helix,the vector {S.v} is horizontal
        and points towards the helical axis, and {S.w} is perpendicular to the 
        osculating plane at {t}. */

    /* Lay down the main channel: */
    double tt0 = -0.5*helixN;  /* Starting argument. */
    double tt1 = +0.5*helixN;  /* Final argument. */
    voxm_splat_tube_round_helix(a, tt0,tt1, &hxmidS, helixL,helixD,helixH, inR,otR, fuzzR, sub, spS0, spS1);
    
    /* Lay down the backflow channels: */
    int i;
    for (i = 0; i < stageN; i++)
      { double mcAi = mcA0 + i*mcA; /* Angular arg of start of main channel of stage {i}. */
        double bbAi = mcAi + bbD;   /* Angular arg of start of backloop of stage {i}. */
        double beAi = mcAi + beD;   /* Angular arg of end of backloop of stage {i}. */
        double bbti = bbAi/(2*M_PI);  /* Time argument at start of backloop. */
        double beti = beAi/(2*M_PI);  /* Time argument at end of backloop. */
        r3_path_state_t bbS = helix_state(bbti);
        if (debug) { r3_path_state_debug(stderr, &bbS, "", "backloop start"); }
        r3_path_state_t beS = helix_state(beti);
        if (debug) { r3_path_state_debug(stderr, &beS, "", "backloop end"); }
        mtv_valve_backloop_make(a, inR,otR, &bbS, &beS, bfR,trD,bmW,beW, fuzzR,sub, NULL, NULL); 
      }
    
    fprintf(stderr, "\n");
    return;
    
    /* INTERNAL IMPLEMENTATIONS*/
      
    r3_path_state_t helix_state(double t)
      { /* Get a state on the helix that goes through origin when {t=0}: */
        r3_path_state_t S;
        r3_path_helix(t, helixL, helixD, helixH, &S);
        /* Modify so that for {t=0} it is at the right place: */
        r3_path_state_compose(&S, &hxmidS, &S);
        return S;
      }
  }
  
