/*---------------------------------------------------------------------\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\----------------------------------------------------------------------/
*
* This file contains private API, this might break at any time between releases.
* You have been warned!
*
*/

#ifndef ZYPPNG_IO_SOCKET_P_DEFINED
#define ZYPPNG_IO_SOCKET_P_DEFINED

#include <zypp-core/ng/io/socket.h>
#include <zypp-core/ng/io/private/iobuffer_p.h>
#include <zypp-core/ng/base/socketnotifier.h>
#include <zypp-core/ng/base/Timer>
#include "iodevice_p.h"

#include <variant>

namespace zyppng {

  class SocketPrivate : public IODevicePrivate
  {
    ZYPP_DECLARE_PUBLIC(Socket)
  public:

    SocketPrivate( int domain, int type, int protocol, Socket &p ) : IODevicePrivate(p),
      _domain(domain),
      _type( type ),
      _protocol( protocol )
    { }

    static Socket::Ptr wrapSocket( int fd, int domain, int type, int protocol, Socket::SocketState state );

    bool initSocket () ;
    void setError ( Socket::SocketError error, std::string &&err, bool emit = true );
    bool handleConnectError ( int error );

    bool transition ( Socket::SocketState newState );
    Socket::SocketState state () const;

    bool connectToHost ();
    void onSocketActivated ( int ev );
    void onSocketActivatedSlot ( const SocketNotifier &, int ev ) {
      return onSocketActivated(ev);
    }
    int64_t rawBytesAvailable () const;
    bool readRawBytesToBuffer ();
    bool writePendingData ();

    int _domain;
    int _type;
    int _protocol;
    std::shared_ptr<SockAddr> _targetAddr;

    int _socket = -1;
    bool _borrowedSocket = false;

    //error handling
    bool _emittedErr = false;
    Socket::SocketError _error = Socket::NoError;
    std::string _errorDesc;

    //signals
    Signal< void(Socket::SocketError)> _sigError;
    Signal< void()> _incomingConnection;
    Signal< void()> _connected;
    Signal< void()> _disconnected;


    struct InitialState {
      static constexpr Socket::SocketState type() { return Socket::InitialState; }
    };

    struct ConnectingState : public sigc::trackable {
      NON_COPYABLE(ConnectingState);
      ConnectingState() = default;

      static constexpr Socket::SocketState type() { return Socket::ConnectingState; }

      SocketNotifier::Ptr _connectNotifier;
      Timer::Ptr _connectTimeout;
    };

    struct ConnectedState : public sigc::trackable {
      NON_COPYABLE(ConnectedState);
      ConnectedState() = default;
      static constexpr Socket::SocketState type() { return Socket::ConnectedState; }
      SocketNotifier::Ptr _socketNotifier;
      IOBuffer _writeBuffer;
    };

    struct ListeningState : public sigc::trackable {
      NON_COPYABLE(ListeningState);
      ListeningState() = default;
      static constexpr Socket::SocketState type() { return Socket::ListeningState; }
      SocketNotifier::Ptr _socketNotifier;
    };

    struct ClosingState {
      static constexpr Socket::SocketState type() { return Socket::ClosingState; }
      ClosingState( IOBuffer &&writeBuffer );

      SocketNotifier::Ptr _socketNotifier;
      IOBuffer _writeBuffer;
    };

    struct ClosedState {
      static constexpr Socket::SocketState type() { return Socket::ClosedState; }
    };
    std::variant< InitialState, ConnectingState, ConnectedState, ListeningState, ClosingState, ClosedState > _state = InitialState();
  };

}

#endif
