#ifndef __CLUSTERMANAGER_H__
#define __CLUSTERMANAGER_H__

#include <iostream>
#include <list>
#include "tcpserver.h"
#include "workunit.h"
#include "pthread.h"

#define CM_NEWWORK 1
#define CM_END 2
#define CM_FINISH 3
#define CM_EXIT 4
#define CM_REQUEST_NEWJOB 5
#define CM_DATA 6
#define CM_DELDATA 7
#define CM_NEWDATA 8
#define CM_QUEUE_FINISHED 9
#define CM_JOBRESULT 10

void *pthread_listen( void *arg );
void *pthread_processworker( void *arg );

/// a class to manager a cluster
/**
 * Work load distibution system
 *
 * this works through tcp
 */
class clustermanager
{
 public:
  clustermanager();
  ~clustermanager();

  /// initialize the data structures
  /**
   * @param add address to listen to
   * @param port port to listen to
   * @param cmdb database wrapper to use to get data
   */
  int init(std::string add, int port);

  /// start the service
  /**
   * this will really start to run the server
   *
   * @warning must be called before addwork function
   */
  int start();

  /// sets the image data and sends to current list of workers
  int setdata( std::vector< image2d* > &sa,
               std::vector< image2d* > &sb,
               image2d *ma, image2d *mb );

  /// add a workunit to the queue
  /**
   * @param wu pointer to work
   * 
   * caller responsible for deletion of the workunit 
   * after it is retrieved from getfinished function
   */
  int addwork( workunit *wu );

  /// returns a finished work unit
  /**
   * blocking call will give back a finished work unit
   * 
   * returns null when no work in queue and workerlists
   */
  int getfinished( std::vector< workunit* > &lst);

  /// finishes the job list
  /**
   * returns when all work units in joblist have been completed
   */
  int runjoblist();

  /// internal function do not call
  int workerlisten();

  /// internal thread function do not call
  int processworker( void *arg );

  /// stop listening and join all threads
  /**
   * call after getfinished returns NULL
   */
  int end();

 private:
  struct worker
  {
    worker()
    {
      cn = NULL;
      id = 0;
      wu = NULL;
      status = 0;
    }
    tcpconnection *cn; ///< the socket connection 
    pthread_t id;      ///< an id
    workunit *wu;      ///< the current workunit

    /*
     * 0 -> no job/job finish
     * 1 -> exit
     * 2 -> finish work queue
     * 3 -> send data
     */
    int curjob;        ///< the current job assigned to this worker

    /*
     * 0 -> exited
     * 1 -> idle/running
     */
    int status;

    pthread_mutex_t joblock;  ///< associated to curjob
    pthread_cond_t jobcond;

  };

  /// sends exit signal to worker
  int sendexit( worker *w );

  /// check exit signal from worker
  int workerrequest( worker *w );

  /// sends image data to worker w
  int senddata( worker *w );

  /// sends the work queue to woker w
  int sendqueue( worker *w );

  /// waits for all workers to finish
  int waitworkers();

  tcpserver serv; ///< server

  int finish;     ///< flag to mark when it will finish
  pthread_t tid;

  pthread_mutex_t workerlist_lock;
  pthread_cond_t workerlist_cond;
  std::list< worker* > workerlist;

  pthread_mutex_t queuelock;
  std::vector< workunit* > queue;

  pthread_mutex_t finishedlock;
  std::vector< workunit* > finished;

  pthread_mutex_t wcountlock;
  int activeworkers;

  pthread_mutex_t workdatalock; ///< lock must be held when updating work data or when work data is being sent to workers
  std::vector< image2d* > imsa;
  std::vector< image2d* > imsb;

  image2d *mapa;
  image2d *mapb;
};

struct processworker_data
{
  clustermanager *arg1;
  void *arg2;
};

#endif
