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

#include <iostream>
#include <errno.h>
#include <string.h>

#include "Life.hxx"
#include "CatomWorld.hxx"
#include "CatomSim.hxx"

CODE_MODULE_DECLARATION( Life, Life );

using namespace std;

StateFile::Module* Life::StateFileConstructor() { signpost dprdebug_block_signpost(__FILE__,__LINE__,__FUNCTION__); 

  myModule_ = StateFile::Module::getInstance( name_ );

  // Check to see if this module has already been setup
  if (myModule_->getVersion())
    return myModule_;

  // If not, set up this module for the first time
  myModule_->setVersion( 1 );
  myModule_->setVersionRange( 1 );
  myModule_->setContentProfile( "cc" );
  myModule_->setContentNames( "model:done" );

  // NOTE: this is pre-allocating the buffer to a temp MAX size
  myModule_->getContent()->setLength( 5 );
  return myModule_;
}

void Life::loadModule() { signpost dprdebug_block_signpost(__FILE__,__LINE__,__FUNCTION__); 

  // This is required, was data read from the file
  if (!myModule_->getRead())
    return;
  // Buffer contains data appropriate for this (Catom,CodeModule)
  uint8 *buffer = myModule_->getContentData();

  // Error check your newly read data and do something with it

  // For example:
  assert( buffer[0] == (uint8)123 );
  uint32 *bar = (uint32*) &buffer[1];
  assert( *bar == 987654 );

  return;
}

void Life::saveModule() { signpost dprdebug_block_signpost(__FILE__,__LINE__,__FUNCTION__); 

  // Retrieve buffer to hold your new CodeModule data
  // The current contents of the buffer are undefined
  // See StateFile/ByteData for access details
  uint8 *buffer = myModule_->getContentData();

  if (buffer == NULL) {
    myModule_->getContent()->setLength( 5 );
    buffer = myModule_->getContentData();
  }
  
  // Write your new data to the buffer
  // This example works for static sized data
  // If you have variable sized data to write, use the
  // ByteData Accessors with myModule_->getContent() 
  uint32 bar = 987654;
  buffer[0] = 123;
  memcpy( &buffer[1], &bar, 4 );

  return;
}

void Life::newTick() { signpost dprdebug_block_signpost(__FILE__,__LINE__,__FUNCTION__); 
  
  Catom * me = &(worldPtr->catomHash[hostCatom]->C);
  CatomSim * catomSim = worldPtr->catomHash[hostCatom];

  /*
  // Bootstrap
  if((me->getLocation()).isEqual(Point3D(3,11,1)) ||
     (me->getLocation()).isEqual(Point3D(3,13,1)) ||
     (me->getLocation()).isEqual(Point3D(5,11,1)) ||
     (me->getLocation()).isEqual(Point3D(5,13,1)) ||
     (me->getLocation()).isEqual(Point3D(23,11,1)) ||
     (me->getLocation()).isEqual(Point3D(23,13,1)) ||
     (me->getLocation()).isEqual(Point3D(23,15,1)) ||
     (me->getLocation()).isEqual(Point3D(25,9,1)) ||
     (me->getLocation()).isEqual(Point3D(25,17,1)) ||
     (me->getLocation()).isEqual(Point3D(27,7,1)) ||
     (me->getLocation()).isEqual(Point3D(27,19,1)) ||
     (me->getLocation()).isEqual(Point3D(29,7,1)) ||
     (me->getLocation()).isEqual(Point3D(29,19,1)) ||
     (me->getLocation()).isEqual(Point3D(31,13,1)) ||
     (me->getLocation()).isEqual(Point3D(33,9,1)) ||
     (me->getLocation()).isEqual(Point3D(33,17,1)) ||
     (me->getLocation()).isEqual(Point3D(35,11,1)) ||
     (me->getLocation()).isEqual(Point3D(35,13,1)) ||
     (me->getLocation()).isEqual(Point3D(35,15,1)) ||
     (me->getLocation()).isEqual(Point3D(37,13,1)) ||
     (me->getLocation()).isEqual(Point3D(43,11,1)) ||
     (me->getLocation()).isEqual(Point3D(43,9,1)) ||
     (me->getLocation()).isEqual(Point3D(43,7,1)) ||
     (me->getLocation()).isEqual(Point3D(45,11,1)) ||
     (me->getLocation()).isEqual(Point3D(45,9,1)) ||
     (me->getLocation()).isEqual(Point3D(45,7,1)) ||
     (me->getLocation()).isEqual(Point3D(47,5,1)) ||
     (me->getLocation()).isEqual(Point3D(47,13,1)) ||
     (me->getLocation()).isEqual(Point3D(51,3,1)) ||
     (me->getLocation()).isEqual(Point3D(51,5,1)) ||
     (me->getLocation()).isEqual(Point3D(51,13,1)) ||
     (me->getLocation()).isEqual(Point3D(51,15,1)) ||
     (me->getLocation()).isEqual(Point3D(71,7,1)) ||
     (me->getLocation()).isEqual(Point3D(71,9,1)) ||
     (me->getLocation()).isEqual(Point3D(73,7,1)) ||
     (me->getLocation()).isEqual(Point3D(73,9,1))) me->setColor(0,0,0,0);
  else
    me->setColor(0,0,0,255);
  return;
  */

  int n1,n2,n3,n4,n5,n6,n7,n8 = 0;

  // Collect Data about my neighbors
  n1 = HOSTCATOM.getNeighbor(2);
  n2 = HOSTCATOM.getNeighbor(3);
  n3 = HOSTCATOM.getNeighbor(4);
  n4 = HOSTCATOM.getNeighbor(5);

  // If I'm an edge, I die and I'm done.
  if (!n1 || !n2 || !n3 || !n4) {
    me->setColor(0,0,0,255); 
    return;
  }

  if(n1 > 0) n5 = worldPtr->catomHash[n1]->C.getNeighbor(3);
  if(n1 > 0) n6 = worldPtr->catomHash[n1]->C.getNeighbor(4);
  if(n4 > 0) n7 = worldPtr->catomHash[n4]->C.getNeighbor(3);
  if(n4 > 0) n8 = worldPtr->catomHash[n4]->C.getNeighbor(4);


  int numNeighbors = 0;

  if( worldPtr->catomHash[n1]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n2]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n3]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n4]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n5]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n6]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n7]->alpha == 0 )
    numNeighbors++;
  if( worldPtr->catomHash[n8]->alpha == 0 )
    numNeighbors++;

  // if( numNeighbors > 0 ) cout << "NumNeighbors: " << numNeighbors << endl;
  
  // Reach intermediate Step and set color
  // Death
  if( numNeighbors > 3 || numNeighbors < 2 ) catomSim->red = 255;
  // Rebirth
  if( numNeighbors == 3 ) catomSim->blue = 255;

}


void Life::endTick() { signpost dprdebug_block_signpost(__FILE__,__LINE__,__FUNCTION__); 

  Catom * me = &(worldPtr->catomHash[hostCatom]->C);
  CatomSim * catomSim = worldPtr->catomHash[hostCatom];
  
  // check my color and set transparancy
  if( catomSim->blue == 255 ) me->setColor(0,0,0,0);
  if( catomSim->red == 255 ) me->setColor(0,0,0,255);

}
