#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef _WIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#include <vector>
#include <iostream>
#include <fstream>
#include <time.h>

#define STD_PORT       9777

#define MS_ABSOLUTE    0x01
//#define MS_RELATIVE    0x02

#define BTN_USE_NAME   0x01
#define BTN_DOWN       0x02
#define BTN_UP         0x04
#define BTN_USE_AMOUNT 0x08
#define BTN_QUEUE      0x10
#define BTN_NO_REPEAT  0x20
#define BTN_VKEY       0x40
#define BTN_AXIS       0x80

#define PT_HELO         0x01
#define PT_BYE          0x02
#define PT_BUTTON       0x03
#define PT_MOUSE        0x04
#define PT_PING         0x05
#define PT_BROADCAST    0x06
#define PT_NOTIFICATION 0x07
#define PT_BLOB         0x08
#define PT_LOG          0x09
#define PT_ACTION       0x0A
#define PT_DEBUG        0xFF

#define ICON_NONE       0x00
#define ICON_JPEG       0x01
#define ICON_PNG        0x02
#define ICON_GIF        0x03

#define MAX_PACKET_SIZE  1024
#define HEADER_SIZE      32
#define MAX_PAYLOAD_SIZE (MAX_PACKET_SIZE - HEADER_SIZE)

#define MAJOR_VERSION 2
#define MINOR_VERSION 0

#define LOGDEBUG   0
#define LOGINFO    1
#define LOGNOTICE  2
#define LOGWARNING 3
#define LOGERROR   4
#define LOGSEVERE  5
#define LOGFATAL   6
#define LOGNONE    7

#define ACTION_EXECBUILTIN 0x01
#define ACTION_BUTTON      0x02

class CAddress
{
private:
  struct sockaddr_in m_Addr;
public:
  CAddress(int Port = STD_PORT)
  {
    m_Addr.sin_family = AF_INET;
    m_Addr.sin_port = htons(Port);
    m_Addr.sin_addr.s_addr = INADDR_ANY;
    memset(m_Addr.sin_zero, '\0', sizeof m_Addr.sin_zero);
  }

  CAddress(const char *Address, int Port = STD_PORT)
  {
    m_Addr.sin_port = htons(Port);

    struct hostent *h;
    if (Address == NULL || (h=gethostbyname(Address)) == NULL)
    {
        if (Address != NULL)
			printf("Error: Get host by name\n");

        m_Addr.sin_addr.s_addr  = INADDR_ANY;
        m_Addr.sin_family       = AF_INET;
    }
    else
    {
      m_Addr.sin_family = h->h_addrtype;
      m_Addr.sin_addr = *((struct in_addr *)h->h_addr);
    }
    memset(m_Addr.sin_zero, '\0', sizeof m_Addr.sin_zero);
  }

  void SetPort(int port)
  {
    m_Addr.sin_port = htons(port);
  }

  const sockaddr *GetAddress()
  {
    return ((struct sockaddr *)&m_Addr);
  }

  bool Bind(int Sockfd)
  {
    return (bind(Sockfd, (struct sockaddr *)&m_Addr, sizeof m_Addr) == 0);
  }
};

class XBMCClientUtils
{
public:
  XBMCClientUtils() = default;
  ~XBMCClientUtils() = default;
  static unsigned int GetUniqueIdentifier()
  {
    static time_t id = time(NULL);
    return id;
  }

  static void Clean()
  {
  #ifdef _WIN32
    WSACleanup();
  #endif
  }

  static bool Initialize()
  {
  #ifdef _WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(1, 1), &wsaData))
      return false;
  #endif
    return true;
  }
};

class CPacket