#include <stdio.h>
#include <stdlib.h>
#include "hbrowser.hpp"
#include "hnode.hpp"

#define EPSILON 0.000001

HNode::HNode(class NodeInfo* _info) {
  v.set(COORDCART, 0.0, 0.0);
  o.set(COORDHYP, 0.0, 0.0);
  info = _info;
  bro = NULL;
  child = NULL;
}

HNode::~HNode() {
  if (child) delete child;
  if (bro)   delete bro;
  delete info;
}

void HNode::addBro(HNode* b){
  if(b) {
    if (bro) bro->addBro(b);
    else bro = b;
  }
}

void HNode::setChildren(HNode* c){
  child = c;
}

void HNode::setInfo(NodeInfo* _info){
  info = _info;
}

void HNode::countNodes(unsigned long& nodnumber) const {
  nodnumber++;
  if (child) child->countNodes(nodnumber);
  if (bro) bro->countNodes(nodnumber);
}

unsigned long HNode::getNodeCount() const {
  unsigned long nodnumber = 0;
  countNodes(nodnumber) ;
  return nodnumber;
}

static int _nchildren(HNode* t){
   int   n;
   HNode* aux;
   if (t){
      n = 0;
      aux = t->child;
      
      while(aux){
         n++;
         aux = aux->bro;
      }
      return n;
   } return 0;
}


static void _computesubwedge(double r, double d, HNode* t){
  double   dd;
  int      n;

  if (t){
    // nod courant
    t->v.set(COORDCART, r, d);
    t->o.set(r, d);
    HypToCart(t->v, t->v);
    t->o = t->v;
    
    // nod frere
    _computesubwedge(r+d, d, t->bro);

    // nod enfants
    n = _nchildren(t);

    //if (ComplexNorm2(t->v)<0.95)
    if (n) {
      if (n>1){
         dd = d/n;
         _computesubwedge(r, dd, t->child);
      }
      else{
        _computesubwedge(r+d*0.2, d*0.6, t->child);
      }
     }
  }
}

// si l'on ne veut pas s'embeter on affecte 0.0 à r
void HNode::recenterTree(double r) {
  int n;
  double d;
  v.set(COORDCART, 0, 0);
  o.set(0, 2 * H_PI);

  n = _nchildren(this);

  if (n) d = 2.0*H_PI/n;
  else d = 0.0;

  _computesubwedge(r, d, child);
  o = v;
}

HComplex _C1(COORDCART);
HComplex  _C2(COORDCART);
double _R1 = 0, _R2 = 0, R1 = 0, R2 = 0;


static void computeCenters(HComplex& s, HComplex& d){
  HComplex n;
  double t;

  InvSignComplex(n, s);
  AddComplex(n, d, n);                // n = d-s

  // circle 1
  t = ComplexNorm2(s) - ComplexNorm2(d);
  t = (t>0)?t:-t;                        // t = abs(t)

   if (t>EPSILON){                        // ||s|²-|d|²|> epsilon
      t = (1-ComplexNorm2(d))/
          (ComplexNorm2(s)-ComplexNorm2(d));

      _C1.set(t * s.x + (1.0-t) * d.x,  t * s.y + (1.0-t) * d.y);
      R1 =  ComplexNorm2(_C1) - 1.0;     // Pythagore
      _R1 = (R1>0.0)?R1:-R1;
   }
   else {
     _C1.set(n.y, -n.x);
     R1 = _R1 = 0.0;                     // symetry
   }

   // circle 2
   t = ComplexNorm2(d);
   if(t<EPSILON){                         // |d|²<epsilon
      _C2.set(n.y, -n.x);
      R2 = _R2 = 0.0;                     // symetry
   }
   else {
     t = InnerProdComplex(d, n);
     t = (t>0.0)?t:-t;
     if (t<EPSILON){                     // si d et d-s sont orthogo
       InvSignComplex(_C2, d);
       R2 = _R2 = 0.0;                  // symetry
     }
     else {
       t = InnerProdComplex(d, n);
       t = (1-ComplexNorm2(d)) / 2*t;
       
         MulComplexReal(_C2, n, t);
         AddComplex(_C2, _C2, d);
         R2 = ComplexNorm2(_C2) - 1.0;   // Pythagore
         _R2 = (R2>0.0)?R2:-R2;
     }
   }
}

static void reverse_from_line(HComplex& res, HComplex& l){
  double t;
  HComplex s = res;

  t = ComplexNorm2(l);
  if (t){
    t = InnerProdComplex(s, l) / t;
    MulComplexReal(res, l, -2.0 * t);
    AddComplex(res, res, s);
    InvSignComplex(res, res);
  }
}

static void reverse_from_circle(HComplex& res, HComplex& c, double r2_1){
  double t;
  HComplex s = res;

   InvSignComplex(res, c);
   AddComplex(res, s, res);             // res = s-c
   t = ComplexNorm2(res);
   if (t) {
      MulComplexReal(res, res, r2_1/t);   // r2_1 = R1 or R2
      AddComplex(res, res, c);
   }
}

void HNode::transNode_cc(){
  v = o;
  reverse_from_circle(v, _C1, R1);
  reverse_from_circle(v, _C2, R2);

  if (child) child->transNode_cc();
  if (bro) bro->transNode_cc();
}

void HNode::transNode_cl(){
  v = o;
  reverse_from_circle(v, _C1, R1);
  reverse_from_line(v, _C2);

  if (child) child->transNode_cl();
  if (bro) bro->transNode_cl();
}

void HNode::transNode_lc(){
  v = o;
  reverse_from_line(v, _C1);
  reverse_from_circle(v, _C2, R2);

  if (child) child->transNode_lc();
  if (bro) bro->transNode_lc();
}

void HNode::transNode_ll() {
  v = o;
  reverse_from_line(v, _C1);
  reverse_from_line(v, _C2);

  if (child) child->transNode_ll();
  if (bro) bro->transNode_ll();
}

void HNode::transTree(HComplex& s, HComplex& d){
  computeCenters(s, d);

  if (R1){
    if (R2) transNode_cc();
    else transNode_cl();
  }
  else{
    if (R2) transNode_lc();
    else transNode_ll();
  }
}

