#include <iostream>
#include <ctype.h>
#include <assert.h>
#include <unordered_map>

#include "Field.hh"
#include "Vector.hh"
#include "Symmetry.hh"
#include "PointConfiguration.hh"
#include "Permutation.hh"
#include "IntegerSet.hh"

namespace topcom {
  Vector cycle_coords_1234(const Vector& vec) {
    Vector result(4);
    result[1] = vec[0];
    result[2] = vec[1];
    result[3] = vec[2];
    result[0] = vec[3];
    return result;
  }
  Vector cycle_coords_123(const Vector& vec) {
    Vector result(4);
    result[1] = vec[0];
    result[2] = vec[1];
    result[0] = vec[2];
    result[3] = vec[3];
    return result;
  }
  Vector rotate_planes_12_34(const Vector& vec) {
    Vector result(4);
    result[0] = -vec[1];
    result[1] = vec[0];
    result[2] = -vec[3];
    result[3] = vec[2];
    return result;
  }
};


int main(const int argc, const char** argv) {
  using namespace topcom;

  CommandlineOptions::init(argc, argv);

  PointConfiguration result;
  std::set<Vector> pointset;

  // origin is point 0:
  Vector origin(4, FieldConstants::ZERO);
  result.augment(origin);
  pointset.insert(origin);

  // we start with a unit vector in R^4 and its double:
  Vector rootpoint = unit_vector(4, 0);
  result.augment(rootpoint);
  pointset.insert(rootpoint);
  Vector scaled_rootpoint = rootpoint;
  scaled_rootpoint.scale(Field("2"));
  result.augment(scaled_rootpoint);
  pointset.insert(scaled_rootpoint);

  // Vector seedpoint(4);
  // seedpoint(0) = Field("0");
  // seedpoint(1) = Field("4/3");
  // seedpoint(2) = Field("4/3");
  // seedpoint(3) = Field("0");

  // result.augment(seedpoint);
  // pointset.insert(seedpoint);

  Vector steinerpoint(4);
  steinerpoint(0) = Field("1/10");
  steinerpoint(1) = Field("1/10");
  steinerpoint(2) = Field("1/10");
  steinerpoint(3) = Field("0");

  result.augment(steinerpoint);
  pointset.insert(steinerpoint);
  
  PointConfiguration old_points = result;

  // next, compute the orbit w.r.t. cyclic permutation of
  // the first three coordinates
  // and the simultaneous rotation in planes 1-2 and 3-4:
  bool newpoint_found = true;
  std::set<Vector>::const_iterator finditer;
  while (newpoint_found) {
    newpoint_found = false;
    PointConfiguration new_points;
    for (parameter_type pointidx = 0; pointidx < old_points.size(); ++pointidx) {
      Vector cycled_point = cycle_coords_123(old_points[pointidx]);
      finditer = pointset.find(cycled_point);
      if (finditer == pointset.end()) {
       	newpoint_found = true;
       	new_points.augment(cycled_point);
       	pointset.insert(cycled_point);
      }
      Vector rotated_point = rotate_planes_12_34(old_points[pointidx]);
      finditer = pointset.find(rotated_point);
      if (finditer == pointset.end()) {
	newpoint_found = true;
	new_points.augment(rotated_point);
	pointset.insert(rotated_point);
      }
    }
    result.augment(new_points);
    old_points.swap(new_points);
    new_points.clear();
  }

  // next, compute the two symmetry generators in terms of point indices:
  symmetry_collectordata collector;
  // Symmetry cycle_sym(result.no());
  // for (parameter_type pointidx = 0; pointidx < result.no(); ++pointidx) {
  //   Vector cycled_point = cycle_coords_123(result[pointidx]);
  //   MessageStreams::debug() << "cycled point " << cycled_point
  // 			    << " of point " << result[pointidx]
  // 			    << " at index " << pointidx
  // 			    << " has index ";
  //   cycle_sym[pointidx] = result.index_of_point(cycled_point).second;
  //   MessageStreams::debug() << cycle_sym[pointidx] << std::endl;
  // }
  // collector.insert(cycle_sym);
  Symmetry rotate_sym(result.no());
  for (parameter_type pointidx = 0; pointidx < result.no(); ++pointidx) {
    Vector rotated_point = rotate_planes_12_34(result[pointidx]);
    MessageStreams::debug() << "rotated point " << rotated_point
			    << " of point " << result[pointidx]
			    << " at index " << pointidx
			    << " has index ";
    rotate_sym[pointidx] = result.index_of_point(rotated_point).second;
    MessageStreams::debug() << rotate_sym[pointidx] << std::endl;
  }
  collector.insert(rotate_sym);
  SymmetryGroup symmetries(result.no(), collector, false);

  // print results:
  result.homogenize();
  MessageStreams::result() << result << std::endl;

  symmetries.write_sorted_generators(MessageStreams::result());
  MessageStreams::result() << std::endl;

  symmetries.write_sorted_generators(MessageStreams::result());
  MessageStreams::result() << std::endl;
  return 0;
}

// eof kDn.cc
