"""
Created on 2014.03.15

@author: Giovanni Cannata

Copyright 2014 Giovanni Cannata

This file is part of python3-ldap.

python3-ldap is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

python3-ldap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with python3-ldap in the COPYING and COPYING.LESSER files.
If not, see <http://www.gnu.org/licenses/>.
"""
from datetime import datetime, timedelta
from os import linesep
from .exceptions import LDAPMetricsError


class ConnectionUsage(object):
    """
    Collect statistics on connection usage
    """

    def reset(self):
        self.opened_sockets = 0
        self.closed_sockets = 0
        self.wrapped_sockets = 0
        self.bytes_transmitted = 0
        self.bytes_received = 0
        self.messages_transmitted = 0
        self.messages_received = 0
        self.operations = 0
        self.abandon_operations = 0
        self.add_operations = 0
        self.bind_operations = 0
        self.compare_operations = 0
        self.delete_operations = 0
        self.extended_operations = 0
        self.modify_operations = 0
        self.modify_dn_operations = 0
        self.search_operations = 0
        self.unbind_operations = 0
        self.referrals_received = 0
        self.referrals_followed = 0
        self.restartable_failures = 0
        self.restartable_successes = 0
        self.servers_from_pool = 0

    def __init__(self):
        self.initial_connection_start_time = None
        self.open_socket_start_time = None
        self.connection_stop_time = None
        self.opened_sockets = 0
        self.closed_sockets = 0
        self.wrapped_sockets = 0
        self.bytes_transmitted = 0
        self.bytes_received = 0
        self.messages_transmitted = 0
        self.messages_received = 0
        self.operations = 0
        self.abandon_operations = 0
        self.add_operations = 0
        self.bind_operations = 0
        self.compare_operations = 0
        self.delete_operations = 0
        self.extended_operations = 0
        self.modify_operations = 0
        self.modify_dn_operations = 0
        self.search_operations = 0
        self.unbind_operations = 0
        self.referrals_received = 0
        self.referrals_followed = 0
        self.restartable_failures = 0
        self.restartable_successes = 0
        self.servers_from_pool = 0

    def __repr__(self):
        r = 'Connection Usage:' + linesep
        r += '  Time: [elapsed:        ' + str(self.elapsed_time) + ']' + linesep
        r += '    Initial start time:  ' + (str(self.initial_connection_start_time.isoformat()) if self.initial_connection_start_time else '') + linesep
        r += '    Open socket time:    ' + (str(self.open_socket_start_time.isoformat()) if self.open_socket_start_time else '') + linesep
        r += '    Close socket time:   ' + (str(self.connection_stop_time.isoformat()) if self.connection_stop_time else '') + linesep
        r += '  Server:' + linesep
        r += '    Servers from pool:   ' + str(self.servers_from_pool) + linesep
        r += '    Sockets opened:      ' + str(self.opened_sockets) + linesep
        r += '    Sockets closed:      ' + str(self.closed_sockets) + linesep
        r += '    Sockets wrapped:     ' + str(self.wrapped_sockets) + linesep
        r += '  Bytes:                 ' + str(self.bytes_transmitted + self.bytes_received) + linesep
        r += '    Transmitted:         ' + str(self.bytes_transmitted) + linesep
        r += '    Received:            ' + str(self.bytes_received) + linesep
        r += '  Messages:              ' + str(self.messages_transmitted + self.messages_received) + linesep
        r += '    Transmitted:         ' + str(self.messages_transmitted) + linesep
        r += '    Received:            ' + str(self.messages_received) + linesep
        r += '  Operations:            ' + str(self.operations) + linesep
        r += '    Abandon:             ' + str(self.abandon_operations) + linesep
        r += '    Bind:                ' + str(self.bind_operations) + linesep
        r += '    Compare:             ' + str(self.compare_operations) + linesep
        r += '    Delete:              ' + str(self.delete_operations) + linesep
        r += '    Extended:            ' + str(self.extended_operations) + linesep
        r += '    Modify:              ' + str(self.modify_operations) + linesep
        r += '    ModifyDn:            ' + str(self.modify_dn_operations) + linesep
        r += '    Search:              ' + str(self.search_operations) + linesep
        r += '    Unbind:              ' + str(self.unbind_operations) + linesep
        r += '  Referrals:             ' + linesep
        r += '    Received:            ' + str(self.referrals_received) + linesep
        r += '    Followed:            ' + str(self.referrals_followed) + linesep
        r += '  Restartable tries:     ' + str(self.restartable_failures + self.restartable_successes) + linesep
        r += '    Failed restarts:     ' + str(self.restartable_failures) + linesep
        r += '    Successful restarts: ' + str(self.restartable_successes) + linesep
        return r

    def __str__(self):
        return self.__repr__()

    def __iadd__(self, other):
        if not isinstance(other, ConnectionUsage):
            raise LDAPMetricsError('unable to add to ConnectionUsage')

        self.opened_sockets += other.opened_sockets
        self.closed_sockets += other.closed_sockets
        self.wrapped_sockets += other.wrapped_sockets
        self.bytes_transmitted += other.bytes_transmitted
        self.bytes_received += other.bytes_received
        self.messages_transmitted += other.messages_transmitted
        self.messages_received += other.messages_received
        self.operations += other.operations
        self.abandon_operations += other.abandon_operations
        self.add_operations += other.add_operations
        self.bind_operations += other.bind_operations
        self.compare_operations += other.compare_operations
        self.delete_operations += other.delete_operations
        self.extended_operations += other.extended_operations
        self.modify_operations += other.modify_operations
        self.modify_dn_operations += other.modify_dn_operations
        self.search_operations += other.search_operations
        self.unbind_operations += other.unbind_operations
        self.referrals_received += other.referrals_received
        self.referrals_followed += other.referrals_followed
        self.restartable_failures += other.restartable_failures
        self.restartable_successes += other.restartable_successes
        self.servers_from_pool += other.servers_from_pool
        return self

    def transmitted_message(self, message, length):
        self.bytes_transmitted += length
        self.operations += 1
        self.messages_transmitted += 1
        if message['type'] == 'abandonRequest':
            self.abandon_operations += 1
        elif message['type'] == 'addRequest':
            self.add_operations += 1
        elif message['type'] == 'bindRequest':
            self.bind_operations += 1
        elif message['type'] == 'compareRequest':
            self.compare_operations += 1
        elif message['type'] == 'delRequest':
            self.delete_operations += 1
        elif message['type'] == 'extendedReq':
            self.extended_operations += 1
        elif message['type'] == 'modifyRequest':
            self.modify_operations += 1
        elif message['type'] == 'modDNRequest':
            self.modify_dn_operations += 1
        elif message['type'] == 'searchRequest':
            self.search_operations += 1
        elif message['type'] == 'unbindRequest':
            self.unbind_operations += 1
        else:
            raise LDAPMetricsError('unable to collect usage for unknown message type')

    def received_message(self, length):
        self.bytes_received += length
        self.messages_received += 1

    def start(self, reset=True):
        if reset:
            self.reset()
        self.open_socket_start_time = datetime.now()
        self.connection_stop_time = None
        if not self.initial_connection_start_time:
            self.initial_connection_start_time = self.open_socket_start_time

    def stop(self):
        if self.open_socket_start_time:
            self.connection_stop_time = datetime.now()

    @property
    def elapsed_time(self):
        if self.connection_stop_time:
            return self.connection_stop_time - self.open_socket_start_time
        else:
            # noinspection PyTypeChecker
            return (datetime.now() - self.open_socket_start_time) if self.open_socket_start_time else timedelta(0)
