# import Polatis
import re
# DEBUG flag
debug = 0
[docs]def sdebug(msg):
    if debug > 0:
        print 'SwitchHandler:: {}'.format(msg) 
def _debug_setup(filename):
    class flushFile(object):
        def __init__(self, fileL):
            if debug == 0:
                return
            self.fileL = open(fileL, 'w')
        def writef(self, msg):
            if debug == 0:
                return
            self.fileL.write(msg + '\n')
            self.fileL.flush()
    logfile = flushFile(filename)
    return logfile
[docs]class SwitchHandler(object):
    '''The intention of this class if to provide a general implementation for any kind of fiber switch.
    The key features of the class are get_switch_state(), and switch_book object.'''
    def __init__(self, configFile, stages, resource):
        if not resource:
            raise ValueError('SwitchHandler:: invalid raw_switches dict passed in costructor')
        self._p = resource
        self.active = False # only for compliance with ScriptController
        sdebug('reading config file to fill in switch book...')
        self.switch_book = self._readIn(configFile,stages)
        sdebug('switch book complete.')
        self.active_connetions = {}
[docs]    def get_switch_state(self):
        '''
        Queries all current connections.
        :returns: String of all connections between devices
        '''
        formatted_output = ""
        for k,v in self.active_connetions.iteritems():
            k_name, k_switch = k
            v_name, v_switch = v
            formatted_output += "{:15} : {:15} {:5} {:15} {:15}\n".format(
            k_name,
            '{}[{}]'.format(*k_switch),
            "--->",
            v_name,
            '{}[{}]'.format(*v_switch),
            )
        if formatted_output:
            return formatted_output
        else:
            return "No Connnections Logged" 
[docs]    def connect_devices(self, in_device, out_device):
        '''
        Connect two devices together:
            in_device::out ---> out_device::in
        '''
        (_, pair_in), (pair_out, _) = (self.switch_book[in_device], self.switch_book[out_device])
        if pair_in[0] != pair_out[0]:
            raise RuntimeError('SwitchHandler:: Asked to connect devices ({}, {}) attached to separate switches.'.format(in_device, out_device))
        ingress = pair_in[1]
        egress  = pair_out[1]
        sdebug('connecting {}[{}] - {}[{}]'.format(pair_in[0], ingress, pair_out[0], egress))
        sdebug('under swicth: {}'.format(self._p[pair_in[0]]))
        self._p[pair_in[0]].quick_connect(ingress, egress)
        # log the connection
        self.active_connetions[(in_device, pair_in)] = (out_device, pair_out) 
    def _readIn(self, configFile,stages):
        '''Reads in the configFile info'''
        switch_book_init = {}
        bug = _debug_setup('switch_handler_debug.txt')
        bug.writef('stages: {}'.format(stages))
        for entry in configFile:
            if entry['O'] != 'Polatis' and 'P' in entry.keys(): #skip the Polatis
                bug.writef('\nentry: ' + str(entry))
                # continue parsing
                ports = list(map(lambda x: x.strip(), entry['P'].split('>')))
                if len(ports) == 1 or len(entry['P'].strip()) == 1:
                    # User didn't specify '>' or just declared '>'
                    raise AttributeError('Invalid switch port decalration in Config File. Hint: {}'.format(entry))
                ingress = []
                egress = []
                if len(ports[0].strip()) == 0:
                    # we only have output ports
                    bug.writef('we only have output ports')
                    egress = list(map(lambda x: self._parseSwitchPair(x), ports[1].split(',')))
                    ingress = [None] * len(egress)
                elif len(ports[1].strip()) == 0:
                    # we only have input ports
                    bug.writef('we only have input ports')
                    ingress = list(map(lambda x: self._parseSwitchPair(x), ports[0].split(',')))
                    egress = [None] * len(ingress)
                else:
                    # both input and output ports have been specified
                    bug.writef('both input and output ports have been specified')
                    ingress = list(map(lambda x: self._parseSwitchPair(x), ports[0].split(',')))
                    egress = list(map(lambda x: self._parseSwitchPair(x), ports[1].split(',')))
                ports = list(zip(ingress, egress))
                bug.writef('ports: ' + str(ports))
                for actualObject in zip(stages.keys(), stages.values()):
                    if entry['OBJ'] == actualObject[1]:
                        stage_name = actualObject[0]
                        bug.writef('stage_type: ' + str(stage_name))
                        for pair in ports:
                            switch_book_init[stage_name]=pair
                            bug.writef('inserted: {} --> {}'.format(stage_name, str(pair)))
        return switch_book_init
    def _parseSwitchPair(self, formatted):
        # parse out the raw_switch_id and port from -> SwitchId[PortNumber]
        bracket_match = re.match(r'\s*(\w+)\s*\[\s*(\w+)\s*\]', formatted)
        if not bracket_match:
            raise ValueError('Incorrect port specification in {}. Expected SwitchId[PortNo]\nHint: {}'.format(entry['P'], entry))
        switch, port_s = bracket_match.groups()
        return switch, port_s
    ## NOTE Thinking about getting rid of this
    def _integrateStages(self, stages):
        '''Integrates the stages dictionary after boot'''
        for actualObject in zip(stages.keys(), stages.values()):
            for k,entry in self.switch_book.items():
                if entry['device'] == str(actualObject[1]):
                    entry['ref'] = actualObject[0]
[docs]    def whoAmI(self):
        return 'SwitchHandler' 
    def __str__(self):
        return '' 
'''
Copyright (C) 2017  Robert Polster
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''