/******************************************************************************
 *                         Create Cluster Graph                               *
 ******************************************************************************/
static
void create_cluster_graph(Graph::Graph& g,
    std::vector<int> clusters,
    int nCluster,
    std::vector<WgtType>& radii,
    Graph::Graph& cg)
{
    // Steps
    //   1. get the linkage between cluster
    //   2. link the edge
    using namespace std;
    vector< vector<int> > cls_connection(nCluster, vector<int>(nCluster, 0));
    vector<int> cls_nodes;
    vector<int> nbors;


    // Step 1
    for (int c=0; c<nCluster; ++c)
    {
        cls_nodes.clear();
        cls_nodes.resize(0);

        for (int i=0; i<g.get_num_vtxs(); ++i)
            if (clusters.at(i) == c) cls_nodes.push_back(i);


        for (int i=0; i<cls_nodes.size(); ++i)
        {
            nbors = g.adj(cls_nodes.at(i));
            for (int n=0; n<nbors.size(); ++n)
            {
                if (clusters.at(nbors.at(n)) != c)
                    cls_connection.at(c).at(clusters.at(nbors.at(n))) += 1;
            }
        }
    }
    // Step 2
    for (int i=0; i<nCluster; ++i)
    {
        for (int j=i+1; j<nCluster; ++j)
            if (cls_connection.at(i).at(j) != 0) cg.add_edge(i, j, radii.at(i) + radii.at(j));
    }
}
static
void force_direct_with_torque(char* outfilePath, Graph::Graph& g, Graph::Graph& cg,
	std::vector< std::pair<int, int> > c_edges,
	DenseMat& cluster_dist_mat,
	std::vector<int>& clusters,
	std::vector< std::vector<CoordType> >& center_coord,
	std::vector< WgtType >& radii,
	std::vector< std::vector<CoordType> >& coord,
	int iteration,
	double initStep)
{

	// Iteration Steps
	// 1. calculate repulsive displacement
	// 2. calculate attractive displacement
	// 3. calculate rotate angle
	// 4. shift and rotate the cluster nodes and corresponding nodes
	using namespace std;
	using namespace boost::posix_time;

	// algorithm declaration
	double step = initStep;
	double curr_energy = INFINITY_ENERGY;
	double pre_energy;

	// for iteration dumping
	fstream fo;

	// [modified!] clusters_nodes
	vector<VtxType> cluster_nodes; // id: vtx id of clustered graph
                                  // val: vtx id in whole graph

	// force equilibrium declaration
	vector< pair<WgtType, WgtType> > node_disp(cg.get_num_vtxs());
	vector< WgtType > rotate_angles(cg.get_num_vtxs());
	pair<WgtType, WgtType> pos_diff;
	double rep_force;
	double att_force;
	double disp_x;
	double disp_y;
	double disp_val;
	int e_u;  // u in c-edge (u, v)
	int e_v;  // v in c-edge (u, v)
	double disp_u_x;
	double disp_u_y;
	double disp_v_x;
	double disp_v_y;
	

	CoordType c_x;  // center of u's x coord
    CoordType c_y;
    CoordType new_x; // relative to center nodes' x coordinates
    CoordType new_y; // relative to center nodes' x coordinates

    double angle;   // perform rotation for current node

    // dump before force process
    fo.open("out/"+string(outfilePath)+"_start.coord", fstream::out);
	for (int i=0; i<coord.size(); ++i)
	{
		fo << coord.at(i).at(0) << " " << coord.at(i).at(1) << endl;
	}
	fo.close();
	// center coord
	fo.open("out/"+string(outfilePath)+"_start.center", fstream::out);
	for (int i=0; i<center_coord.size(); ++i)
	{
		fo << center_coord.at(i).at(0) << " " << center_coord.at(i).at(1) << endl;
	}
	fo.close();

	// angle
	fo.open("out/"+string(outfilePath)+"_start.angle", fstream::out);
	for (int i=0; i<rotate_angles.size(); ++i)
	{
		fo << rotate_angles.at(i) << endl;
	}
	fo.close();

	for (int t=0; t<iteration; t++)
	{
		pre_energy = curr_energy;
		curr_energy = 0;
		// step 1: repulsive force placement
		for (int i=0; i<cg.get_num_vtxs(); ++i)
		{
			node_disp.at(i) = make_pair(0, 0);
			for (int j=0; j<cg.get_num_vtxs(); ++j)
			{
				if (i != j)
				{
					pos_diff = make_pair(center_coord.at(i).at(0)-center_coord.at(j).at(0),
										 center_coord.at(i).at(1)-center_coord.at(j).at(1));
					rep_force = repulsive_force(pos_diff, radii.at(i), radii.at(j));
					disp_x = node_disp.at(i).first + pos_diff.first/pos_diff_dist(pos_diff)*rep_force;
					disp_y = node_disp.at(i).second + pos_diff.second/pos_diff_dist(pos_diff)*rep_force;
					node_disp.at(i) = make_pair(disp_x+node_disp.at(i).first, disp_y+node_disp.at(i).second);
					if (t==0)
					{
						cout << "pos diff of cv_" << i << " = " << pos_diff.first << ", " << pos_diff.second << endl;	
						cout << "repulsive_force of cv_" << i << " = " << rep_force << endl;
						cout << "disp = " << disp_x << ", " << disp_y << endl;
						cout << "node disp = " << node_disp.at(i).first << ", " << node_disp.at(i).second << endl;
					} 
					
				}
			}

		}

		// step 2: attractive force placement
		for (int e=0; e<c_edges.size(); ++e)
		{
			e_u = c_edges.at(e).first;
			e_v = c_edges.at(e).second;

			// \delta <- e.u.pos - e.v.pos
			pos_diff = make_pair(center_coord.at(e_u).at(0)-center_coord.at(e_v).at(0),
								 center_coord.at(e_u).at(1)-center_coord.at(e_v).at(1));

			att_force = attractive_force(pos_diff, radii.at(e_u), radii.at(e_v));

			// e.u.disp <- e.u.disp - unit-direction * att_force(abs(\delta))
			disp_u_x = node_disp.at(e_u).first - pos_diff.first/pos_diff_dist(pos_diff)*att_force;
			disp_u_y = node_disp.at(e_u).second - pos_diff.second/pos_diff_dist(pos_diff)*att_force;
			node_disp.at(e_u) = make_pair(disp_u_x+node_disp.at(e_u).first, disp_u_y+node_disp.at(e_u).second);

			// e.v.disp <- e.v.disp - unit-direction * att_force(abs(\delta))
			disp_v_x = node_disp.at(e_v).first + pos_diff.first/pos_diff_dist(pos_diff)*att_force;
			disp_v_y = node_disp.at(e_v).second + pos_diff.second/pos_diff_dist(pos_diff)*att_force;
			node_disp.at(e_v) = make_pair(disp_v_x+node_disp.at(e_v).first, disp_v_y+node_disp.at(e_v).second);
			if (t==0)
			{
				cout << "pos diff of cv_" << e << " = " << pos_diff.first << ", " << pos_diff.second << endl;	
				cout << "attractive_force of cv_" << e << " = " << att_force << endl;
				cout << "disp = " << disp_v_x << ", " << disp_v_y << endl;
			} 
		}

		// step 3: torque equilibrium
		fill(rotate_angles.begin(), rotate_angles.end(), 0);
		for (int i=0; i<cg.get_num_vtxs(); ++i)
		{
			cluster_nodes.resize(0);
        	for (int c=0; c<clusters.size(); ++c)
            	if (clusters.at(c) == i) cluster_nodes.push_back(c);
			
			calculate_rotate_angle(g, i, cluster_nodes, clusters,
				coord, center_coord, radii, rotate_angles);

		}

		// step 4: shift and rotate the cluster nodes and corresponding nodes
		for (int i=0; i<cg.get_num_vtxs(); ++i)
		{
			cluster_nodes.resize(0);
        	for (int c=0; c<clusters.size(); ++c)
            	if (clusters.at(c) == i) cluster_nodes.push_back(c);

			disp_val = sqrt(pow(node_disp.at(i).first, 2) + pow(node_disp.at(i).second, 2));
			c_x = center_coord.at(i).at(0);
			c_y = center_coord.at(i).at(1);
			c_x += step * node_disp.at(i).first/disp_val;
			c_y += step * node_disp.at(i).second/disp_val;

			for (int j=0; j<cluster_nodes.size(); ++j)
			{
				angle = rotate_angles.at(i);
				// shift nodes
				coord.at(cluster_nodes.at(j)).at(0) -= center_coord.at(i).at(0);
				coord.at(cluster_nodes.at(j)).at(1) -= center_coord.at(i).at(1);

				// rotate nodes
				new_x = coord.at(cluster_nodes.at(j)).at(0);
        		new_y = coord.at(cluster_nodes.at(j)).at(1);
				coord.at(cluster_nodes.at(j)).at(0) = new_x*cos(angle) + new_y*sin(angle);
        		coord.at(cluster_nodes.at(j)).at(1) = -new_x*sin(angle) + new_y*cos(angle);

				// match to new center
				coord.at(cluster_nodes.at(j)).at(0) += c_x;
				coord.at(cluster_nodes.at(j)).at(1) += c_y;

			}

			center_coord.at(i).at(0) = c_x;
			center_coord.at(i).at(1) = c_y;


		}
		

		// update step
		calculate_energy(cluster_dist_mat, center_coord, curr_energy);
		update_step(step, pre_energy, curr_energy);

		cout << "curr_energy=" << curr_energy << endl;
		cout << "step=" << step << endl;

		// check convergence


		// dump information of each iteration
		if (t < 5)
		{
			// coordinates
			fo.open("out/"+string(outfilePath)+"_"+to_string(t)+".coord", fstream::out);
			for (int i=0; i<coord.size(); ++i)
			{
				fo << coord.at(i).at(0) << " " << coord.at(i).at(1) << endl;
			}
			fo.close();

			// center coord
			fo.open("out/"+string(outfilePath)+"_"+to_string(t)+".center", fstream::out);
			for (int i=0; i<center_coord.size(); ++i)
			{
				fo << center_coord.at(i).at(0) << " " << center_coord.at(i).at(0) << endl;
			}
			fo.close();

			// angle
			fo.open("out/"+string(outfilePath)+"_"+to_string(t)+".angle", fstream::out);
			for (int i=0; i<rotate_angles.size(); ++i)
			{
				fo << rotate_angles.at(i) << endl;
			}
			fo.close();
		}
	}

}