/* -*- c++ -*- */
/* 
 * Copyright 2012 Free Software Foundation, Inc.
 *
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifndef ICE_APPLICATION_BASE_H
#define ICE_APPLICATION_BASE_H

#ifdef HAVE_WINDOWS_H
#include <winsock2.h>
#include <sys/time.h>
#endif

#include <gnuradio/api.h>
#include <gnuradio/prefs.h>
#include <Ice/Ice.h>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <stdio.h>
#include <iostream>
#include <set>
#include <string>
#include <stdio.h>

namespace {
  static const unsigned int ICEAPPLICATION_ACTIVATION_TIMEOUT_MS(600);
};

class GR_RUNTIME_API ice_application_common : public Ice::Application 
{
 public:
  template<typename TserverBase, typename TserverClass> friend class ice_application_base;
  static boost::shared_ptr<ice_application_common> Instance();
  ~ice_application_common() {;}
  static int d_reacquire_attributes;
  
 protected:
  static bool d_main_called;
  static bool d_have_ice_config;
  static std::string d_endpointStr;
  static boost::shared_ptr<boost::thread> d_thread;
  ice_application_common() {;}
  int run(int, char*[]);
};

template<typename TserverBase, typename TserverClass>
class ice_application_base
{
public:
  boost::shared_ptr<ice_application_common> d_application;
  ice_application_base(TserverClass* _this);
  ~ice_application_base() {;}
    
  static TserverBase* i();
  static const std::vector<std::string> endpoints();
    
protected:
  bool have_ice_config() { return d_application->d_have_ice_config; }
  void set_endpoint(const std::string& endpoint) { d_application->d_endpointStr = endpoint;}
  
  //this one is the key... overwrite in templated/inherited variants
  virtual TserverBase* i_impl() = 0;

  //tools for the i_impl... 
  //tell it when it has to resync with the communicator
  virtual bool reacquire_sync();
  virtual void sync_reacquire();

  static TserverClass* d_this;
    
  int d_reacquire;
  //static int d_reacquire_attributes;
    
private:
  void starticeexample();
    
  bool application_started();
    
  int run(int, char*[]);
    
  static void kickoff();
};

template<typename TserverBase, typename TserverClass>
TserverClass* ice_application_base<TserverBase, TserverClass>::d_this(0);

//template<typename TserverBase, typename TserverClass>
//int ice_application_base<TserverBase, TserverClass>::d_reacquire_attributes(0);

template<typename TserverBase, typename TserverClass>
ice_application_base<TserverBase, TserverClass>::ice_application_base(TserverClass* _this)
  : d_reacquire(0) 
{
  //d_reacquire_attributes = 0;
  d_this = _this;
  d_application = ice_application_common::Instance();
}

template<typename TserverBase, typename TserverClass>
void ice_application_base<TserverBase, TserverClass>::starticeexample()
{
  char* argv[2];
  argv[0] = (char*)"";

  std::string conffile = gr::prefs::singleton()->get_string("ControlPort", "config", "");

  if(conffile.size() > 0) {
    std::stringstream iceconf;
    ice_application_common::d_have_ice_config = true; 
    ice_application_common::d_main_called = true;
    iceconf << conffile;
    d_application->main(0, argv, iceconf.str().c_str());
  }
  else {
    ice_application_common::d_have_ice_config = false; 
    ice_application_common::d_main_called = true;
    d_application->main(0, argv);
  }
}

template<typename TserverBase, typename TserverClass>
void ice_application_base<TserverBase, TserverClass>::kickoff()
{
  static bool run_once = false;
  
  //if(!d_this->application_started()) {
  if(!run_once) {
    ++d_this->d_application->d_reacquire_attributes;
    
    ice_application_common::d_thread = boost::shared_ptr<boost::thread>
      (new boost::thread(boost::bind(&ice_application_base::starticeexample, d_this)));
  
    ::timespec timer_ts, rem_ts;
    timer_ts.tv_sec = 0; timer_ts.tv_nsec = ICEAPPLICATION_ACTIVATION_TIMEOUT_MS*1000;
  
    int iter = 0;
    while(!d_this->application_started()) {
      #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
	  ::Sleep(timer_ts.tv_nsec / 1000000);
	  #else
      ::nanosleep(&timer_ts, &rem_ts);
	  #endif
      if(!d_this->application_started())
	std::cout << "@";
      if(iter++ > 100) {
	std::cout << "ice_application_base::kickoff(), timeout waiting to get communicator() d_application->main() might have failed?!" << std::endl;;
	break;
      }
    }
    
    run_once = true;
  }
  
  return;
}


template<typename TserverBase, typename TserverClass>
bool ice_application_base<TserverBase, TserverClass>::reacquire_sync()
{
  return (d_this->d_reacquire != d_application->d_reacquire_attributes);
}

template<typename TserverBase, typename TserverClass>
void ice_application_base<TserverBase, TserverClass>::sync_reacquire()
{
  d_this->d_reacquire = d_application->d_reacquire_attributes;
}


template<typename TserverBase, typename TserverClass>
const std::vector<std::string> ice_application_base<TserverBase, TserverClass>::endpoints()
{
  std::vector<std::string> ep; ep.push_back(d_this->d_application->d_endpointStr); return ep;
}

template<typename TserverBase, typename TserverClass>
TserverBase* ice_application_base<TserverBase, TserverClass>::i()
{
  //printf("indacall\n");
  
  assert(d_this != 0);
  if(!d_this->application_started()) {
    //printf("anotherkickoff\n");
    kickoff();
  }
  //printf("donekickedoff\n");
  
  /*else if(!d_proxy) {
    d_proxy = d_this->i_impl();
    assert(d_proxy != 0);
    }*/

  return d_this->i_impl(); 
}

/*template<typename TserverBase, typename TserverClass>
  int ice_application_base<TserverBase, TserverClass>::run(int argc, char* argv[]) {
  int implreturn(run_impl(argc, argv));
  ice_application_base<TserverBase, TserverClass>::communicator()->waitForShutdown();
  return implreturn;
  }*/

template<typename TserverBase, typename TImplClass>
bool ice_application_base<TserverBase, TImplClass>::application_started()
{
  return ice_application_base<TserverBase, TImplClass>::d_this->d_application->communicator();
}

/*template<typename TserverBase, typename TImplClass>
int ice_application_base<TserverBase, TImplClass>::run_impl(int argc, char* argv[]) { return EXIT_SUCCESS; }
*/

#endif