/*
#  $Id$
# -----------------------------------------------------------------------------
#  The part of 'VideoNEXT MediaClient SDK'
# -----------------------------------------------------------------------------
#  Author: Petrov Maxim, 03/04/2009
#  Edited by:
#  QA by:
#  Copyright: videoNEXT LLC
# -----------------------------------------------------------------------------
*/

#include "ace/OS_NS_time.h"
#include <unistd.h>
#include <string.h>

#ifndef WIN32
#include <sys/uio.h>
#include <netinet/tcp.h>
#endif

#include <libxml/parser.h>
#include <libxml/uri.h>
#include <libxml/xmlmemory.h>
#include <libxml/valid.h>

#include <list>

#include "MediaClientImpl.h"
#include "rfc3174.h"
#include "Mutex.h"

#if defined(__WIN32__) || defined(_WIN32)                                                                                                                   
extern "C" int initializeWinsockIfNecessary();                                                                                                              
#endif 

#include "vn_client.h"

namespace videonext { namespace media {

Role makeRole(const std::string& id, const std::string &name, const std::string &descr)
{
   Role role;
   role.id    = id;
   role.name  = name;
   role.descr = descr;

   return role;
}

MediaObject makeMediaObject(const std::string& id, const DynaParams &params)
{
   MediaObject obj;
   obj.id     = id;
   obj.params = params;

   return obj;
}

MediaObjectsSet makeMediaObjectsSet(const std::string& id, const std::string &name, const std::set<std::string> &objects)
{
   MediaObjectsSet mediaObjectsSet;
   mediaObjectsSet.id    = id;
   mediaObjectsSet.name  = name;
   mediaObjectsSet.objects = objects;

   return mediaObjectsSet;
}

Event makeEvent(unsigned id, const DynaParams &params)
{
   Event event;
   event.id     = id;
   event.params = params;

   return event;
}


void parseXML(void *ctx, const xmlChar *name, const xmlChar **_atts)
{
   MediaClientImpl *client = (MediaClientImpl*)ctx;
   std::map<std::string, std::string> &parseMap = client->xmlParseMap;

   if (xmlStrEqual(name, (const xmlChar*)"STATUS"))
   {
      for(const xmlChar **atts = _atts; atts != NULL && *atts != NULL; atts++)
      {
         if (xmlStrEqual(*atts, (const xmlChar*)"VALUE"))
         {
            parseMap.insert(std::make_pair("STATUS", (char*)*(atts + 1)));
         }
         else if (xmlStrEqual(*atts, (const xmlChar*)"MESSAGE"))
         {
            parseMap.insert(std::make_pair("MESSAGE",(char*)*(atts + 1)));
         }
      }
   }
   else if (xmlStrEqual(name, (const xmlChar*)"RESULT"))
   {
      for(const xmlChar **atts = _atts; atts != NULL && *atts != NULL; atts++)
      {
         if (xmlStrEqual(*atts, (const xmlChar*)"STATUS"))
         {
            parseMap.insert(std::make_pair("STATUS", (char*)*(atts + 1)));
         }
         else if (xmlStrEqual(*atts, (const xmlChar*)"MESSAGE"))
         {
            parseMap.insert(std::make_pair("MESSAGE",(char*)*(atts + 1)));
         }
      }
   }
   else if (xmlStrEqual(name, (const xmlChar*)"PARAM"))
   {
      //<PARAM NAME="ENCRYPTIONKEY" VALUE="3a9e851e"/>

      for(const xmlChar **atts = _atts; atts != NULL && *atts != NULL; atts++)
      {
         if (xmlStrEqual(*atts, (const xmlChar*)"ENCRYPTIONKEY"))
         {
            parseMap.insert(std::make_pair("ENCRYPTIONKEY", (char*)*(atts + 2)));
         }
      }
   }
   else if (xmlStrEqual(name, (const xmlChar*)"AUTHORIZATION"))
   {
      //<AUTHORIZATION ID="FC153E9E100FE411DE892A7000E09131B4DC" URL="rtsp%3A%2F%2F172.16.35.1%3A8554%2Fh264transmedia%3Fdev%3D1%26objid%3D102">

      for(const xmlChar **atts = _atts; atts != NULL && *atts != NULL; atts++)
      {
         if (xmlStrEqual(*atts, (const xmlChar*)"URL"))
         {
            parseMap.insert(std::make_pair("URL", (char*)*(atts + 1)));
         }
         if (xmlStrEqual(*atts, (const xmlChar*)"ID"))
         {
            parseMap.insert(std::make_pair("ID", (char*)*(atts + 1)));
         }
      }
   }
   else if (xmlStrEqual(name, (const xmlChar*)"USER"))
   {
      //<USER ID="21" NAME="admin" ....>
      
      for(const xmlChar **atts = _atts; atts != NULL && *atts != NULL; atts++)
      {
         if (xmlStrEqual(*atts, (const xmlChar*)"NAME")) 
         {
            if (xmlStrEqual(*(atts + 1), (const xmlChar*)client->username.c_str())) 
            {
               client->userID = (char*)*(_atts + 1);
               break;
            }
         }                    
      }
   }
   else if (xmlStrEqual(name, (const xmlChar*)"ROLE"))
   {
      //<ROLE ID="11" NAME="Admin" DESCR="Administrative Role" ...>
      // unescaping role name and role descr
    
      char unescaped_name[128];
      char unescaped_descr[128];

      xmlURIUnescapeString((char*)*(_atts + 3), 
                           sizeof(unescaped_name)-1,
			   unescaped_name);
      xmlURIUnescapeString((char*)*(_atts + 5),
                           sizeof(unescaped_descr)-1, 
			   unescaped_descr);

      parseMap.erase("ROLE_ID");
      parseMap.erase("ROLE_NAME");
      parseMap.erase("ROLE_DESCR");

      parseMap.insert(std::make_pair("ROLE_ID", (char*)*(_atts + 1)));
      parseMap.insert(std::make_pair("ROLE_NAME", unescaped_name));
      parseMap.insert(std::make_pair("ROLE_DESCR", unescaped_descr));
   }
   else if (xmlStrEqual(name, (const xmlChar*)"USER_REF"))
   {
      //<USER_REF ID="21" ...>
      if (client->userID == ((char*)*(_atts + 1))) {
        std::string& role_id = parseMap["ROLE_ID"];
        client->roles.insert(std::make_pair(role_id, makeRole(role_id, parseMap["ROLE_NAME"], parseMap["ROLE_DESCR"])));
      }
   }
   // parse role sets
   else if (xmlStrEqual(name, (const xmlChar*)"SET"))
   {
      //<SET ID="21" NAME="name" ...>
      parseMap.erase("SET_ID");
      parseMap.erase("SET_NAME");

      char unescaped_name[128];

      xmlURIUnescapeString((char*)*(_atts + 3), 
                           sizeof(unescaped_name)-1,
			   unescaped_name);

      parseMap.insert(std::make_pair("SET_ID", (char*)*(_atts + 1)));
      parseMap.insert(std::make_pair("SET_NAME", unescaped_name));

   }
   else if (xmlStrEqual(name, (const xmlChar*)"ROLE_REF"))
   {
      //<ROLE_REF ID="21" CRED="ACDLMPSaemprv"...>
      if (client->roleID == (char*)*(_atts + 1) && strstr((char*)*(_atts + 3), "L"))
      {
         std::string& set_id = parseMap["SET_ID"];
         client->objectsSets.insert(std::make_pair(set_id, MediaObjectsSet()));
         client->objectsSets[set_id].name = parseMap["SET_NAME"];
      }
   }
   else if (xmlStrEqual(name, (const xmlChar*)"RES_REF"))
   {
      //<RES_REF ID="21" CRED="" TYPE="C" ...>
      std::string& set_id = parseMap["SET_ID"];
      if (client->objectsSets.find(set_id) != client->objectsSets.end()
          && (xmlStrEqual(*(_atts + 5), (const xmlChar*)"C") || xmlStrEqual(*(_atts + 5), (const xmlChar*)"A")))
         client->objectsSets[set_id].objects.insert((char*)*(_atts + 1));
   }
   else if (xmlStrEqual(name, (const xmlChar*)"INFO"))
   {
      //<INFO ... VER="2.6.1" LEVEL=.../>

      for(const xmlChar **atts = _atts; atts != NULL && *atts != NULL; atts+=2)
      {
         parseMap.insert(std::make_pair((char*)*atts, (char*)*(atts + 1)));
      }
   }
   // parse nodes
   else if (xmlStrEqual(name, (const xmlChar*)"NODE"))
   {
      //<NODE UNI="j9paKR2cWUxBOKykNzqfaw" HOST="s_master" IP="192.168.0.9" VERID="va-green-2.6.2-A4_7.el5-p0" RTSP_PORT="8554" LAN_IP="192.168.0.9" WAN_IP="192.168.0.9"/>
      
      std::string node_wan_ip = (char*)*(_atts + 13);
      if (node_wan_ip == "127.0.0.1")
         node_wan_ip = client->masterAddress;

      client->nodes.insert(std::make_pair((char*)*(_atts + 1), node_wan_ip ));
   }   
   // parse translation for objects
   else if (xmlStrEqual(name, (const xmlChar*)"RESOURCE"))
   {
      // <RESOURCE  NAME="139" OBJ="139" OTYPE="D" SUBTYPE="C" />
       parseMap.insert(std::make_pair((char*)*(_atts + 3),(char*)*(_atts + 1) ));
   }   

}


void MediaClientImpl::processXMLRequest(ResultStatus *result, const std::string &req, bool clearParams)
{
   result->errorCode = 0;
   std::string outputData;

   performHTTPGET(result,
                  masterAddress, 
                  masterPort,
                  req, 
                  0,
                  &sessionID, 
                  &outputData);

   if (result->errorCode != 0) return;

   //fprintf(stderr, "%s", outputData.c_str());

   xmlSAXHandler saxHandler;
   memset(&saxHandler, 0, sizeof(saxHandler));
   saxHandler.startElement = parseXML;

   if (clearParams) xmlParseMap.clear();

   int ret =
      xmlSAXUserParseMemory(&saxHandler, this, outputData.c_str(), outputData.length());

   if (ret != 0)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML parse failed. Error: %d", ret);
      return;
   }

   if (xmlParseMap["STATUS"] != "OK")
   {
       SET_RESULT_STATUS(result, EINTERNALERROR, "%s", xmlParseMap["MESSAGE"].c_str());
   }
}
      
/*static*/MediaClient *MediaClient::instance()
{
   static MediaClient *singleton = 0; 
   static Mutex singleton_mutex;

   // Perform the Double-Check pattern...
   if (singleton == 0)
   {
      MutexGuard guard(singleton_mutex);

      if (singleton == 0)
         singleton = new MediaClient;
   }

   return singleton;
   
}


////////// MediaClient implementation

MediaClient::MediaClient()
   : pimpl_(new MediaClientImpl)
{     
}

MediaClient::~MediaClient()
{
   delete pimpl_;
}
   
void MediaClient::login(ResultStatus* result,
                        const std::string &host,
                        unsigned port,
                        const std::string &username, 
                        const std::string &password,
                        bool ssl, unsigned timeout)
{
    vn_client_config_t cfg = {};
    strncpy(cfg.remote_host, host.c_str(), sizeof(cfg.remote_host)-1);
    cfg.remote_port = port;
    cfg.use_ssl = ssl;
    cfg.timeout = timeout;

    pimpl_->reset();
    pimpl_->masterAddress = host;
    pimpl_->masterPort    = port;
    pimpl_->username = username;
    pimpl_->timeout = timeout*1000 + 1000;

    pimpl_->ctx_ = vn_client_create(&cfg);
    
    if(username.empty()) // special case for DS to work
        return;

    vn_asynch_result_t *ar = vn_client_login(pimpl_->ctx_, username.c_str(), password.c_str());
    //vn_asynch_result_set_callback(ar, process_login, NULL);

    // get result of call
    vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

    if(!res) {
        result->errorCode = -1;
        result->errorString = "Timeout occured";
    } else {
        result->errorCode = res->error_code;
        result->errorString = res->error_string;

        if(res->error_code) {
            vn_client_destroy(pimpl_->ctx_);
            pimpl_->ctx_ = NULL;
        } else if(res->kvl) {
            pimpl_->roleID = (char*)(*res->kvl)->value;
            printf("Default role=%s\n", pimpl_->roleID.c_str());
        }
    }

    vn_asynch_result_destroy(ar);
}

void MediaClient::setAuth(const std::string& php_sess_id, const std::string& csrf_token)
{
    vn_client_set_auth(php_sess_id.c_str(), csrf_token.c_str());
}

void MediaClient::getMediaURL(ResultStatus *result,
                              std::string  *url,
                              const std::string& objectId,
                              bool isLocal,
                              const std::string& audioObjectId,
                              time_t startTime,
                              time_t endTime,
                              const std::string &transcode,
                              const std::string &dimensions)
{
   if (!pimpl_->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }

   char starttime[sizeof("yyyymmddhhmmss")+1] = {0};
   char endtime[sizeof("yyyymmddhhmmss")+1]   = {0};

   if (startTime && endTime)
   {
#if defined(__MINGW32CE__) || defined(WIN32)
      struct tm *ptm;
      ptm = gmtime(&startTime);
      strftime(starttime, sizeof starttime, "%Y%m%d%H%M%S", ptm);
      ptm = gmtime(&endTime);
      strftime(endtime, sizeof endtime, "%Y%m%d%H%M%S", ptm);
#else
      struct tm ptm;
      gmtime_r(&startTime, &ptm);
      strftime(starttime, sizeof starttime, "%Y%m%d%H%M%S", &ptm);
      gmtime_r(&endTime, &ptm);
      strftime(endtime, sizeof endtime, "%Y%m%d%H%M%S", &ptm);
#endif
   }

   vn_asynch_result_t *ar = vn_client_get_media_url(
               pimpl_->ctx_, objectId.c_str(), audioObjectId.empty() ? NULL : audioObjectId.c_str(),
               startTime, endTime, transcode.empty() ? NULL : transcode.c_str(), dimensions.empty() ? NULL : dimensions.c_str(), false, isLocal);
   //vn_asynch_result_set_callback(ar, process_login, NULL);

   // get result of call
   vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

   if(!res) {
       result->errorCode = -1;
       result->errorString = "Timeout occured";
   } else {
       result->errorCode = res->error_code;
       result->errorString = res->error_string;

       if(res->kvl) {
           *url = (char*)res->kvl[0]->value;
           fprintf(stderr, "*** [%s] media url: %s\n", objectId.c_str(), url->c_str());
       }
   }

   vn_asynch_result_destroy(ar);
   return;



   std::string req = 
      std::string("/api/authmng.php?return=mediastreamauth&streamtype=")
      + (starttime[0] ? "archive" : "live") 
      + std::string("&objid=") + objectId
      + (!audioObjectId.empty() ? (std::string("&audioobjid=") + audioObjectId) : "")
      + (starttime[0] ? ("&time_start="+std::string(starttime)) : "")
      + (endtime[0] ?("&time_end="+std::string(endtime)) : "")
      + (!transcode.empty() ? "&transcode=" + transcode : "")
      + (!dimensions.empty() ? "&dimensions=" + dimensions : "");

   pimpl_->processXMLRequest(result, req);
   if (result->errorCode != 0) return;


   if (!pimpl_->xmlParseMap["URL"].empty())
   {
      char *unescapedURL = new char[pimpl_->xmlParseMap["URL"].length() + 1];
      if (xmlURIUnescapeString(pimpl_->xmlParseMap["URL"].c_str(), 
			       pimpl_->xmlParseMap["URL"].length(), 
			       unescapedURL))
      {
         *url = std::string(unescapedURL) + "&authorizationid=" + pimpl_->xmlParseMap["ID"];
      }

      delete[] unescapedURL;
   }
}


void MediaClient::getUserRoles(ResultStatus *result,
                               std::map<std::string/*role id*/, Role> *roles)
{
   if (!pimpl_->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }
   
   pimpl_->roles.clear();


   vn_asynch_result_t *ar = vn_client_get_roles(pimpl_->ctx_);
   //vn_asynch_result_set_callback(ar, process_server_info, NULL);

   // get result of call
   vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

   if(!res) {
       result->errorCode = -1;
       result->errorString = "Timeout occured";
   } else {
       result->errorCode = res->error_code;
       result->errorString = res->error_string;
       if(res->kvl) {
           for (vn_kv_t **p = res->kvl; *p != 0; ++p) {
               vn_kv_t *pos = *p;
               Role role = makeRole((char*)pos->key, (char*)pos->value, "");
               (*roles)[role.id] = role;
               (pimpl_->roles)[role.id] = role;
               fprintf(stderr, "\t\"%s\" ==> \"%s\"\n", role.id.c_str(), (char*)pos->value);
           }
       }

       if(!res->error_code) {
           // getting server info
           //getServerInfo(result, &pimpl_->serverInfo);
       }
   }

   vn_asynch_result_destroy(ar);

/*
   pimpl_->processXMLRequest(result, "/api/cgi-bin/crdmng.cgi?return=roleinfo");
   if (result->errorCode != 0) return;
   
   if (pimpl_->roles.empty())
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "There are no defined Roles for '%s'", pimpl_->username.c_str());
      return;
   }
   */
   //std::map<unsigned/*role id*/, Role>::iterator iter     =  pimpl_->roles.begin();
   //std::map<unsigned/*role id*/, Role>::iterator iter_end =  pimpl_->roles.end();
   /*
   for (; iter != iter_end; ++iter)
   {
      roles->insert(std::make_pair(iter->first, makeRole(iter->first, iter->second.name, iter->second.descr)));
   }

   //pimpl_->roleID = pimpl_->roles.begin()->id;
   */
}
         
void MediaClient::setUserRole(ResultStatus *result, const std::string& roleID)
{
   if (!pimpl_->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }
  
   result->errorCode = 0;
   
   if (pimpl_->roles.find(roleID) == pimpl_->roles.end())
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "No such role");
      return;
   }

   pimpl_->roleID = roleID;
}

std::string MediaClient::getUserRole()
{
   return pimpl_->roleID;
}

void MediaClient::getMediaObjectSets(ResultStatus *result, std::map<std::string, MediaObjectsSet> *sets)
{
   if (!pimpl_->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }

   if (pimpl_->roleID.empty())
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "No role defined");
      return;
   }

   pimpl_->objectsSets.clear();


   vn_asynch_result_t *ar = vn_client_get_sets(pimpl_->ctx_, pimpl_->roleID.c_str());
   //vn_asynch_result_set_callback(ar, process_server_info, NULL);

   // get result of call
   vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

   if(!res) {
       result->errorCode = -1;
       result->errorString = "Timeout occured";
   } else {
       result->errorCode = res->error_code;
       result->errorString = res->error_string;
       if(res->kvl) {
           for (vn_kv_t **p = res->kvl; *p != 0; ++p) {
               vn_kv_t *pos = *p;
               std::set<std::string> objs;
               MediaObjectsSet set = makeMediaObjectsSet((char*)pos->key, (char*)pos->value, objs);
               (*sets)[set.id] = set;
               (pimpl_->objectsSets)[set.id] = set;
               fprintf(stderr, "\t\"%s\" ==> \"%s\"\n", set.id.c_str(), set.name.c_str());
           }
       }

       if(!res->error_code) {
           // getting server info
           //getServerInfo(result, &pimpl_->serverInfo);
       }
   }

   vn_asynch_result_destroy(ar);

/*
   pimpl_->processXMLRequest(result, "/api/cgi-bin/crdmng.cgi?return=setinfo");
   if (result->errorCode != 0) return;
   
   if (pimpl_->objectsSets.empty())
   {
      std::map<unsigned, Role>::iterator iter = pimpl_->roles.find(pimpl_->roleID);
      SET_RESULT_STATUS(result, EINTERNALERROR, "There are no defined Device Sets for '%s'", iter!=pimpl_->roles.end()?iter->second.name.c_str():pimpl_->username.c_str());
      return;
   }
*/
   //std::map<unsigned/*set id*/, MediaObjectsSet>::iterator iter     =  pimpl_->objectsSets.begin();
   //std::map<unsigned/*set id*/, MediaObjectsSet>::iterator iter_end =  pimpl_->objectsSets.end();
/*
   for (; iter != iter_end; ++iter)
   {
      if (!iter->second.objects.empty())
         sets->insert(std::make_pair(iter->first, makeMediaObjectsSet(iter->first, iter->second.name, iter->second.objects)));
   }
*/
}
   

void MediaClient::getMediaObjects(ResultStatus *result,
                                  std::map<std::string/*role id*/, Role> *roles,
                                  std::map<std::string, MediaObjectsSet> *sets,
                                  std::map<std::string/*object id*/, MediaObject> *mediaObjects)
{
   if (!pimpl_->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }

   if (pimpl_->roleID.empty())
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "No role defined");
      return;
   }

   pimpl_->nodes.clear();
   pimpl_->roles.clear();
   pimpl_->objectsSets.clear();

   vn_asynch_result_t* ar = vn_client_get_resource_tree(pimpl_->ctx_, pimpl_->roleID.c_str());
   //vn_asynch_result_set_callback(ar, process_resource_tree, NULL);

   // get result of call
   vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

   if(!res) {
       result->errorCode = -1;
       result->errorString = "Timeout occured";
   } else {
       result->errorCode = res->error_code;
       result->errorString = res->error_string;
       if(res->kvl) {
           for (vn_kv_t **p = res->kvl; *p != 0; ++p) {
               vn_kv_t *pos = *p;
               // iterate over role attributes
               char *r_name=NULL, *r_descr=NULL;
               for (vn_kv_t **r_a = (vn_kv_t **)pos->value; *r_a != 0; ++r_a) {
                   vn_kv_t *r_attr = *r_a;
                   if(!strcmp((char*)r_attr->key, "name")) {
                       r_name = (char*)r_attr->value;
                       fprintf(stderr, "r_name=%s\n", r_name);
                   }
                   else if(!strcmp((char*)r_attr->key, "description"))
                       r_descr = (char*)r_attr->value;
                   else if(!strcmp((char*)r_attr->key, "sets")) {
                       // iterate over sets
                       fprintf(stderr, "iterate over sets...\n");
                       int m=0;
                       for (vn_kv_t **set = (vn_kv_t **)r_attr->value; *set != 0; ++set) {
                           vn_kv_t *s = *set;
                           char* s_name=NULL;
                           std::set<std::string> objs;
                           char* s_id = (char*)s->key;
                           for (vn_kv_t **s_attr = (vn_kv_t **)s->value; *s_attr != 0; ++s_attr) {
                               vn_kv_t *s_a = *s_attr;
                               if(!strcmp((char*)s_a->key, "name"))
                                   s_name = (char*)s_a->value;
                               else if(!strcmp((char*)s_a->key, "objects")) {
                                   // iterate over object lists for sets
                                   for (vn_kv_t **o = (vn_kv_t **)s_a->value; *o != 0; ++o) {
                                       objs.insert((char*)(*o)->value);
                                       fprintf(stderr, "[%s] ", (char*)(*o)->value);
                                   }
                                   fprintf(stderr, "\n");
                               }
                           }
                           if(s_id && s_name) {
                               fprintf(stderr, "[%d] role=%s, set_name=%s, set_id=%s, objs.size=%zu\n", m++, r_name, s_name, s_id, objs.size());
                               MediaObjectsSet set = makeMediaObjectsSet(s_id, s_name, objs);
                               (*sets)[set.id] = set;
                               (pimpl_->objectsSets)[set.id] = set;
                               fprintf(stderr, "\t\"%s\" ==> \"%s\"\n", set.id.c_str(), set.name.c_str());
                           }
                       }
                   } else if(!strcmp((char*)r_attr->key, "objects")) {
                       // iterate over objects
                       for (vn_kv_t **obj = (vn_kv_t **)r_attr->value; *obj != 0; ++obj) {
                           vn_kv_t *o = *obj;

                           DynaParams attrs;
                           // iterate over attributes
                           for (vn_kv_t **a = (vn_kv_t **)o->value; *a != 0; ++a) {
                               vn_kv_t *attr = *a;
                               attrs[(char*)attr->key] = (char*)attr->value;
                           }

                           /*if(attrs.find("otype") != attrs.end() && attrs["otype"] == "D"
                           && attrs.find("subtype") != attrs.end() && attrs["subtype"] == "C") {
                       fprintf(stderr, "Cam obj=%d\n", *((unsigned*)pos->key));
                       objects.insert(*((unsigned*)pos->key));*/


                           mediaObjects->insert(std::make_pair((char*)o->key,
                                                               makeMediaObject((char*)o->key, attrs)));
                           fprintf(stderr, "%s, ", (char*)o->key);
                       }
                       fprintf(stderr, "\n");
                   }
               }

               Role role = makeRole((char*)pos->key, r_name, r_descr?r_descr:"");
               (*roles)[role.id] = role;
               (pimpl_->roles)[role.id] = role;
               fprintf(stderr, "\t\"%s\" ==> \"%s\"\n", role.id.c_str(), r_name);
           }
       }
   }

   vn_asynch_result_destroy(ar);
   return;



   vn_asynch_result_t** ar_arr = new vn_asynch_result_t* [pimpl_->objectsSets.size()];
   memset(ar_arr, 0, sizeof(vn_asynch_result_t*)*pimpl_->objectsSets.size());
   time_t now = time(0);
   bool bEnd = false;
   unsigned timeout = pimpl_->timeout + 2;

   std::map<std::string/*set id*/, MediaObjectsSet>::iterator it = pimpl_->objectsSets.begin();
   std::map<std::string/*set id*/, MediaObjectsSet>::iterator it_end = pimpl_->objectsSets.end();

   do {
       for (size_t i=0; it != it_end; ++it, ++i) {
           unsigned diff = (time(0) - now)*1000;
           if(timeout < diff) {
               result->errorCode = -1;
               result->errorString = "#1 Timeout occured";
               bEnd = true;
               break;
           }
           ar_arr[i] = vn_client_get_objects(pimpl_->ctx_, it->first.c_str(), NULL);
       }

       if(bEnd)
           break;
       
       it = pimpl_->objectsSets.begin();
       for (size_t i=0; it != it_end; ++it, ++i) {
           unsigned diff = (time(0) - now)*1000;
           if(timeout < diff) {
               result->errorCode = -1;
               result->errorString = "#2 Timeout occured";
               break;
           }
           // get result of call
           vn_result_t* res = vn_asynch_result_get(ar_arr[i], timeout - diff);

           if(!res) {
               result->errorCode = -1;
               result->errorString = "Timeout occured";
               break;
           } else {
               result->errorCode = res->error_code;
               result->errorString = res->error_string;
               if(res->error_code)
                   break;
               if(res->kvl) {
                   std::set<std::string> objects;
                   bool bCameras = (it->second.name == "All Cameras");
                   for (vn_kv_t **p = res->kvl; *p != 0; ++p) {
                       vn_kv_t *pos = *p;
                       if(bCameras) {
                           DynaParams attrs;
                           // iterate over attributes
                           for (vn_kv_t **a = (vn_kv_t **)pos->value; *a != 0; ++a) {
                               vn_kv_t *attr = *a;
                               attrs[(char*)attr->key] = (char*)attr->value;
                           }
                           mediaObjects->insert(std::make_pair((char*)pos->key,
                                                               makeMediaObject((char*)pos->key, attrs)));
                           fprintf(stderr, "%s,", (char*)pos->key);
                       }

                       /*if(attrs.find("otype") != attrs.end() && attrs["otype"] == "D"
                       && attrs.find("subtype") != attrs.end() && attrs["subtype"] == "C") {
                   fprintf(stderr, "Cam obj=%d\n", *((unsigned*)pos->key));
                   objects.insert(*((unsigned*)pos->key));*/

                       objects.insert((char*)pos->key);
                   }
                   if(bCameras)
                       fprintf(stderr, "\n");
                   it->second.objects = objects;
               }
           }
       }
   } while(0);

   for(size_t i=0; i<pimpl_->objectsSets.size(); i++)
       if(ar_arr[i])
           vn_asynch_result_destroy(ar_arr[i]);
   delete[] ar_arr;
   return;


   pimpl_->processXMLRequest(result, "/api/cgi-bin/hostinfo.cgi?return=domain");
   if (result->errorCode != 0) return;
   if (pimpl_->nodes.empty())
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "No nodes found");
      return;
   }
   
   // Getting object ids for each node
   DynaParams nodeDevices;
   DynaParams::iterator node_iter     = pimpl_->nodes.begin();
   DynaParams::iterator node_iter_end = pimpl_->nodes.end();
   for(; node_iter != node_iter_end; ++node_iter)
   {
      fprintf(stderr, "getting translated objects for %s (%s)\n", node_iter->first.c_str(),  node_iter->second.c_str());
      pimpl_->xmlParseMap.clear();
      pimpl_->processXMLRequest(result, std::string("/api/cgi-bin/crdmng.cgi?return=transl") + "&nodename=" +  node_iter->first);
      if (result->errorCode != 0)
      {
         if (result->errorString.find("Can't process your request") != std::string::npos)
         {
            result->errorCode = 0;
            continue;
         }
      }
      

      DynaParams::iterator dev_iter     = pimpl_->xmlParseMap.begin();
      DynaParams::iterator dev_iter_end = pimpl_->xmlParseMap.end();
      for (; dev_iter != dev_iter_end; ++dev_iter)
      {
         nodeDevices.insert(std::make_pair(dev_iter->first, node_iter->second));
      }
   }


   // Getting objects info
   std::string outputData;
   performHTTPGET(result,
                  pimpl_->masterAddress, 
                  pimpl_->masterPort,
                  std::string("/api/cgi-bin/crdmng.cgi?return=usercredentials") + "&id=" + pimpl_->userID
                  + "&get_obj_attr_list=devicetype,location,devid,archstate,objid,name,positionctl,media_format,cameramodel,audio,associate,audio_devid",
                  0,
                  &pimpl_->sessionID, 
                  &outputData);

   if (result->errorCode != 0) return;


   //fprintf(stderr, "%s\n", outputData.c_str());

   xmlDocPtr doc;
   xmlNodePtr cur;
   doc = xmlParseMemory(outputData.c_str(), outputData.length());
   if (doc == 0) 
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML document was not parsed successfully");
      return;
   }
    
   cur = xmlDocGetRootElement(doc);
    
   if (cur == 0) 
   {
      xmlFreeDoc(doc);
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML parse error: document is empty");
      return;
   }
    
   if (!xmlStrEqual(cur->name, (const xmlChar *) "RESULT")) 
   {
      xmlFreeDoc(doc);
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML parse error: document of the wrong type, root != RESULT");
      return;
   }           


   cur = cur->xmlChildrenNode;
    
   while (cur)
   {
      if (xmlStrEqual(cur->name, (const xmlChar *)"STATUS"))
      {
         xmlAttrPtr prop = cur->properties;
         std::string status;
         std::string msg;
         
         while (prop != 0)
         {
            if (xmlStrEqual(prop->name, (const xmlChar *)"VALUE"))
            {
               status = (char*)(prop->children->content);
            }
            else if (xmlStrEqual(prop->name, (const xmlChar *)"MESSAGE"))
            {
               msg = (char*)(prop->children->content);
            }
            
            prop = prop->next;
         }

         if (status != "OK")
         {
            xmlFreeDoc(doc);
            SET_RESULT_STATUS(result, EINTERNALERROR, "%s", msg.c_str());
            return;
         }
      }
      else if (xmlStrEqual(cur->name, (const xmlChar *)"ROLE"))
      {
         xmlNodePtr child = cur->xmlChildrenNode;
         
         while (child)
         {
            DynaParams params; 
            std::string objid;
            if (xmlStrEqual(child->name, (const xmlChar *)"RES_REF"))
            {
               xmlAttrPtr prop = child->properties;
               while (prop != 0)
               {
                  if (xmlStrEqual(prop->name, (const xmlChar *)"ID"))
                  {
                     objid = (char*)(prop->children->content);
                  }
                  else if (xmlStrEqual(prop->name, (const xmlChar *)"OTYPE")) 
                  {
                     if (!xmlStrEqual(prop->children->content, (const xmlChar *)"D"))
                     {
                        fprintf(stderr, "objid is not device: %s\n", objid.c_str());
                        objid.clear();
                        break;
                     }
                  }
                  else if (xmlStrEqual(prop->name, (const xmlChar *)"SUBTYPE")) 
                  {
                     if (!xmlStrEqual(prop->children->content, (const xmlChar *)"A") && !xmlStrEqual(prop->children->content, (const xmlChar *)"C"))
                     {
                        fprintf(stderr, "objid is not camera/audio device: %s\n", objid.c_str());
                        objid.clear();
                     }
                  }

                  prop = prop->next;
               }
               
               if (objid.empty())
               {
                  child = child->next;
                  continue;
               } 
               
               if (mediaObjects->find(objid) != mediaObjects->end())
               {
//                  fprintf(stderr, "skiping existing object: %s\n", objid.c_str());
                  objid.clear();
                  child = child->next;
                  continue;
               }

               xmlNodePtr child_attr = child->xmlChildrenNode;
               while (child_attr)
               {
                  if (xmlStrEqual(child_attr->name, (const xmlChar *)"ATTR"))
                  {
                     std::string name; 
                     char unescaped_val[128];
                     prop = child_attr->properties;
                     while (prop != 0)
                     {
                        if (xmlStrEqual(prop->name, (const xmlChar *)"NAME"))
                        {
                           name = (char*)(prop->children->content);
                        }
                        else if (xmlStrEqual(prop->name, (const xmlChar *)"VAL"))
                        {
                            xmlURIUnescapeString((char*)(prop->children->content), 
                                                 sizeof(unescaped_val) - 1,
                                                 unescaped_val);
                        }
                        prop = prop->next;
                     }

                     //fprintf(stderr, "got attr: '%s' => '%s'\n", name.c_str(), unescaped_val);

                     params.insert(std::make_pair(name, unescaped_val));
                  }
                  
                  child_attr = child_attr->next;
               }

               params.insert(std::make_pair("NODE_IP", nodeDevices[objid]));
               mediaObjects->insert(std::make_pair(objid, makeMediaObject(objid, params)));
            }
     

            child = child->next;
         }
      }
      
      cur = cur->next;
   }
    
   xmlFreeDoc(doc);  
}

void MediaClient::getServerInfo(ResultStatus *result, DynaParams *info)
{
    vn_asynch_result_t *ar = vn_client_get_server_info(pimpl_->ctx_);
    //vn_asynch_result_set_callback(ar, process_server_info, NULL);

    // get result of call
    vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

    if(!res) {
        result->errorCode = -1;
        result->errorString = "Timeout occured";
    } else {
        result->errorCode = res->error_code;
        result->errorString = res->error_string;
        if(res->kvl) {
            for (vn_kv_t **p = res->kvl; *p != 0; ++p) {
                vn_kv_t *pos = *p;
                (*info)[(char*)pos->key] = (char*)pos->value;
                fprintf(stderr, "\t\"%s\" ==> \"%s\"\n", (char*)pos->key, (char*)pos->value);
            }
        }

        if(!res->error_code) {
            // getting server info
            //getServerInfo(result, &pimpl_->serverInfo);
        }
    }

    vn_asynch_result_destroy(ar);
/*
   pimpl_->processXMLRequest(result, "/api/cgi-bin/vctl.pl?return=info");
   if (result->errorCode != 0) return;


   pimpl_->xmlParseMap.erase("STATUS");

   *info = pimpl_->xmlParseMap;
*/
}

void MediaClient::getEvents(ResultStatus *result, 
                            std::map<unsigned, Event> *events,
                            std::string& role_id,
                            std::string& filter)
{
   result->errorCode = 0;

   if (!pimpl_->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }

   vn_asynch_result_t *ar = vn_client_get_events(pimpl_->ctx_,
                                                 role_id.empty() ? NULL : role_id.c_str(),
                                                 filter.empty() ? NULL : filter.c_str());
   //vn_asynch_result_set_callback(ar, process_server_info, NULL);

   // get result of call
   vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

   if(!res) {
       result->errorCode = -1;
       result->errorString = "Timeout occured";
   } else {
       result->errorCode = res->error_code;
       result->errorString = res->error_string;
       if(res->kvl) {
           for (vn_kv_t **p = res->kvl; *p != 0; ++p) {
               vn_kv_t *pos = *p;

               DynaParams attrs;
               for (vn_kv_t **a = (vn_kv_t **)pos->value; *a != 0; ++a) {
                   vn_kv_t *attr = *a;
                   attrs[(char*)attr->key] = (char*)attr->value;
                   fprintf(stderr, "\t%s ==> %s\n",
                           (char*)attr->key, (char*)attr->value);
               }

               events->insert(std::make_pair(*((unsigned*)pos->key),
                                             makeEvent(*((unsigned*)pos->key), attrs)));
           }
       }

       if(!res->error_code) {
           // getting server info
           //getServerInfo(result, &pimpl_->serverInfo);
       }
   }

   vn_asynch_result_destroy(ar);

   /*char starttime[12] = {0};
   char endtime[12]   = {0};
   char maxcount[12]  = {0};
   
   if (startTime && endTime)
   {
      snprintf(starttime, sizeof(starttime), "%ld",  (unsigned long)startTime);
      snprintf(endtime, sizeof(endtime), "%ld",  (unsigned long)endTime);
   }

   snprintf(maxcount, sizeof maxcount, "%ld", (unsigned long)maxCount);

   std::string req = 
      std::string("/storage/eventlog?")
      + (starttime[0] ? ("&start="+std::string(starttime)) : "")
      + (endtime[0] ?("&end="+std::string(endtime)) : "")
      + (maxcount[0] ?("&max="+std::string(maxcount)) : "");

   std::string outputData;

   performHTTPGET(result,
                  pimpl_->masterAddress,
                  pimpl_->masterPort,
                  req, 
                  0,
                  &pimpl_->sessionID, 
                  &outputData);

   if (result->errorCode != 0) return;

   xmlDocPtr doc;
   xmlNodePtr cur;

   doc = xmlParseMemory(outputData.c_str(), outputData.length());
    
   if (doc == 0) 
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML document was not parsed successfully");
      return;
   }
    
   cur = xmlDocGetRootElement(doc);
    
   if (cur == 0) 
   {
      xmlFreeDoc(doc);
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML parse error: document is empty");
      return;
   }
    
   if (!xmlStrEqual(cur->name, (const xmlChar *) "RESULT")) 
   {
      xmlFreeDoc(doc);
      SET_RESULT_STATUS(result, EINTERNALERROR, "XML parse error: document of the wrong type, root != RESULT");
      return;
   }           


   cur = cur->xmlChildrenNode;
    
   while (cur)
   {
      if (xmlStrEqual(cur->name, (const xmlChar *)"STATUS"))
      {
         xmlAttrPtr prop = cur->properties;
         std::string status;
         std::string msg;
         
         while (prop != 0)
         {
            if (xmlStrEqual(prop->name, (const xmlChar *)"VALUE"))
            {
               status = (char*)(prop->children->content);
            }
            else if (xmlStrEqual(prop->name, (const xmlChar *)"MESSAGE"))
            {
               msg = (char*)(prop->children->content);
            }
            
            prop = prop->next;
         }

         if (status != "OK")
         {
            xmlFreeDoc(doc);
            SET_RESULT_STATUS(result, EINTERNALERROR, "%s", msg.c_str());
            return;
         }
      }

      if (xmlStrEqual(cur->name, (const xmlChar *)"EVENTS"))
      {
         xmlNodePtr child = cur->xmlChildrenNode;

         while (child)
         {
            char unescaped_val[128];

            if (xmlStrEqual(child->name, (const xmlChar *)"EVENT"))
            {
               DynaParams params;
               xmlAttrPtr prop = child->properties;
               while (prop != 0)
               {
                  if ((xmlStrEqual(prop->name, (const xmlChar *)"MESSAGE") 
                       || xmlStrEqual(prop->name, (const xmlChar *)"NOTE"))
                     && prop->children->content != 0)
                  {
                     xmlURIUnescapeString((char*)(prop->children->content), 
                                          sizeof(unescaped_val) - 1,
                                          unescaped_val);

                     params.insert(std::make_pair((char*)prop->name, unescaped_val));
                  }
                  else
                  {
                     params.insert(std::make_pair((char*)prop->name, (char*)prop->children->content));
                  }
                  
                  prop = prop->next;               
               }

               unsigned id = atol(params["ID"].c_str());            
               events->insert(std::make_pair(id, makeEvent(id, params)));
            }

            child = child->next;            
         }
      }

      cur = cur->next;
   }
    
   xmlFreeDoc(doc);*/
}

void MediaClient::getSnapshot(ResultStatus *result, const std::string& obj_id, time_t ts, unsigned stream_num, int metadata, std::string* img)
{
    result->errorCode = 0;

    if (!pimpl_->ctx_)
    {
       SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
       return;
    }

    vn_asynch_result_t *ar = vn_client_get_snapshot(pimpl_->ctx_, obj_id.c_str(), ts, stream_num, metadata);
    //vn_asynch_result_set_callback(ar, process_server_info, NULL);

    // get result of call
    vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

    if(!res) {
        result->errorCode = -1;
        result->errorString = "Timeout occured";
    } else {
        result->errorCode = res->error_code;
        result->errorString = res->error_string;
        if(res->kvl)
            img->assign((char*)res->kvl[0]->value, *((unsigned*)res->kvl[0]->key));

        if(!res->error_code) {
            // getting server info
            //getServerInfo(result, &pimpl_->serverInfo);
        }
    }

    vn_asynch_result_destroy(ar);
}

void MediaClient::performPTZCommand(ResultStatus *result, const std::string& objectId,
                                    const std::string &mode, const std::string &cmd)
{
    result->errorCode = 0;

    if (!pimpl_->ctx_)
    {
       SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
       return;
    }

    vn_asynch_result_t *ar = vn_client_perform_ptz_command(pimpl_->ctx_, objectId.c_str(), mode.c_str(), cmd.c_str());

    // get result of call
    vn_result_t* res = vn_asynch_result_get(ar, pimpl_->timeout);

    if(!res) {
        result->errorCode = -1;
        result->errorString = "Timeout occured";
    } else {
        result->errorCode = res->error_code;
        result->errorString = res->error_string;
    }

    vn_asynch_result_destroy(ar);
}

void performPTZCommand(ResultStatus *result,
                       MediaObject *mediaObject,
                       const std::string &mode,
                       const std::string &command)
{
   std::string req = "/ptz/cgi-bin/send_message.pl?data=%3CPTZ_Command%3Edo.ptz?dev="
      + mediaObject->params["OBJID"] + "%26mode=" + mode + "%26" 
      + command + "%3C/PTZ_Command%3E";

   performHTTPGET(result,
                  MediaClient::instance()->impl()->masterAddress,
                  MediaClient::instance()->impl()->masterPort,
                  req,
                  0,
                  0, 
                  0);
}

void getCredentials(const std::string &password, const std::string &encKey, char *result)
{
   uint8_t digest[SHA1HashSize];
  
   SHA1Context ctx;
   SHA1Reset(&ctx);
   SHA1Input(&ctx, (const uint8_t *)password.data(), password.size());
   SHA1Result(&ctx, digest);

   for (int i=0; i < SHA1HashSize; i++)
   {
      sprintf(&result[i*2], "%02X", digest[i]);
   }
   
   std::string new_password = encKey + result + encKey; 
   SHA1Reset(&ctx);
   SHA1Input(&ctx, (const uint8_t *)new_password.data(), new_password.size());
   SHA1Result(&ctx, digest);

   for (int i=0; i < SHA1HashSize; i++)
   {
      sprintf(&result[i*2], "%02x", digest[i]);
   }
}

////////// PushMediaClient implementation
PushMediaClient::PushMediaClient()
   : socket_(-1)
{
   responseBufferSize_ = 4096*2;
   responseBuffer_ = new char[responseBufferSize_];
   bzero(responseBuffer_, responseBufferSize_);
}

PushMediaClient::~PushMediaClient()
{
   close(socket_);
   delete[] responseBuffer_;
}

void PushMediaClient::registerDevice(
   ResultStatus *result,
   std::string* registeredObjId,
   const std::string &hwid, 
   const std::string &name, 
   const std::string &telnum,
   const std::string &mediaFormat)
{
   MediaClientImpl *mediaClientImpl = MediaClient::instance()->impl();
   int error_code;
   char error_str[256];

   if (!mediaClientImpl->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }

   result->errorCode = 0;

   // Try to register mobile device
   const char* keys[] = {"CAMERAMODEL", "HWID", "NAME", "MEDIA_FORMAT", "FRAMERATE", "ARCFRAMERATE"};
   const char* values[] = {"iStream", hwid.c_str(), name.c_str(), mediaFormat.c_str(), "30", "30"};
   vn_kv_t **kvl = vn_kv_list_create(sizeof(keys)/sizeof(char*));
   for(size_t k = 0; k < sizeof(keys)/sizeof(char*); k++) {
       kvl[k]->key = (void*)keys[k];
       kvl[k]->value = (void*)values[k];
   }

   vn_asynch_result_t *ar = vn_client_add_object(mediaClientImpl->ctx_, (const vn_kv_t **)kvl);

   // get result of call
   vn_result_t* res = vn_asynch_result_get(ar, mediaClientImpl->timeout);

   if(!res) {
       result->errorCode = -1;
       result->errorString = "Timeout occured";
   } else {
       result->errorCode = res->error_code;
       result->errorString = res->error_string;
       if(res->kvl) {
           *registeredObjId = (char*)res->kvl[0]->value;
           printf("Registered objid=%s\n", registeredObjId->c_str());
       }
   }

   vn_asynch_result_destroy(ar);
   return;


   // Connecting to master
   socket_ = connect_socket(result, mediaClientImpl->masterAddress, mediaClientImpl->masterPort);
   
   if (result->errorCode != 0) return;
   

   // preparing register request
   std::string req = "function=addCamera&attributes={\"attr\":{\"MEDIA_FORMAT\":\"" + mediaFormat + "\",\"CAMERAMODEL\":\"iStream\",\"NAME\":\"" 
      + name + "\",\"HWID\":\"" + hwid + "\",\"TELEPHONE_NUMBER\":\"" + telnum + "\",\"FRAMERATE\":\"30\",\"ARCFRAMERATE\":\"30\"}}";


   char *req_escaped = (char*)xmlURIEscapeStr((const xmlChar *)req.c_str(), (const xmlChar*)"=&"); 

   std::string post = std::string("POST /sdi/admincirrus/call.php HTTP/1.0\r\n")
      + std::string("Host: ") + mediaClientImpl->masterAddress + "\r\n"
      + std::string("Cookie: user=; PHPSESSID=") + mediaClientImpl->sessionID + "\r\n"
      + "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n";
   
   char cnt_length[25];
   snprintf(cnt_length, sizeof(cnt_length), "%d", (unsigned)strlen(req_escaped));

   post += std::string("Content-Length: ") + cnt_length + "\r\n";
   post += "\r\n";
   post += req_escaped;
   
   do
   {
      if (send(socket_, post.c_str(), post.length(), 0) <= 0)
      {
         error_code = __get_errno();
         if (strerror_r(error_code, error_str, sizeof(error_str)) != 0)
             fprintf(stderr, "strerror_r failed\n");
         SET_RESULT_STATUS(result, error_code, "Failed to write into socket: %s", error_str);
         break;
      }

      // check main HTTP-state header
      char buffer[1024] = {0};
      if (recv(socket_, buffer, sizeof(buffer) - 1, 0) > 0)
      {
         if (strstr(buffer, "HTTP/1.0 200") != buffer
             && strstr(buffer, "HTTP/1.1 200") != buffer)
         {
            SET_RESULT_STATUS(result, EINTERNALERROR, "Request failed. Server response: %s", buffer);
            break;
         }
      }
      // fprintf(stderr, "%s\n", buffer);
      result->errorString = buffer;

   } while (0);

   close(socket_);
}

void PushMediaClient::initStream(ResultStatus *result, 
                                 const std::string& hwid,
                                 const std::string& objid)
{
   objid_ = objid;
 
   MediaClientImpl *mediaClientImpl = MediaClient::instance()->impl();

   if (!mediaClientImpl->ctx_)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "User is not authenticated");
      return;
   }

   result->errorCode = 0;

   std::string rtspURL;
   MediaClient::instance()->getMediaURL(result, &rtspURL, objid_);
   if (result->errorCode != 0) 
       return;

   initRTSPStream(result, rtspURL);
}   

void PushMediaClient::initRTSPStream(ResultStatus *result, const std::string &rtspURL)
{   
   int error_code;
   char error_str[256];
   
   char proto[16] = {0};
   char authorization[128] = {0};
   char hostname[128] = {0};
   int  port = 0;
   char path[128] = {0};

   url_split(proto, sizeof proto,
             authorization, sizeof authorization,
             hostname, sizeof hostname,
             &port, 
             path, sizeof path, rtspURL.c_str());

   socket_ = connect_socket(result, hostname, port);
   if (result->errorCode != 0) return;
/*
   int flag = 1;
   int ret = setsockopt(socket_,
                        IPPROTO_TCP,
                        TCP_NODELAY,
                        (char *) &flag,                                          
                        sizeof(int));  

   if (ret < 0)
   {
      error_code = __get_errno();
      strerror_r(error_code, error_str, sizeof(error_str));
      SET_RESULT_STATUS(result, error_code, "Failed to write into socket: %s", error_str);
      return;
   }
*/

#ifdef WIN32
   unsigned long mode = 0;  // 1 to enable non-blocking socket
   ioctlsocket(socket_, FIONBIO, &mode);

   struct timeval timeout;      
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
               sizeof(timeout));
   
#endif
   
   std::string req = "XRECORD " + rtspURL + " RTSP/1.0\r\nCSeq: 1\r\n\r\n";
         
   fprintf(stderr, "Request: %s", req.c_str());

   int bytesSent = send(socket_, req.c_str(), req.length(), 0);
   if (bytesSent <= 0)
   {
      error_code = __get_errno();
      if (strerror_r(errno, /*error_code,*/ error_str, sizeof(error_str)) != 0)
          fprintf(stderr, "strerror_r failed\n");
      SET_RESULT_STATUS(result, error_code, "Failed to write into socket (%d): %s", bytesSent, error_str);
      return;
   }

   char resp[256];
   if (recvline(socket_, resp, sizeof(resp)-1) <= 0)
   {
      error_code = __get_errno();
      if (strerror_r(error_code, error_str, sizeof(error_str)) != 0)
          fprintf(stderr, "strerror_r failed\n");
      SET_RESULT_STATUS(result, error_code, "Failed to write into socket: %s", error_str);
      return;
   }

   if (strstr(resp, "RTSP/1.0 200") == 0)
   {
      SET_RESULT_STATUS(result, EINTERNALERROR, "Could not connect to receiver: %s", resp);
      return;
   }

   // reading rest of response
   while (recvline(socket_, resp, sizeof resp) > 0)   
   {
      if (strcmp(resp, "\r\n") == 0) break;
   }
}

void PushMediaClient::postFrame(ResultStatus *result, unsigned char *data, unsigned size, const DynaParams *headers)
{
   int error_code;
   char error_str[256];
   std::string post;
   
   char cnt_length[25];
   snprintf(cnt_length, sizeof(cnt_length), "%d", size);

   post += std::string("Content-Length: ") + cnt_length + "\r\n";

   if (headers)
   {
      DynaParams::const_iterator iter = headers->begin();
      for (; iter != headers->end(); ++iter)
      {
         post +=  iter->first + ": " + iter->second + "\r\n";
      }
   }

   post += "\r\n";

   result->errorCode = 0;
   do
   {
       
#ifndef WIN32
     struct iovec post_data[2];
      post_data[0].iov_base = (char*)post.c_str();
      post_data[0].iov_len  = post.length();

      post_data[1].iov_base = data;
      post_data[1].iov_len  = size;

      if (writev(socket_, post_data, 2) <= 0)
      {
         error_code = __get_errno();
         if (strerror_r(error_code, error_str, sizeof(error_str)) != 0)
             fprintf(stderr, "strerror_r failed\n");
         SET_RESULT_STATUS(result, error_code, "Failed to write into socket: %s", error_str);
         break;
      }
#else
      int bytes_written;
      if ((bytes_written=send(socket_, post.c_str(), post.length(), 0)) <= 0)
      {
         error_code = __get_errno();
         strerror_r(error_code, error_str, sizeof(error_str));
         SET_RESULT_STATUS(result, error_code, "Failed to write into socket return_code: %d, error_code: %d,  %s", bytes_written, error_code, error_str);
         break;
      }

      if ((bytes_written=send(socket_, (const char*)data, size, 0)) <= 0)
      {
         error_code = __get_errno();                  
         strerror_r(error_code, error_str, sizeof(error_str));
         SET_RESULT_STATUS(result, error_code, "Failed to write into socket. return_code: %d, error_code: %d,  %s", bytes_written, error_code, error_str);
         break;
      }
            
#endif

   } while (0);

   if (result->errorCode)
       close(socket_);
}

void url_split(char *proto, int proto_size,
               char *authorization, int authorization_size,
               char *hostname, int hostname_size,
               int  *port_ptr,
               char *path, int path_size,
               const char *url)
{
    const char *p;
    char *q;
    int port;

    port = -1;

    p = url;
    q = proto;
    while (*p != ':' && *p != '\0') {
        if ((q - proto) < proto_size - 1)
            *q++ = *p;
        p++;
    }
    if (proto_size > 0)
        *q = '\0';
    if (authorization_size > 0)
        authorization[0] = '\0';
    if (*p == '\0') {
        if (proto_size > 0)
            proto[0] = '\0';
        if (hostname_size > 0)
            hostname[0] = '\0';
        p = url;
    } else {
        char *at,*slash; // PETR: position of '@' character and '/' character

        p++;
        if (*p == '/')
            p++;
        if (*p == '/')
            p++;
        at = (char*)strchr(p,'@'); // PETR: get the position of '@'
        slash = (char*)strchr(p,'/');  // PETR: get position of '/' - end of hostname
        if (at && slash && at > slash) at = NULL; // PETR: not interested in '@' behind '/'

        q = at ? authorization : hostname;  // PETR: if '@' exists starting with auth.

         while ((at || *p != ':') && *p != '/' && *p != '?' && *p != '\0') { // PETR:
            if (*p == '@') {    // PETR: passed '@'
              if (authorization_size > 0)
                  *q = '\0';
              q = hostname;
              at = NULL;
            } else if (!at) {   // PETR: hostname
              if ((q - hostname) < hostname_size - 1)
                  *q++ = *p;
            } else {
              if ((q - authorization) < authorization_size - 1)
                *q++ = *p;
            }
            p++;
        }
        if (hostname_size > 0)
            *q = '\0';
        if (*p == ':') {
            p++;
            port = strtoul(p, (char **)&p, 10);
        }
    }
    if (port_ptr)
        *port_ptr = port;
    strncpy(path, p, path_size);
}

void tokenize(const std::string& str, const std::string& delimiters, std::vector<std::string> *tokens)
{
    // skip delimiters at beginning.
    std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);

    // find first "non-delimiter".
    std::string::size_type pos = str.find_first_of(delimiters, lastPos);

    while (std::string::npos != pos || std::string::npos != lastPos)
    {
        // found a token, add it to the vector.
        tokens->push_back(str.substr(lastPos, pos - lastPos));

        // skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);

        // find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

int datetimeToTimeval(const char *datetime, struct timeval &tv)
{
    const char *dot = strchr(datetime, '.');
#if 0
    if (!dot)
    {
        fprintf(stderr, "incorrect time\n");
        return -1;
    }
#endif

    struct tm tm;
#ifdef WIN32
    // The synthesized behavior in ACE_OS::strptime_emulation() funtion is not suitable if there are no none-numeric separators in the format string.
    // So let's use simple sscanf()
    bool err(true);
    if(6 == sscanf(datetime, "%04d%02d%02d%02d%02d%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec))
    {
        tm.tm_year -= 1900;
        tm.tm_mon--;
        tv.tv_sec = _mkgmtime(&tm);
        if(tv.tv_sec != -1)
            err = false;
    }

    if(err)
    {
        fprintf(stderr, "Fail to get timestamp from pts \"%s\"", datetime);
        return -1;
    }
#else
    if (ACE_OS::strptime(datetime, "%Y%m%d%H%M%S", &tm) != NULL)
        tv.tv_sec  = timegm(&tm);
#endif

    tv.tv_usec = dot ? atoi(dot+1) : 0;

    return 0;
}

std::string unixtsToDatetime(time_t unixts)
{
    char buf[80] = {0};

#ifdef WIN32
    // gmtime is threadsafe in windows because it uses TLS
    struct tm *ptm = gmtime(&unixts);
    strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", ptm);
#else
    struct tm ptm = {};
    gmtime_r(&unixts, &ptm);
    strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", &ptm);
#endif

    return buf;
}

}}
