#!/bin/python

# This script allows a web browser to be used to the visualise the network of
# MOC cells and corners used within the RegBaseMesh function in moc.c. It
# reads input files "cells.asc", "corners.asc" and "path.asc".  These are
# generated when RegBaseMesh is called (e.g. by using $KAPPA_DIR/ardplot
# region=moc.fit") if moc.c has been compiled with -DMESH_DEBUG. It creates
# output file "output.html". Viewing this html file in a browser requires
# javascript files "svg.js" and "svg-pan-zoom.js" to be present in the same
# directory. These files are available on github:
#   - github.com/svgdotjs/svg.js/tree/master/src
#   - github.com/ariutta/svg-pan-zoom/tree/master/src

box_size = 10000.0

def readtab( file ):
   table = {}
   iline = 0
   print( "Reading "+file )
   fd = open( file, "r" )
   for line in fd:
      iline += 1
      words = line.split()
      if line.startswith('#'):
         cols = words[ 1: ]
         ncol = len(cols)
      elif len(words) != ncol:
         print( "Wrong number of columns ({0}) at line {1}. Should be {2}.".format(len(words), iline, ncol ))
      else:
         for icol in range(ncol):
            if cols[icol] not in table:
               table[ cols[icol] ] = []
            table[ cols[icol] ].append( words[icol] )
   fd.close()
   return table

def writetab( fd, table, name ):
   iline = 0
   print( "Writing "+name )

   fd.write( "var {0} = {{".format(name) )

   first = True
   for col in table:
      if col == 'this':
         continue
      if first:
         fd.write( "{0}:[".format(col) )
         first = False
      else:
         fd.write( ",\n{0}:[".format(col) )

      firstv = True
      for v in table[col]:
         if firstv:
            fd.write( v )
            firstv = False
         else:
            fd.write( ",{0}".format(v) )

      fd.write( "]" )
   fd.write( "};\n" )




print("Reading input files")
cells = readtab("cells.asc")
corners = readtab("corners.asc")
path = readtab("path.asc")

ncell = len( cells['this'] )
ncorner = len( corners['this'] )
npath = len( path[ 'corner'] )

print("Finding corners used in each cell")
these_cells = cells['this']
these_corners = corners['this']
for col in ('bl', 'tl', 'tr', 'br'):
   clist = cells[col]
   newclist = []
   for icell in range(ncell):
      p = clist[icell]
      if p == "null":
         icorner = -1
      else:
         try:
            icorner = these_corners.index(p)
         except ValueError:
            print( "{2} corner {0} referenced by cell {1} cannot be found".format(p,icell,col))
            break;
      newclist.append( str(icorner) )
   cells[col] = newclist

clist = cells['prev']
newclist = []
for icell in range(ncell):
   p = clist[icell]
   if p == "(nil)":
      inew = -1
   else:
      try:
         inew = these_cells.index(p)
      except ValueError:
         print( "Previous cell {0} referenced at cell {1} cannot be found".format(p,icell))
         break;
   newclist.append( str(inew) )
cells['prev'] = newclist


print("Finding cells used in each corner")
for col in ('cell0', 'cell1', 'cell2', 'cell3'):
   clist = corners[col]
   newclist = []
   for icorner in range(ncorner):
      p = clist[icorner]
      if p == "null":
         icell = -1
      else:
         try:
            icell = these_cells.index(p)
         except ValueError:
            print( "{2} {0} referenced by corner {1} cannot be found".format(p,icorner,col))
            break;
      newclist.append( str(icell) )
   corners[col] = newclist


clist = corners['prev']
newclist = []
for icorner in range(ncorner):
   p = clist[icorner]
   if p == "(nil)":
      inew = -1
   else:
      try:
         inew = these_corners.index(p)
      except ValueError:
         print( "Previous corner {0} referenced at corner {1} cannot be found".format(p,icorner))
         break;
   newclist.append( str(inew) )
corners['prev'] = newclist

print("Finding corners used in path")
clist = path['corner']
newclist = []
for ipath in range(npath):
   p = clist[ipath]
   if p == "(nil)":
      inew = -1
   else:
      try:
         inew = these_corners.index(p)
         corners['dist'][inew] = path['dist'][ipath]
      except ValueError:
         print( "Path corner {0} referenced at index {1} cannot be found".format(p,ipath))
         break;
   newclist.append( str(inew) )
path['corner'] = newclist





print("Finding scaling")
alo = 1.0E30
ahi = -1.0E30
for v in corners['ra']:
   v = float(v)
   if v < alo:
      alo = v
   if v > ahi:
      ahi = v
arange = ahi - alo

blo = 1.0E30
bhi = -1.0E30
for v in corners['dec']:
   v = float(v)
   if v < blo:
      blo = v
   if v > bhi:
      bhi = v

brange = bhi - blo

if arange > brange:
   ascale = box_size/arange
else:
   ascale = box_size/brange
aoffset = 0.5*( box_size - ascale*(ahi+alo) );
bscale = -ascale;
boffset = 0.5*( box_size - bscale*(bhi+blo) );




# ----------------------  create output HTML  ------------------------
fd = open("output.html", "w" )
fd.write( '''
<html>

<style>
* {
  box-sizing: border-box;
}

.grid {
  width: 75%;
  height: 300px;
  float: left;
  padding: 15px;
}

html, body {
	width:100%;
	height:100%;
}
.drawing {
	width:75%;
	height:100%;
	position:absolute;
	top:80px;left:0;
}
.svg-container {
	display: block;
	background-color: #BADA55;
	width:100%;
	height:100%;
}

.info {
  width: 20%;
  float: left;
  padding: 15px;
  font-size: 30px;
  font-family: "Courier New", Courier, monospace;
}

.top {
  width: 100%;
  height: 80px;
  float: top;
  padding: 15px;
  font-size: 30px;
}

input, button {
  font-size: 30px;
  margin: 4px 20px;
}

</style>

<script src="svg.js"></script>
<script src="svg-pan-zoom.js"></script>

</head>

<body>
<div class="top">
Path display controls:
<button onclick="resetpath()">Reset</button>
<button onclick="stoppath()">Stop</button>
<button onclick="prevpath()">Prev</button>
<button onclick="startpath(1)">Play</button>
<button onclick="startpath(0)">Reverse play</button>
<button onclick="nextpath()">Next</button>
<button onclick="recentre()">Recentre</button>
<button onclick="speedpath(1)">Faster</button>
<button onclick="speedpath(0)">Slower</button>
Go to:  <input onchange="gotopath()" type="number" id="goto" min="0">
<span id='pathtext'></span>
</div>
<div class="grid">
<div id="drawing" class="drawing"></div>
</div>
<div id="infoid" class="info"></div>
<script>
''')


# -------------------------------------------------------------------
writetab( fd, cells, "cells" );
writetab( fd, corners, "corners" );
writetab( fd, path, "path" );

fd.write("var bsize = {0};\n".format( box_size ) )
fd.write("var ascale = {0};\n".format( ascale ) )
fd.write("var bscale = {0};\n".format( bscale ) )
fd.write("var aoffset = {0};\n".format( aoffset ) )
fd.write("var boffset = {0};\n".format( boffset ) )
# -------------------------------------------------------------------


fd.write( '''
var dotradius=12;
var boxes = [];
var dots = [];
var curbox = -1;
var curdot = -1;
var path_stop = 0;
var path_len = path.corner.length;
var path_ipath = -1;
var path_playing = 0;
var pz;
var pause = 200;

function cornerPos( ic ) {
   return [ ascale*corners.ra[ic]+aoffset,
            bscale*corners.dec[ic]+boffset ];
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function startpath( fwd ){
   if( path_playing ) {
      stoppath();
   } else {
      path_playing = 1;
      path_stop = 0;
      var ipath;
      if( fwd != 0 ) {
         for( ipath=path_ipath+1; ipath<path_len && !path_stop; ipath++ ) {
            showpath(ipath);
            await sleep(pause);
         }
      } else {
         for( ipath=path_ipath; ipath>=0 && !path_stop; ipath-- ) {
            showpath(ipath);
            await sleep(pause);
         }
      }
      path_playing = 0;
   }
}

function stoppath(){
   path_stop = 1;
}

function resetpath(){
   if( path_playing != 0 ) {
      stoppath();
   } else {
      path_ipath = -1;
   }
}

function prevpath(){
   if( path_playing != 0 ) {
      stoppath();
   } else {
      showpath( path_ipath - 1 );
   }
}

function centreAt( icorner ){
   var o = cornerPos( icorner );
   var x_user = o[0];
   var y_user = o[1];

   o = pz.getSizes();
   var width_screen = o.width;
   var height_screen = o.height;
   var zoom = o.realZoom;
   o = o.viewBox;
   var xlo_user = o.x;
   var ylo_user = o.y;

   var px = width_screen/2 - ( x_user - xlo_user )*zoom;
   var py = height_screen/2 - ( y_user - ylo_user )*zoom;
   pz.pan({x:px,y:py});
}

function nextpath(){
   if( path_playing != 0 ) {
      stoppath();
   } else {
      showpath( path_ipath + 1 );
   }
}

function recentre(){
   if( curdot != -1 ) {
      centreAt( curdot );
   } else {
      pz.reset();
   }
}

function speedpath( up ){
   if( up == 0 ) {
      pause *= 1.3;
   } else {
      pause /= 1.3;
   }
}

function gotopath( ){
   if( path_playing != 0 ) {
      stoppath();
   } else {
      showpath( parseInt( document.getElementById("goto").value, 10 ) );
   }
}

function showpath( ipath ) {
   if( ipath >= 0 && ipath < path_len ) {
      document.getElementById("pathtext").innerText = ipath+"/"+path_len;
      path_ipath = ipath;
      ic = path.corner[ipath];
      CornerClicked( ic );

      if( path.dist[ipath] < 0 ) {
         stoppath();
         alert("Corner "+ic+" is the start of a new section (dist="+path.dist[ipath]+")" );
      }
   } else {
      setCurCorner( -1 );
   }
}

function setCurCell( ic ){
   if( curbox != -1 ) {
      if( cells.int[curbox] != 0 ) {
         col = '#BADA30';
      } else {
         col = '#7ADA30';
      }
      boxes[ curbox ].fill(col);
   }
   if( ic != -1 ) {
      boxes[ ic ].fill('#00F');
   }
   curbox = ic;
}

function setCurCorner( ic ){
   if( curdot != -1 ) {
      if( corners.int[curdot] != 0 ) {
         col = '#999';
      } else {
         col = '#000';
      }
      dots[ curdot ].fill(col);
      dots[ curdot ].stroke({ width: 1, color: col });
   }
   if( ic != -1 ) {
      dots[ ic ].fill('#F00');
      dots[ ic ].stroke({ width: 10, color: '#F00' });
   }
   curdot = ic;
}

function CellClicked( icell ){
   ShowCellInfo( icell );
   setCurCell( icell );
}

function ShowCellInfo( icell ){
   var corns = ['bl', 'tl', 'tr', 'br'];
   var html = "cell: "+icell+"<BR>";
   html += "&nbsp;&nbsp;prev: ";
   prev = cells['prev'][icell];
   if( prev != -1 ) {
      html += "<a href='javascript:CellClicked("+prev+");'>"+prev+"</a><BR>";
   } else {
      html += "BR";
   }
   var keys = Object.keys(cells).sort();
   var nkey = keys.length;
   var ikey;
   var key;
   for( ikey = 0; ikey < nkey; ikey++ ) {
      key = keys[ikey];
      if( !( corns.includes( key ) ) && key != 'prev' ){
         html += "&nbsp;&nbsp;"+key+": "+cells[key][icell]+"<BR>";
      }
   }
   html += "<BR>";
   for( ikey = 0; ikey < corns.length; ikey++ ) {
      key = corns[ikey];
      html += "&nbsp;&nbsp;"+key+": ";
      icorner = cells[key][icell];
      if( icorner != -1 ) {
         html += "<a href='javascript:setCurCorner("+icorner+");'>"+icorner+"</a><BR>";
      } else {
         html += "<BR>";
      }
   }

   document.getElementById("infoid").innerHTML = html;
}

function CornerClicked( icorner ){
   ShowCornerInfo( icorner );
   setCurCorner( icorner );
}

function ShowCornerInfo( icorner ){
   var cells = ['cell0', 'cell1', 'cell2', 'cell3'];
   var html = "corner: "+icorner+"<BR>";
   html += "&nbsp;&nbsp;prev: ";
   prev = corners['prev'][icorner];
   if( prev != -1 ) {
      html += "<a href='javascript:CornerClicked("+prev+");'>"+prev+"</a><BR>";
   } else {
      html += "BR";
   }
   var keys = Object.keys(corners).sort();
   var nkey = keys.length;
   var ikey;
   var key;
   for( ikey = 0; ikey < nkey; ikey++ ) {
      key = keys[ikey];
      if( !( cells.includes( key ) ) && key != 'prev' ){
         html += "&nbsp;&nbsp;"+key+": "+corners[key][icorner]+"<BR>";
      }
   }
   html += "<BR>";
   for( ikey = 0; ikey < cells.length; ikey++ ) {
      key = cells[ikey];
      html += "&nbsp;&nbsp;"+key+": ";
      icell = corners[key][icorner];
      if( icell != -1 ) {
         html += "<a href='javascript:setCurCell("+icell+");'>"+icell+"</a><BR>";
      } else {
         html += "<BR>";
      }
   }

   document.getElementById("infoid").innerHTML = html;
}

function Cell( draw, icell ){
   var verts = [];
   var icorner;
   for( icorner of [cells.bl[icell], cells.tl[icell], cells.tr[icell], cells.br[icell]] ){
      if( icorner >= 0 ) {
         verts.push(cornerPos(icorner));
      }
   }
   var box = draw.polygon( verts );
   if( cells.int[icell] != 0 ) {
      col = '#BADA30';
   } else {
      col = '#7ADA30';
   }
   box.fill(col).stroke({ width: 2 });
   box.style({ cursor: 'pointer' });
   box.on('click', function(){CellClicked(icell)} );
   boxes[ icell ] = box;
}


var xlo = 1.0E30;
var xhi = -1.0E30;
var ylo = 1.0E30;
var yhi = -1.0E30;

function Corner( draw, icorner ){
   var dot = draw.circle(dotradius);
   if( corners.int[icorner] != 0 ) {
      col = '#999';
   } else {
      col = '#000';
   }
   dot.fill(col);
   pos = cornerPos(icorner);

   if( pos[0] > xhi ) xhi = pos[0];
   if( pos[0] < xlo ) xlo = pos[0];
   if( pos[1] > yhi ) yhi = pos[1];
   if( pos[1] < ylo ) ylo = pos[1];

   dot.move(pos[0]-dotradius/2,pos[1]-dotradius/2);
   dot.style({ cursor: 'alias' });
   dot.stroke({ width: 1 });
   dot.on('click', function(){CornerClicked(icorner)} );
   dots[ icorner ] = dot;
}

onload = function() {
   var draw = SVG('drawing').size(bsize,bsize).attr('id','svg-container').addClass('svg-container');

   var n = cells.ra.length;
   var i;
   for( i = 0; i < n; i++ ) {
      Cell( draw, i );
   }

   n = corners.ra.length;
   for( i = 0; i < n; i++ ) {
      Corner( draw, i );
   }
   pz = svgPanZoom('#svg-container', {maxZoom: 20, minZoom: 0.1});
}

</script>
</body>
</html>
''')

# -----------------------------------------------------------------
fd.close()

