#include "../hyper.h"

// usage: -viz -geo 1 -canvas i -zoom .2 -shot-1000 -magmashape N
// where N = 1, 2, 3, 4

namespace hr {

int magmav = 7;

/* vertices of the Magma's heptagon */
vector<hyperpoint> vertices;

hyperpoint hcenter;

/* how are all Magma's heptagons transformed and colored */
vector<pair<transmatrix, color_t>> heps;

int magmashape = 0;

int magmacount = 1000;

int magmalong = 10;

bool magmadebug = false;

/* transformation from the original to the next heptagon; 
   edge a of the original heptagon matches edge b of the next heptagon */

EX transmatrix get_adj(int a, int b) {
  
  hyperpoint vl = vertices[a];
  hyperpoint vr = vertices[a+1];
  hyperpoint vm = mid(vl, vr);
      
  transmatrix rm = gpushxto0(vm);
  
  hyperpoint xvl = vertices[b];
  hyperpoint xvr = vertices[b+1];
  hyperpoint xvm = mid(xvl, xvr);
  
  transmatrix xrm = gpushxto0(xvm);
  
  transmatrix Res = rgpushxto0(vm) * rspintox(rm*vr);
    
  Res = Res * spintox(xrm*xvl) * xrm;
  
  return Res;
  }

pair<int, int> hash(hyperpoint h) {
  return {int(h[0] * 1000000 + .5), int(h[1] * 1000000 + .5)};
  }

void make() {

  int& v = magmav;
  
  /* compute the vertices */
  vertices.resize(magmav+1);
  for(int i=0; i<=magmav; i++)
    vertices[i] = spin(2*M_PI*(i+(v-7)/4.)/v) * xpush0(1);
  ld xx = vertices[2][0];
  
  int down = v/2 + 2;
  
  for(int k=3; k<down; k++)
    vertices[k][0] = 2 * xx - vertices[k][0];
  
  hcenter = Hypc;
  for(int i=0; i<magmav; i++) hcenter += vertices[i];
  hcenter = normalize(hcenter);
  
  vertices.resize(magmav);
  for(int i=0; i<magmav; i++) vertices.push_back(vertices[i]);
  for(int i=0; i<magmav; i++) vertices.push_back(vertices[i]);
  
  map<pair<int, int>, int> counts;
  
  int big = v - 2;
  
  auto rehash = [&] (const transmatrix& T) {
    for(int a=0; a<v; a++) {
      auto hashval = hash(T * vertices[a]);
      if(a == 2 || a == down)
        counts[hashval]++;
      else if(a > 2 && a < down)
        counts[hashval] += 2*v - big;
      else 
        counts[hashval] += big;
      }
    };

  heps.emplace_back(Id, 0xFFFFFFFF);
  
  auto advance = [&] (int i, int j, bool mirror = false) {
    transmatrix T = heps.back().first * get_adj(i, j);
    if(mirror) T = T * MirrorY;   
    heps.emplace_back(T, heps.back().second ^ 0xFFFF00);
    };
  
  /* create the core */
  
  int last = v-1;
  int t = 3;

  switch(magmashape) {
  
    case 1:
      for(int i=1; i<2*v; i++)
        advance(0, t);
      break;

    case 2:
      for(int i=0; i<t+1; i++)
        advance(t, 0);
      break;
    
    case 3:
      for(int a=0; a<2; a++) {
        advance(t-1, 0);
        for(int i=a; i<v+1; i++)
          advance(t, 0);
        }
      break;
    
    case 4: {     
      advance(t, 1);
      for(int a=0; a<v-3; a++)
        advance(t, 0);
      advance(t, 1);
      for(int a=0; a<v-4; a++)
        advance(t, 0);
      break;
      }
    
    case 5:
      for(int a=0; a<v-1; a++) {
        advance(t, 0);
        }
      for(int b=0; b<magmalong; b++) {
        advance(t, last);
        advance(t, 0);
        }
  
      for(int a=0; a<v; a++) {
        advance(t, 0);
        }
  
      for(int b=0; b<magmalong; b++) {
        advance(t, last);
        advance(t, 0);
        }
      break;
    }
  
  /* center the core */

  hyperpoint center = Hypc;
  for(auto& h: heps) for(int i=0; i<v; i++)
    center += h.first * vertices[i];
  
  center = normalize(center);
  
  for(auto& h: heps) h.first = gpushxto0(center) * h.first;
  
  counts.clear();
  for(auto& h: heps) rehash(h.first);
  
  /* grow */

  for(int a=0; a<magmacount; a++) {
    hyperpoint p = heps.back().first * vertices[2];
    
    int total = counts[hash(p)];
    
      println(hlog, "total ", total);
    if(total == 2*v) 
      advance(down, down, true);

    else if(total == 2 * v - big)
      advance(t, last);
    else if(total < 2 * v)
      advance(t, 0);
    else
      break;

    rehash(heps.back().first);
    }

  }

void draw_at(transmatrix T, color_t col, int id) {
  for(int i=0; i<=magmav; i++) 
    curvepoint(T * vertices[i]);
  queuecurve(0xFF, col, PPR::LINE);
  if(magmadebug) {
    for(int i=0; i<magmav; i++) {
      hyperpoint h = mid(vertices[i], vertices[i+1]);
      h += spin(M_PI/2) * (vertices[i+1] - vertices[i]) * .05;
      queuestr(T * rgpushxto0(h), 0.4, its(i), 0x80);
      }
    queuestr(T * rgpushxto0(hcenter), 0.4, "#"+its(id), 0x80);
    }

  }

void draw_magma() {
  if(heps.empty()) make();
  transmatrix V = ggmatrix(currentmap->gamestart());
  int id = 0;
  for(auto h: heps)
    draw_at(V * h.first, h.second, id++);
  }

int readArgs() {
  using namespace arg;
           
  if(0) ;
  else if(argis("-magmashape")) {    
    shift(); magmashape = argi();
    }

  else if(argis("-magmav")) {    
    shift(); magmav = argi();
    }

  else if(argis("-magmacount")) {    
    shift(); magmacount = argi();
    }

  else if(argis("-magmalong")) {    
    shift(); magmalong = argi();
    }

  else if(argis("-magmadebug")) {
    shift(); magmadebug = argi();
    }

  else return 1;
  return 0;
  }


auto msc = addHook(hooks_frame, 100, draw_magma)
 + addHook(hooks_args, 100, readArgs);

}