///////////////////////////////////////////////////////////
//  IndiClient.cpp
//  Implementation of the Class IndiClient
//  Created on:      16-May-2017 2:43:00 PM
//  Original author: Marco De Pascale
///////////////////////////////////////////////////////////

#include "IndiClient.h"


IndiClient::IndiClient()
{
  m_port = 7624;
  m_host = "127.0.01";
}


IndiClient::IndiClient(int port)
{
  m_port = port;
  m_host = "127.0.01";
}


IndiClient::IndiClient(std::string host)
{
  m_port = 7624;
  m_host = host;
}


IndiClient::IndiClient(int port, std::string host)
{
  m_port = port;
  m_host = host;
}


IndiClient::~IndiClient()
{
  if (m_socketConn.isValid())
    m_socketConn.close();
}


int IndiClient::connect()
{
  int status = -1;
  status = m_socketConn.create(SystemSocket::enumStream);
  status = m_socketConn.connect(m_port, m_host);
  status = m_socketConn.setNonBlocking(true);
  return status;
}

int IndiClient::close()
{
  return m_socketConn.close();
}

int IndiClient::is_valid()
{
  return m_socketConn.isValid();
}

// Setting Indi message with property, and sending XML string
int IndiClient::send_new_property(const IndiProperty &_property, int *_counter,
                                  IceUtil::Mutex *_mtxCount)
{
  int status = 0;
  if (_property.getState() != IndiProperty::Busy)
    {
      // State must be set to Busy when requesting a change in remote Property
      // if not Busy throw error
      A_LOG("Property state not Busy");
    }
  else
    {
      for (unsigned int ui = 0; ui < _property.getNumElements(); ui++)
        {
          std::cout << _property[ui].getName() << " = "
                    << _property[ui].getValue() << std::endl;
        }
      // std::cout << "send_new_property " << _mtxCount << "  " << _counter << std::endl;
      IndiMessage message = IndiMessage(IndiMessage::NewProperty, _property);
      IndiXmlParser xmlParser(message, INDI_PROTOCOL_VERSION);
      // std::cout << "**" << xmlParser.createXmlString() << "**" << std::endl;
      IceUtil::Mutex::TryLock lock(*_mtxCount);

      while (!lock.acquired())
        {
          A_LOG("send_new_property: Lock not aquired, waiting for mutex");
        }
      // mutex locked
      I_LOG("Sending SET message...");
      status = m_socketConn.send(xmlParser.createXmlString());
      *_counter += 7 + 1;
      D_LOG("message counter = " << *_counter);

      // if (lock.acquired())
      //   {
      //     I_LOG("Sending SET message...");
      //     status = m_socketConn.send(xmlParser.createXmlString());
      //     // double increment because a notification of property change
      //     // arrives when motion is completed
      //     // *_counter += 2;

      //     // the message counter have to be increased by the number of devices set + 1
      //     // because of the sent message
      //     *_counter += 7 + 1;
      //     D_LOG("message counter = " << *_counter);
      //   }
      // else
      //   {
      //     A_LOG("send_new_property: Lock not aquired");
      //   }
      return status;
    }
}


int IndiClient::send_get_property(const IndiProperty& property, int *_counter, IceUtil::Mutex *_mtxCount)
{
  // std::cout << "send_get_property " << _mtxCount << "  " << _counter << std::endl;
  int status = 0;
  IndiMessage message = IndiMessage(IndiMessage::GetProperties, property);
  IndiXmlParser xmlParser(message, INDI_PROTOCOL_VERSION);
  //std::cout << xmlParser.createXmlString() << std::endl;
  IceUtil::Mutex::TryLock lock(*_mtxCount);

  while (!lock.acquired())
    {
      A_LOG("send_get_property: Lock not aquired, waiting for mutex");
    }
  // mutex locked
  I_LOG("Sending GET message...");
  status = m_socketConn.send(xmlParser.createXmlString());
  *_counter += 1;
  D_LOG("message counter = " << *_counter);

  // if (lock.acquired())
  //   {
  //     I_LOG("Sending GET message...");
  //     status = m_socketConn.send(xmlParser.createXmlString());
  //     *_counter += 1;
  //     D_LOG("message counter = " << *_counter);
  //   }
  // else
  //   {
  //     A_LOG("send_get_property: Lock not aquired");
  //   }
  return status;
}


int IndiClient::send_get_properties(const std::vector<IndiProperty> &vecProperties)
{
  std::string xmlString = "";
  std::vector<IndiProperty>::const_iterator it; // const_iterator because vecProperties is const vector 
  IndiMessage* message = NULL;
  IndiXmlParser* xmlParser = NULL;
  
  
  // can be recoursive
  for ( it = vecProperties.begin(); it < vecProperties.end(); ++it)
    {
      message = new IndiMessage(IndiMessage::GetProperties, *it);
      xmlParser= new IndiXmlParser(*message, INDI_PROTOCOL_VERSION);
      xmlString = xmlString + xmlParser->createXmlString();
      delete xmlParser;
      delete message;
    }
  return m_socketConn.send(xmlString);
}

int IndiClient::get_socket_fd()
{
  return m_socketConn.getFd();
}


int IndiClient::recv_xml(std::string &strRecvXml)
{
  std::size_t pos = 0;
  std::size_t tmpPos = 0;
  int result = 0;
  int fdSocket = m_socketConn.getFd();
  const int timeOut = 10;
  const int MAX_BUFFER_SIZE = 2048;
  char socketBuffer[MAX_BUFFER_SIZE+1];
  fd_set fdReadSet;
  struct timeval tv;
  // Initialize time out struct
  tv.tv_sec = timeOut;
  tv.tv_usec = timeOut * 1000;

  while ((strRecvXml.empty()))
    {
      do {
        FD_ZERO(&fdReadSet);
        FD_SET(fdSocket, &fdReadSet);
        result = select(fdSocket + 1, &fdReadSet, NULL, NULL, &tv);
      } while (result == -1 && errno == EINTR);
      if (result == 0)
        {
          // time out
          std::cout << "time Out" << std::endl;
          break;
        }

      if (result > 0) {
        if (FD_ISSET(fdSocket, &fdReadSet))
          {
            /* The socket_fd has data available to be read */
            result = recv(fdSocket, socketBuffer, MAX_BUFFER_SIZE, 0);
            // result = recv(fdSocket, socketBuffer, MAX_BUFFER_SIZE, MSG_PEEK);
            if (result == 0) 
              {
                /* This means the other side closed the socket */
                m_socketConn.close();
                std::cout << "Connection closed on remote side" << std::endl;
                break;
              }
            else 
              {
                // Convert buffer in string type and check if it has to be cosidered empty 
                // (i.e. it contains only a new line char)
                strRecvXml = std::string(socketBuffer, result);

                tmpPos = strRecvXml.find_first_of('\n', pos);
                if ((tmpPos == 0) && (strRecvXml.size() == 1))
                  {
                    // Received string is considered empty, reset to receive new data
                    strRecvXml = "";
                  }

		// std::string::iterator itStr = strRecvXml.begin();
		// std::string::iterator tmpItStr = strRecvXml.begin();
		// while ((tmpPos = strRecvXml.find('\n', pos)) != std::string::npos)
		//   {
		//     std::cout << tmpPos << std::endl;
		//     std::cout << *(itStr+tmpPos+1) << std::endl;
		//     tmpItStr = strRecvXml.erase(itStr+tmpPos);//-pos);
		//     //itStr = tmpItStr;
		//     pos = tmpPos+1;
		//   }


		// strRecvXml.erase(std::remove(strRecvXml.begin(), strRecvXml.end(), '\n'), 
		// 		 strRecvXml.end());
		// erase_new_line(strRecvXml);
		// std::cout << "Received string without \\n  " << strRecvXml << std::endl;
		// std::cout << strRecvXml << std::endl;
              }
          }
      }
    }
  return result;  
}

int IndiClient::cb_recv_xml(std::string &strRecvXml, const int &nfds)
{
  std::size_t pos = 0;
  std::size_t tmpPos = 0;
  const int MAX_BUFFER_SIZE = 2048;
  char socketBuffer[MAX_BUFFER_SIZE+1];
  const int fdSocket = get_socket_fd();
  int result = 0;

  if (nfds == fdSocket)
    {
      result = recv(fdSocket, socketBuffer, MAX_BUFFER_SIZE, 0);
      if (result == 0) 
	{
	  /* This means the other side closed the socket */
	  m_socketConn.close();
	  std::cout << "Connection closed on remote side" << std::endl;
	}
      else 
	{
	  // Convert buffer in string type and check if it has to be cosidered empty 
	  // (i.e. it contains only a new line char)
	  strRecvXml = std::string(socketBuffer, result);
	  
	  tmpPos = strRecvXml.find_first_of('\n', pos);
	  if ((tmpPos == 0) && (strRecvXml.size() == 1))
	    {
	      // Received string is considered empty, reset to receive new data
	      strRecvXml = "";
	    }
	}
      // else
      // 	{
      // 	  //
      // 	}
    }
  return result;
}


int IndiClient::send_new_property2(const IndiProperty &property, 
				   IndiClient &c,
				   int (IndiClient::* rxml)(std::string &, const int &),
				   std::string &strRecvXml, const int &nfds)
{
  int result = -1;
  if (property.getState() != IndiProperty::Busy)
    {
      // State must be set to Busy when requesting a change in remote Property
      // if not Busy throw error
    }
  else 
    {
      IndiMessage message = IndiMessage(IndiMessage::NewProperty, property);
      IndiXmlParser xmlParser(message, INDI_PROTOCOL_VERSION);
      // std::cout << "**" << xmlParser.createXmlString() << "**" << std::endl;
      result = m_socketConn.send(xmlParser.createXmlString());
      int cb = (c.*rxml)(strRecvXml, nfds);
    }
 
  return result;
}

int IndiClient::send_new_property3(const IndiProperty &property, 
				   IndiClient &c,
				   int (IndiClient::* rxml)(std::string &),
				   std::string &strRecvXml)
{
  int result = -1;
  if (property.getState() != IndiProperty::Busy)
    {
      // State must be set to Busy when requesting a change in remote Property
      // if not Busy throw error
    }
  else 
    {
      IndiMessage message = IndiMessage(IndiMessage::NewProperty, property);
      IndiXmlParser xmlParser(message, INDI_PROTOCOL_VERSION);
      std::cout << "**" << xmlParser.createXmlString() << "**" << std::endl;
      result = m_socketConn.send(xmlParser.createXmlString());
      int cb = (c.*rxml)(strRecvXml);
    }
  std::cout << "XML string sent" << std::endl;
  return result;
}

int IndiClient::send_get_property3(const IndiProperty &property, 
				   IndiClient &c,
				   int (IndiClient::* rxml)(std::string &),
				   std::string &strRecvXml)
{
  int result = -1;
  IndiMessage message = IndiMessage(IndiMessage::GetProperties, property);
  IndiXmlParser xmlParser(message, INDI_PROTOCOL_VERSION);
  std::cout << "**" << xmlParser.createXmlString() << "**" << std::endl;
  result = m_socketConn.send(xmlParser.createXmlString());
  int cb = (c.*rxml)(strRecvXml);
 
  return result;
}


// void IndiClient::erase_new_line(std::string &str)
// {
//   std::size_t pos = str.find('\n');
//   if (pos != std::string::npos)
//     {
//       str.erase(str.begin()+pos);
//       std::cout << "erase" << std::endl;
//       erase_new_line(str);
//     }
//   else
//     {
//       // do nothing
//     }
// }

