# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import logging

from autotest_lib.client.common_lib.cros.network import ap_constants
from autotest_lib.client.common_lib.cros.network import ping_runner
from autotest_lib.server.cros.ap_configurators import ap_spec


class PduNotResponding(Exception):
    """PDU exception class."""
    def __init__(self, PDU):
        self.PDU = PDU
        logging.info('Caught PDU exception for %s', self.PDU)

    def __str__(self):
        return repr('Caught PDU exception for %s' % self.PDU)


class APConfiguratorAbstract(object):
    """Abstract Base class to find and control access points."""

    def __init__(self):
        super(APConfiguratorAbstract, self).__init__()
        # Some APs will turn on their beacon, but the DHCP server is not
        # running.  Each configurator can add this delay to have the test
        # wait before attempting to connect.
        self._dhcp_delay = 0


    @property
    def dhcp_delay(self):
        """Returns the DHCP delay."""
        return self._dhcp_delay


    @property
    def ssid(self):
        """Returns the SSID."""
        raise NotImplementedError('Missing subclass implementation')


    @property
    def name(self):
        """Returns a string to describe the router."""
        raise NotImplementedError('Missing subclass implementation')


    @property
    def configuration_success(self):
        """Returns configuration status as defined in ap_constants"""
        return self._configuration_success


    @configuration_success.setter
    def configuration_success(self, value):
        """
        Set the configuration status.

        @param value: the status of AP configuration.

        """
        self._configuration_success = value


    @property
    def configurator_type(self):
        """Returns the configurator type."""
        return ap_spec.CONFIGURATOR_STATIC


    @property
    def short_name(self):
        """Returns a short string to describe the router."""
        raise NotImplementedError('Missing subclass implementation')


    def check_pdu_status(self):
        """Check if the PDU is up before making any request."""
        ping_options = ping_runner.PingConfig(self.pdu, count=2,
                                              ignore_status=True,
                                              ignore_result=True)
        runner = ping_runner.PingRunner()
        logging.info('Pinging rpm %s', self.pdu)
        ping_result = runner.ping(ping_options)
        logging.info('ping result = %s', str(ping_result))
        # If all ping packets failed then mark PDU down.
        if ping_result.loss == 100:
            self.configuration_success = ap_constants.PDU_FAIL
            raise PduNotResponding(self.pdu)


    def get_supported_bands(self):
        """Returns a list of dictionaries describing the supported bands.

        Example: returned is a dictionary of band and a list of channels. The
                 band object returned must be one of those defined in the
                 __init___ of this class.

        supported_bands = [{'band' : self.band_2GHz,
                            'channels' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]},
                           {'band' : ap_spec.BAND_5GHZ,
                            'channels' : [26, 40, 44, 48, 149, 153, 165]}]

        Note: The derived class must implement this method.

        @return a list of dictionaries as described above

        """
        raise NotImplementedError('Missing subclass implementation')


    def get_supported_modes(self):
        """
        Returns a list of dictionaries describing the supported modes.

        Example: returned is a dictionary of band and a list of modes. The band
                 and modes objects returned must be one of those defined in the
                 __init___ of this class.

        supported_modes = [{'band' : ap_spec.BAND_2GHZ,
                            'modes' : [mode_b, mode_b | mode_g]},
                           {'band' : ap_spec.BAND_5GHZ,
                            'modes' : [mode_a, mode_n, mode_a | mode_n]}]

        Note: The derived class must implement this method.

        @return a list of dictionaries as described above

        """
        raise NotImplementedError('Missing subclass implementation')


    def is_visibility_supported(self):
        """
        Returns if AP supports setting the visibility (SSID broadcast).

        @return True if supported; False otherwise.

        """
        return True


    def is_band_and_channel_supported(self, band, channel):
        """
        Returns if a given band and channel are supported.

        @param band: the band to check if supported
        @param channel: the channel to check if supported

        @return True if combination is supported; False otherwise.

        """
        raise NotImplementedError('Missing subclass implementation')


    def is_security_mode_supported(self, security_mode):
        """
        Returns if a given security_type is supported.

        Note: The derived class must implement this method.

        @param security_mode: one of the following modes:
                         self.security_disabled,
                         self.security_wep,
                         self.security_wpapsk,
                         self.security_wpa2psk

        @return True if the security mode is supported; False otherwise.

        """
        raise NotImplementedError('Missing subclass implementation')



    def set_using_ap_spec(self, set_ap_spec, power_up=True):
        """
        Sets all configurator options.
        Note: The derived class may override this method.

        @param set_ap_spec: APSpec object
        @param power_up: bool, enable power via rpm if applicable

        """
        logging.warning('%s.%s: Not Implemented',
                self.__class__.__name__,
                self.set_using_ap_spec.__name__)


    def apply_settings(self):
        """
        Apply all settings to the access point.
        Note: The derived class may override this method.

        """
        logging.warning('%s.%s: Not Implemented',
                self.__class__.__name__,
                self.apply_settings.__name__)


    def get_association_parameters(self):
        """
        Returns xmlrpc_datatypes.AssociationParameters for this AP

        Note: The derived class must implement this method.

        @return xmlrpc_datatypes.AssociationParameters

        """
        raise NotImplementedError('Missing subclass implementation')


    def debug_last_failure(self, outputdir):
        """
        Write debug information for last AP_CONFIG_FAIL

        @param outputdir: a string directory path for debug files
        """
        pass


    def debug_full_state(self, outputdir):
        """
        Write debug information for full AP state

        @param outputdir: a string directory path for debug files
        """
        pass


    def store_config_failure(self, trace):
        """
        Store configuration failure for latter logging

        @param trace: a string traceback of config exception
        """
        pass