///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Copyright (C) 2006 by Intel Corporation and Carnegie Mellon University    //
// Contacts: casey.j.helfrich @ intel.com                                    //
//           bdr @ cs.cmu.edu                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "FeatureMap.hxx"
#include "CatomWorld.hxx"
#include "DPRSim.hxx"

////////////////////////////////////////////////////////////////////////////
// Feature Methods

/* This is the old code; new code in .hxx file
Feature* Feature::getRemoteFeature() {
  
  if DPR_FEATUREMAP_DEBUG
    cout << "issuing getRemoteFeature(" << FID << ") for Catom (" 
	 << hostCatomID << ")" << endl;
  
  if(FID > NUM_FEATURES+1) {
    if DPR_FEATUREMAP_DEBUG
      cout << "Invalid FID!" << endl;
    return NULL;
  }
  
  catomID neighborCatom = worldPtr->catomHash[hostCatomID]->C.getNeighbor(FID);
  
  if (neighborCatom == 0) return NULL;
  
  //return &(worldPtr->catomHash[neighborCatom]->C.getFeatureMap()[NUM_FEATURES - FID + 1]);

  featureID nfid = worldPtr->catomHash[neighborCatom]->C.getFeatureTouching( hostCatomID );

  if ( nfid==0 ) return NULL;

  return &(worldPtr->catomHash[neighborCatom]->C.getFeatureMap()[nfid]);
}

*/


Point3D *Feature::locations = 0;
vector<Point3D> *Feature::faces = 0;



//Feature::Feature(catomID hostCatom, featureID F, Point3D loc) :
//  hostCatomID(hostCatom), FID(F), location(loc), NA(hostCatom, F)
Feature::Feature(catomID hostCatom, featureID F) :
  hostCatomID(hostCatom), FID(F), NA(hostCatom, F)
{

}

Feature::~Feature() {

}


static void setup_feature_map( string name, Point3D **centers, vector<Point3D> **faces, unsigned int *num_features, unsigned int *num_neighbors ) {

  if ( name=="square" ) name = "2D 4";
  if ( name=="hex" ) name = "2D 6";
  if ( name=="fcc" ) {
    *num_features = 12;
    *num_neighbors = 12;
    *centers = new Point3D[13];
    *faces = new vector<Point3D>[13];
    (*centers)[1] = 0.70710678*Point3D(-1,0,-1);
      (*faces)[1].push_back( 0.70710678*Point3D( 0, 0,-2) );
      (*faces)[1].push_back( 0.70710678*Point3D(-1,-1,-1) );
      (*faces)[1].push_back( 0.70710678*Point3D(-2, 0, 0) );
      (*faces)[1].push_back( 0.70710678*Point3D(-1, 1,-1) );
    (*centers)[2] = 0.70710678*Point3D(0,-1,-1);
      (*faces)[2].push_back( 0.70710678*Point3D( 0, 0,-2) );
      (*faces)[2].push_back( 0.70710678*Point3D(-1,-1,-1) );
      (*faces)[2].push_back( 0.70710678*Point3D( 0,-2, 0) );
      (*faces)[2].push_back( 0.70710678*Point3D( 1,-1,-1) );
    (*centers)[3] = 0.70710678*Point3D(1,0,-1);
      (*faces)[3].push_back( 0.70710678*Point3D( 0, 0,-2) );
      (*faces)[3].push_back( 0.70710678*Point3D( 1,-1,-1) );
      (*faces)[3].push_back( 0.70710678*Point3D( 2, 0, 0) );
      (*faces)[3].push_back( 0.70710678*Point3D( 1, 1,-1) );
    (*centers)[4] = 0.70710678*Point3D(0,1,-1);
      (*faces)[4].push_back( 0.70710678*Point3D( 0, 0,-2) );
      (*faces)[4].push_back( 0.70710678*Point3D( 1, 1,-1) );
      (*faces)[4].push_back( 0.70710678*Point3D( 0, 2, 0) );
      (*faces)[4].push_back( 0.70710678*Point3D(-1, 1,-1) );
    (*centers)[5] = 0.70710678*Point3D(-1,-1,0);
      (*faces)[5].push_back( 0.70710678*Point3D(-2, 0, 0) );
      (*faces)[5].push_back( 0.70710678*Point3D(-1,-1,-1) );
      (*faces)[5].push_back( 0.70710678*Point3D( 0,-2, 0) );
      (*faces)[5].push_back( 0.70710678*Point3D(-1,-1, 1) );
    (*centers)[6] = 0.70710678*Point3D(-1,1,0);
      (*faces)[6].push_back( 0.70710678*Point3D(-2, 0, 0) );
      (*faces)[6].push_back( 0.70710678*Point3D(-1, 1,-1) );
      (*faces)[6].push_back( 0.70710678*Point3D( 0, 2, 0) );
      (*faces)[6].push_back( 0.70710678*Point3D(-1, 1, 1) );
    (*centers)[7] = 0.70710678*Point3D(1,1,0);
      (*faces)[7].push_back( 0.70710678*Point3D( 2, 0, 0) );
      (*faces)[7].push_back( 0.70710678*Point3D( 1, 1,-1) );
      (*faces)[7].push_back( 0.70710678*Point3D( 0, 2, 0) );
      (*faces)[7].push_back( 0.70710678*Point3D( 1, 1, 1) );
    (*centers)[8] = 0.70710678*Point3D(1,-1,0);
      (*faces)[8].push_back( 0.70710678*Point3D( 2, 0, 0) );
      (*faces)[8].push_back( 0.70710678*Point3D( 1,-1,-1) );
      (*faces)[8].push_back( 0.70710678*Point3D( 0,-2, 0) );
      (*faces)[8].push_back( 0.70710678*Point3D( 1,-1, 1) );
    (*centers)[9] = 0.70710678*Point3D(-1,0,1);
      (*faces)[9].push_back( 0.70710678*Point3D( 0, 0, 2) );
      (*faces)[9].push_back( 0.70710678*Point3D(-1,-1, 1) );
      (*faces)[9].push_back( 0.70710678*Point3D(-2, 0, 0) );
      (*faces)[9].push_back( 0.70710678*Point3D(-1, 1, 1) );
    (*centers)[10] = 0.70710678*Point3D(0,-1,1);
      (*faces)[10].push_back( 0.70710678*Point3D( 0, 0, 2) );
      (*faces)[10].push_back( 0.70710678*Point3D(-1,-1, 1) );
      (*faces)[10].push_back( 0.70710678*Point3D( 0,-2, 0) );
      (*faces)[10].push_back( 0.70710678*Point3D( 1,-1, 1) );
    (*centers)[11] = 0.70710678*Point3D(1,0,1);
      (*faces)[11].push_back( 0.70710678*Point3D( 0, 0, 2) );
      (*faces)[11].push_back( 0.70710678*Point3D( 1,-1, 1) );
      (*faces)[11].push_back( 0.70710678*Point3D( 2, 0, 0) );
      (*faces)[11].push_back( 0.70710678*Point3D( 1, 1, 1) );
    (*centers)[12] = 0.70710678*Point3D(0,1,1);
      (*faces)[12].push_back( 0.70710678*Point3D( 0, 0, 2) );
      (*faces)[12].push_back( 0.70710678*Point3D( 1, 1, 1) );
      (*faces)[12].push_back( 0.70710678*Point3D( 0, 2, 0) );
      (*faces)[12].push_back( 0.70710678*Point3D(-1, 1, 1) );
  }
  else if ( name=="hcp" ) {
    *num_features = 12;
    *num_neighbors = 12;
  }
  else if ( name.c_str()[0] == '2' && name.c_str()[1] == 'D' 
            && name.c_str()[2] == ' ' && name.length() > 3 ) {
    *num_features = strtol( &(name.c_str()[3]), 0, 0 );
    if (*num_features<3) *num_features=3;
    *num_neighbors = 6;
    *centers = new Point3D[*num_features+1];
    *faces = new vector<Point3D>[*num_features+3];
    double theta = 2*M_PI/ (double)*num_features;
    double frad = 1.0 / cos( theta/2.0 );
    unsigned int n;
    for ( n=1; n<=*num_features; n++ ) {
      (*centers)[n] = Point3D( 1, 0, theta*((double)n-1), 1 );
      Point3D tmp = Point3D( frad, 0, theta*((double)n-1.5), 1);
      (*faces)[n].push_back( tmp+Point3D(0,0,1) );
      (*faces)[n].push_back( tmp+Point3D(0,0,-1) );
      (*faces)[*num_features+1].push_back( tmp+Point3D(0,0,1) );
      (*faces)[*num_features+2].push_back( tmp+Point3D(0,0,-1) );
      tmp = Point3D( frad, 0, theta*((double)n-0.5), 1);
      (*faces)[n].push_back( tmp+Point3D(0,0,-1) );
      (*faces)[n].push_back( tmp+Point3D(0,0,1) );
    }
  }
  else {  // cube
    *num_features = 6;
    *num_neighbors = 12;
    *centers = new Point3D[7];
    *faces = new vector<Point3D>[7];
    (*centers)[1] = Point3D(0,0,1); 
      (*faces)[1].push_back(Point3D(-1,-1,1));
      (*faces)[1].push_back(Point3D(-1,1,1)); 
      (*faces)[1].push_back(Point3D(1,1,1));
      (*faces)[1].push_back(Point3D(1,-1,1));
    (*centers)[2] = Point3D(1,0,0);
      (*faces)[2].push_back(Point3D(1,-1,-1)); 
      (*faces)[1].push_back(Point3D(1,-1,1)); 
      (*faces)[2].push_back(Point3D(1,1,1)); 
      (*faces)[1].push_back(Point3D(1,1,-1));
    (*centers)[3] = Point3D(0,1,0);
      (*faces)[3].push_back(Point3D(-1,1,-1)); 
      (*faces)[1].push_back(Point3D(-1,1,1)); 
      (*faces)[3].push_back(Point3D(1,1,1)); 
      (*faces)[1].push_back(Point3D(1,1,-1));
    (*centers)[4] = Point3D(0,-1,0);
      (*faces)[4].push_back(Point3D(-1,-1,-1)); 
      (*faces)[1].push_back(Point3D(-1,-1,1)); 
      (*faces)[4].push_back(Point3D(1,-1,1)); 
      (*faces)[1].push_back(Point3D(1,-1,-1));
    (*centers)[5] = Point3D(-1,0,0);
      (*faces)[5].push_back(Point3D(-1,-1,-1)); 
      (*faces)[1].push_back(Point3D(-1,-1,1)); 
      (*faces)[5].push_back(Point3D(-1,1,1)); 
      (*faces)[1].push_back(Point3D(-1,1,-1));
    (*centers)[6] = Point3D(0,0,-1);
      (*faces)[6].push_back(Point3D(-1,-1,-1)); 
      (*faces)[1].push_back(Point3D(-1,1,-1)); 
      (*faces)[6].push_back(Point3D(1,1,-1)); 
      (*faces)[1].push_back(Point3D(1,-1,-1));
  }
  
}


void get_lattice_info( string name, unsigned int *lattice_type, unsigned int *num_features, unsigned int *num_neighbors ) {
  if (Feature::locations) { delete [] Feature::locations; }
  if (Feature::faces) { delete [] Feature::faces; }
  setup_feature_map( name, &Feature::locations, &Feature::faces, num_features, num_neighbors );
}



void get_feature_map( unsigned int lattice_type, catomID i, Feature *map ) {
  // need to test and adjust locations if not faceted (but only once)
  static bool initialized = false;
  if (!initialized) {
    
    initialized = true;
  }
  // simply make n features
  unsigned int n;
  for ( n=1; n<=NUM_FEATURES; n++ ) {
    map[n] = Feature( i, n );
  }
}

