from __future__ import absolute_import, division, print_function
__metaclass__ = type

import os
import socket


def path_env_var_name(name):
    '''
    Get the ENV var name whose value (if any) will be the file system path
    to the UNIX socket file for that IO stream.

    The names in current use:

    >>> path_env_var_name('out')
    'QB_STDIO_OUT'

    >>> path_env_var_name('err')
    'QB_STDIO_ERR'

    >>> path_env_var_name('log')
    'QB_STDIO_LOG'
    '''

    return "QB_STDIO_{}".format(name.upper())


class Connection:
    '''
    Port of Ruby `QB::IPC::STDIO::Client::Connection` class.
    '''
    
    def __init__(self, name, type):
        self.name = name
        self.type = type
        self.path = None
        self.socket = None
        self.env_var_name = path_env_var_name(self.name)
        self.connected = False
    
    def __str__(self):
        attrs = ' '.join(
            "{}={}".format(name, getattr(self, name))
            for name in ('name', 'type', 'path', 'connected')
        )
        return "<qb.ipc.stdio.Connection {}>".format(attrs)
    
    def get_path(self):
        if self.env_var_name in os.environ:
            self.path = os.environ[self.env_var_name]
        return self.path
    
    def connect(self, warnings=None):
        if self.connected:
            raise RuntimeError("{} is already connected!".format(self))
        
        if self.get_path() is None:
            return False
        
        self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        
        try:
            self.socket.connect(self.path)
        except socket.error, msg:
            if warngings is not None:
                warning = 'Failed to connect to QB STDOUT stream at {}: {}'
                warning = warning.format(qb_stdout_path, msg)
                warnings.append(warning)
            
            self.socket = None
            return False
        
        self.connected = True
        
        return True
    
    def disconnect(self):
        if not self.connected:
            raise RuntimeError("{} is not connected!".format(self))
        
        # if self.type == 'out':
        #     self.socket.flush()
        
        self.socket.close()
        self.socket = None
        self.connected = False
        
    def println(self, line):
        if not line.endswith( u"\n" ):
            line = line + u"\n"
        self.socket.sendall(line.encode("utf-8"))
        

class Client:
    def __init__(self):
        # I don't think need STDIN or we want to deal with what it means here
        # self.stdin  = Connection(name='in', type='in')
        self.stdout = Connection(name='out', type='out')
        self.stderr = Connection(name='err', type='out')
        self.log    = Connection(name='log', type='out')
    
    def connections(self):
        return [self.stdout, self.stderr, self.log]
    
    def connect(self, warnings=None):
        for connection in self.connections():
            if not connection.connected:
                connection.connect(warnings)
        return self
    
    def disconnect(sefl):
        for connection in self.connections():
            if connection.connected:
                connection.disconnect()

client = Client()