import java.util.*;

public
class Cycleizer implements Embedder {

protected Blackboard _bb;
public Cycleizer( Blackboard black ) {
	_bb = black;
}

Vector _visitedNodes;
Vector _theCycle;
Vector _theRest;

protected final void save_new_cycle( Node begin, Node end ) {
  if( end.level()-begin.level()+1 != _theCycleLength
      || _theCycle != null ) 
    return;

  _theCycle = new Vector();
  int b = _visitedNodes.indexOf(begin); 
  int e = _visitedNodes.indexOf(end); 
  for( int i = b; i <= e; ++i )
    _theCycle.addElement( _visitedNodes.elementAt(i) );
}

protected final void find_cycle( Node curr, int level ) {
  _visitedNodes.addElement( curr );
  curr.mark();
  curr.pick( true );
  curr.level( level );
  Vector outedges = curr.outedges();
  int outedgecnt = outedges.size();
  for (int i = 0; i < outedgecnt; ++i ) {
    Node n = ((Edge) outedges.elementAt(i)).to();
    if( n.picked() )       save_new_cycle( n, curr );
    else if( !n.marked() ) find_cycle( n, level+1 );
  }
  Vector inedges = curr.inedges();
  int inedgecnt = inedges.size();
  for (int i = 0; i < inedgecnt; ++i ) {
    Edge e = (Edge) inedges.elementAt(i);
    if( !e.reversed() ) continue;
    Node n = e.from();
    if( n.picked() )       save_new_cycle( n, curr );
    else if( !n.marked() ) find_cycle( n, level+1 );
  }
  curr.pick( false );
  _visitedNodes.removeElement( curr );
}

protected final void get_cycle() {
  _theCycle = null;
  _bb.unmarkNodes();
  Vector nodes = _bb.nodes();
  int nodecnt = nodes.size();
  _visitedNodes = new Vector();
  for (int i = 0; i < nodecnt; ++i ) {
    Node n = (Node) nodes.elementAt(i);
    if( !n.marked() ) find_cycle( n, 0 );      
    if( _theCycle != null ) break;
  }
}

protected final void get_fixed() {
  _theCycle = _bb.get_fixed();
  if( _theCycle.size() == 0 )
    _theCycle = null;
}

protected final void circularize() {
  if( _theCycle == null ) return;
	double rX = (_bb.ux()-_bb.lx())/2;
	double rY = (_bb.uy()-_bb.ly())/2;
	double theta = 0;
	Vector nodes = _theCycle;
	int nodecnt = nodes.size();
	double delta = 2*Math.PI / nodecnt;
	for (int i = 0; i < nodecnt; ++i ) {
		Node n = (Node) nodes.elementAt(i);
		n.XY( rX*Math.cos(theta), rY*Math.sin(theta) );
		theta += delta;
	}
}

protected final void get_rest() {
  if( _theCycle == null )
    _theRest = (Vector) _bb.nodes().clone();
  else {
    _theRest = new Vector();
    Vector nodes = _bb.nodes();
    int nodecnt = nodes.size();
    int pos = 0;
    for (int i = 0; i < nodecnt; ++i ) {
      Node n = (Node) nodes.elementAt(i);
      if( !_theCycle.contains( n ) ) {
        n.level( pos );                      // We use the level for pos. info
        _theRest.addElement( n );
        ++pos;
      }
      else
        n.level( -1 );                     // And it's -1 if node is in cycle
    }
  }
}

// See the GraphPack paper for a discussion of this
protected final void layout_rest() {
  int restcnt = _theRest.size();
  if( restcnt == 0 ) return;
  double M[][] = new double[restcnt][];
  for( int i = 0; i < restcnt; ++i )
    M[i] = new double[restcnt+2];      // The last 2 cols are the x and y's.

  // Fill in the matrix with -1.0 if i,j are adjacent, 0.0 otherwise (default)
  for( int i = 0; i < restcnt; ++i ) {
    Node n = (Node) _theRest.elementAt( i );
    int degree = 0;
    Vector outedges = n.outedges();
    int outedgecnt = outedges.size();
    for( int j = 0; j < outedgecnt; ++j ) {
      Node to = ((Edge) outedges.elementAt( j )).to();
      int col = to.level();
      if( col == -1 ) continue;
      M[i][col] = -1.0;
      ++degree;
    }
    Vector inedges = n.inedges();
    int inedgecnt = inedges.size();
    for( int j = 0; j < inedgecnt; ++j ) {
      Node from = ((Edge) inedges.elementAt( j )).from();
      int col = from.level();
      if( col == -1 ) continue;
      M[i][col] = -1.0;
      ++degree;
    }
    M[i][i] = degree+1;  // This was lacking in the GraphPack paper?!?!?!
  }
  
  // Fill in the x and y columns with the sum of adjacent nodes' x and y's.
  for( int i = 0; i < restcnt; ++i ) {
    Node u = (Node) _theRest.elementAt( i );
    double x = 0.0, y = 0.0;
    Vector inedges = u.inedges();
    int inedgecnt = inedges.size();
    for( int j = 0; j < inedgecnt; ++j ) {
      Node v = ((Edge) inedges.elementAt(j)).from();
      x += v.X();
      y += v.Y();
    }
    Vector outedges = u.outedges();
    int outedgecnt = outedges.size();
    for( int j = 0; j < outedgecnt; ++j ) {
      Node v = ((Edge) outedges.elementAt(j)).to();
      x += v.X();
      y += v.Y();
    }
    M[i][ restcnt ] = x;
    M[i][restcnt+1] = y;
  }

  // We do a Gauss-Jordan Upper Triangularization of the matrix
  int n = restcnt;   // Number of rows
  int m = restcnt+2; // Number of columns

  for( int i = 0; i < n-1; ++i ) {
		double fac = M[i][i];
		for( int j = 0; j < m; ++j )
      M[i][j] /= fac;
		for( int j = i+1; j < n; ++j ) {
			fac = M[j][i];
			for( int k = i; k < m; ++k ) 
        M[j][k] = M[j][k] - fac*M[i][k];
		}
	}

	double factor = M[n-1][n-1];
	for( int j = 0; j < m; ++j )
    M[n-1][j] /= factor;

  // We solve for the position of the vertices from the upper triangular matrix
	for( int i = n-1; i > 0; --i ) {
    for( int j = 0; j < i; ++j ) {
      double fac = M[j][i];
      for( int k = i-1; k < m; ++k )
        M[j][k] = M[j][k] - fac*M[i][k];
    }
  }
  for( int i = 0; i < n; ++i ) {
    Node u = (Node) _theRest.elementAt( i );
    u.XY( M[i][n]/M[i][i], M[i][n+1]/M[i][i] );
  }
}

protected synchronized final void cycleize() {
	Vector nodes = _bb.nodes();
	int nodecnt = nodes.size();
	for (int i = 0; i < nodecnt; ++i ) {
		Node n = (Node) nodes.elementAt(i);
    if( !n.fixed() )
      n.XY( 0.0, 0.0 );
	}
  get_fixed();
  if( _theCycle == null ) {
    get_cycle();
    circularize();
  }
  if( _theCycle != null ) {
    get_rest();
    layout_rest();
  }
}


	// Implementation of embedder interface, Init and Embed.
	//
protected int _theCycleLength;
public final void Init() {
  _bb.removeDummies();
	double L = _bb.globals.L();
	_bb.setArea( -L/2, -L/2, L/2, L/2 );
  _theCycleLength = _bb.globals.cycleLength();
	cycleize();
}

protected boolean _updated = false;
public final void Embed() {
	if( !_updated ) {
		_bb.Update();
		_updated = true;
	}
  int cycleLength = _bb.globals.cycleLength();
  if( cycleLength != _theCycleLength ) {
    double L = _bb.globals.L();
    _bb.setArea( -L/2, -L/2, L/2, L/2 );
    _theCycleLength = cycleLength;
    cycleize();
  }
}

} // class Circularizer

