/*-
 * syslog-rs - a syslog client translated from libc to rust
 * 
 * Copyright 2025 Aleksandr Morozov
 * 
 * The syslog-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */

use std::fmt;

use crate::{error::SyRes, AsyncSyslogDestination, TapType};

/// A trait which implements the necessary functions for syslog internals.
/// It `taps` the connection and provides the functionality for the syslog client
/// to use the communication channel.
#[allow(async_fn_in_trait)]
pub trait AsyncSyslogTap<D: AsyncSyslogDestination + fmt::Display>: fmt::Debug + Send
{
    /// Proves the new instance of the syslog communication.
    /// 
    /// # Arguments
    /// 
    /// `req_tap` - a connection endpoint setup information i.e address, path...
    /// 
    /// # Returns
    /// 
    /// In case of any error a [SyRes] i.e [Result::Err] should be returned.
    fn new(req_tap: D) -> SyRes<Self> where Self: Sized;

    /// Opens a connection to target.
    async fn connectlog(&mut self) -> SyRes<()>;

    /// Writes provided data to socket
    async fn send(&mut self, msg: &[u8]) -> std::io::Result<usize>;

    /// Disconnects the connection.
    async fn disconnectlog(&mut self) -> std::io::Result<()>;

    /// Tells if connection was opened.
    fn is_connected(&self) -> bool;

    /// Returns the exact type of the connection.
    fn get_type(&self) -> TapType;

    /// Returns (in bytes) the maximum message size (including headers).
    fn get_max_msg_size() -> usize;

    /// Updates the connection information without breaking tap.
    fn update_tap_data(&mut self, tap_data: D);
}

/// A structure which holds a socket instance and all necessary information
/// which can be used to reestablish the connection. This is for async only.
#[derive(Debug)]
pub struct AsyncTap<T, D: AsyncSyslogDestination>
{
    /// Channel
    pub(crate) sock: Option<T>,

    /// Tap settings and state
    pub(crate) tap_data: D,

    pub(crate) cur_tap_type: TapType,
}

/// An implementation for the external executor setup.
#[cfg(feature = "build_async_interface")]
impl<T, D: AsyncSyslogDestination> AsyncTap<T, D>
{
    /// Creates the new socket instance which is `tapped`.
    pub 
    fn new(req_tap: D) -> SyRes<AsyncTap<T, D>>
    {
        return Ok(
            Self
            {
                sock: None, 
                tap_data: req_tap,
                cur_tap_type: TapType::None,
            }
        );
    }

    /// Returns the information about the `tap` connection.
    pub 
    fn get_tap_data(&self) -> &D
    {
        return &self.tap_data;
    }

    /// Updates the current connection information on the intances records. It
    /// does not performs reconnect or whatever IO.
    pub 
    fn update_tap_data(&mut self, tap_data: D)
    {
        self.tap_data = tap_data;
    }

    /// Returns the connection type identification.
    pub 
    fn get_tap_type(&self) -> TapType
    {
        return self.cur_tap_type;
    }

    /// Returns the access to connection.
    pub 
    fn get_sock(&self) -> Option<&T>
    {
        return self.sock.as_ref();
    }

    /// Returns the mutable access to connection.
    pub 
    fn get_sock_mut(&mut self) -> Option<&mut T>
    {
        return self.sock.as_mut();
    }

    /// Consumes the connections and returns to caller.
    pub 
    fn take_sock(&mut self) -> Option<T>
    {
        return self.sock.take();
    }

    /// Sets the connection socket of type `T`.
    pub 
    fn set_sock(&mut self, sock: T)
    {
        self.sock.replace(sock);

        return;
    }

    /// Sets the type of the connection.
    pub 
    fn set_cur_tap_type(&mut self, cur_tap_type: TapType)
    {
        return self.cur_tap_type = cur_tap_type;
    }
}
