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

#ifndef __CATOM_HXX
#define __CATOM_HXX

#include <pthread.h>
#include <iostream>
#include <sstream>
#include <queue>
#include <map>
#include <set>
#include <ext/hash_map>

#include "CodeModule.hxx"
#include "GlobalConstants.hxx"
#include "Primitives.hxx"
#include "FeatureMap.hxx"
#include "Network.hxx"
#include "StateFile/ByteData.hxx"

#include <ode/ode.h>

using namespace std;
using namespace __gnu_cxx;

typedef bool(CodeModule::*msgHandlerPtr)(Message*);

///////////////////////////////////////////////////////////////////////////////
// MailboxManager
//
// The class that contains all of the queues and data structure for messages
// once they arrive on a Catom
//

class MailboxManager {
private:
  map<MailboxID, pair<CodeModule*, msgHandlerPtr> > messageHandlers;
  map<MailboxID, queue<Message*> > mailboxes;
  map<MailboxID, queue<Message*> > messagesForNextTick;
  map<MailboxID, bool> boxHasDelay;
  
  pthread_mutex_t boxesLock;
  void lock();
  void unlock();
  
public:
  MailboxManager();
  ~MailboxManager();
  
  // called at the beginning of each new tick to do upkeep
  void newTick();
  
  // inserts the given message into the appropriate queue
  bool fileMessage(Message*);
  
  // calls handler for any arbitrary message
  // called from within catom code OS thread
  // returns: whether a message was handled
  bool handleMessage();

  // calls handler for a particular mailbox
  // returns: whether a message was handled
  bool handleMessage(MailboxID);

  // immediately calls hander for a particular message
  // doesn't involve mailboxes, just dispatches
  bool handleMessage(Message*);
  
  // defines the handler for a particular mailbox name
  void registerHandler(MailboxID, CodeModule*, msgHandlerPtr, bool hasDelay=true);
};

class SpeculationManager {
public:
  SpeculationManager(catomID _hostCatom);
  ~SpeculationManager();

  void newTick();
  void simulationEnd();
  void dumpStats();
  
  featureID speculateDestFromSource(featureID source);
  void messageRouted(featureID source, featureID dest);

private:
  catomID hostCatom;
  map<featureID, unsigned long long> lastUsed;
  map<featureID, map<featureID, unsigned long long> > history;

  unsigned long long specLastUsedCorrect;
  unsigned long long specLastUsedWrong;
  unsigned long long specMostUsedCorrect;
  unsigned long long specMostUsedWrong;
};


///////////////////////////////////////////////////////////////////////////////
// Catom
// 
// The REAL Catom class, only contains information that a catom will have in
// the real world.  All other data is stored in the CatomSim wrapper.
//

class Catom {
private:
  
  // Private Data includes the unique Catom ID, battery life, a description
  // of the catom's surface (includes networking), and a dynamic array
  // of features that are in contact with something.
  
  catomID ID;
  double batteryLife;
  Feature *map;
  featureID *neighbors;
  catomID *neighborID;
  bool sel;
  
public:
  
  MailboxManager mailboxManager;
  SpeculationManager speculationManager;
  
  // return the unique catomID of this Catom  
  catomID getID();
  
  // return the current absolute 3D location of this Catom
  WorldPoint3D getLocation();
  
  // return feature closest to the absolute position p
  featureID getNearestFeature( Point3D p, catomID hostid=0 );

  // return feature touching catom neighborid
  featureID getFeatureTouching( catomID neighborid, catomID hostID=0 );
  
  // give access to the map of the surface of this Catom
  Feature* getFeatureMap();

  // return my feature FID
  Feature* getFeature(featureID f);

  // return the current battery life of this Catom
  double getBatteryLife();
  
  // return true if the Catom is powered
  bool amPowered();
  
  // update battery life values for this Catom
  void setBatteryLife( double b );

  // change the color of the catom when it is visually rendered
  void setColor( uint8 r, uint8 g, uint8 b, uint8 a );

  // return true if this Catom is touching the floor
  bool amOnFloor();
  
  // updates the internal neighbors[NUM_NEIGHBORS] list
  void getNeighbors();
  
  // am I on some kind of surface (could be internal).  This is true if there
  // is at least k kissing points free of neighboors
  bool amOnSurface();
  
  // am I mobile. true if there are enough free kissing points to move at
  // least one magnet point
  bool amMobile();
  
  // calculate the center of the hypothetical catom attached to Feature f
  Point3D getNeighborCenter(featureID f); 
  
  // return the CatomID of the Catom attached to Feature f 
  // if no neighbor is present, return 0
  catomID getNeighbor(featureID f);

  // clear list and add/remove particular catom from list
  // should not normally be done, only when moving (e.g., magic, physics)
  void clearNeighbors();
  void removeNeighbor( catomID targetCatom );
  int addNeighbor( featureID f, catomID targetCatom, featureID t_f );
  catomID getNthNeighbor( int i ) { return neighborID[i]; }
  featureID getNthFeature( int i ) { return neighbors[i]; }
  featureID getNthFeatureWithANeighbor( int i ) { return neighbors[i]; }
  featureID getNorthPoleFeature() { return getNearestFeature(getLocation() + RelPoint3D(0,0,1));}
  
  // attempt to move around 'target' to 'dest'.
  // Return true if succeeded made any movement.
  bool moveTo(catomID targetCatom, featureID targetFeature, bool roll=false,
		bool lock=true, bool unlock=true );

  // absolute position variants
  bool moveTo( Point3D dest, dQuaternion orient=0, 
		bool lock=true, bool unlock=true );
  bool moveTo( double dest_x, double dest_y, double dest_z, 
		dQuaternion orient=0, bool lock=true, bool unlock=true );

  // used for the speech bubble display
  void selected();
  bool speak(int speakSize, char *speakBuf);
  bool say(int saySize, char *sayBuf);
  bool shout(int shoutSize, char *shoutBuf);
   
  // Constructors
  Catom(catomID i);
  
  // Deconstructor
  ~Catom();
};

#endif  /* __CATOM_HXX */
