import argparse

try:
    from ConfigParser import (
        NoSectionError,
        SafeConfigParser as ConfigParser
    )
except ImportError:
    from configparser import (
        NoSectionError,
        SafeConfigParser as ConfigParser
    )

from getpass import getpass
from os import path
import sys

import appdirs

from pypjlink import Projector
from pypjlink import projector
from pypjlink.cliutils import make_command, print_error

def cmd_power(p, state=None):
    if state is None:
        print(p.get_power())
    else:
        p.set_power(state)

def cmd_input(p, source, number):
    if source is None:
        source, number = p.get_input()
        print(source, number)
    else:
        p.set_input(source, number)

def cmd_inputs(p):
    for source, number in p.get_inputs():
        print('%s-%s' % (source, number))

def cmd_mute_state(p):
    video, audio = p.get_mute()
    print('video:', 'muted' if video else 'unmuted')
    print('audio:', 'muted' if audio else 'unmuted')

def cmd_mute(p, what):
    if what is None:
        return cmd_mute_state(p)
    what = {
        'video': projector.MUTE_VIDEO,
        'audio': projector.MUTE_AUDIO,
        'all': projector.MUTE_VIDEO | projector.MUTE_AUDIO,
    }[what]
    p.set_mute(what, True)

def cmd_unmute(p, what):
    if what is None:
        return cmd_mute_state(p)
    what = {
        'video': projector.MUTE_VIDEO,
        'audio': projector.MUTE_AUDIO,
        'all': projector.MUTE_VIDEO | projector.MUTE_AUDIO,
    }[what]
    p.set_mute(what, False)

def cmd_info(p):
    info = [
        ('Name', p.get_name()),
        ('Manufacturer', p.get_manufacturer()),
        ('Product Name', p.get_product_name()),
        ('Other Info', p.get_other_info())
    ]
    for key, value in info:
        print('%s: %s' % (key, value))

def cmd_lamps(p):
    for i, (time, state) in enumerate(p.get_lamps(), 1):
        print('Lamp %d: %s (%d hours)' % (
            i,
            'on' if state else 'off',
            time,
        ))

def cmd_errors(p):
    for what, state in p.get_errors().items():
        print('%s: %s' % (what, state))

def make_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--projector')
    parser.add_argument('-e', '--encoding', default='utf-8')

    sub = parser.add_subparsers(dest='command', title='command')
    sub.required = True

    power = make_command(sub, 'power', cmd_power)
    power.add_argument('state', nargs='?', choices=('on', 'off'))

    inpt = make_command(sub, 'input', cmd_input)
    inpt.add_argument('source', nargs='?', choices=projector.SOURCE_TYPES)
    inpt.add_argument('number', nargs='?', choices='123456789', default='1')

    make_command(sub, 'inputs', cmd_inputs)

    mute = make_command(sub, 'mute', cmd_mute)
    mute.add_argument('what', nargs='?', choices=('video', 'audio', 'all'))

    unmute = make_command(sub, 'unmute', cmd_unmute)
    unmute.add_argument('what', nargs='?', choices=('video', 'audio', 'all'))

    make_command(sub, 'info', cmd_info)
    make_command(sub, 'lamps', cmd_lamps)
    make_command(sub, 'errors', cmd_errors)

    return parser

def resolve_projector(projector):
    password = None

    # host:port
    if projector is not None and ':' in projector:
        host, port = projector.rsplit(':', 1)
        port = int(port)

    # maybe defined in config
    else:
        appdir = appdirs.user_data_dir('pjlink')
        conf_file = path.join(appdir, 'pjlink.conf')

        try:
            config = ConfigParser({'port': '4352', 'password': ''})
            with open(conf_file, 'r') as f:
                config.readfp(f)

            section = projector
            if projector is None:
                section = 'default'

            host = config.get(section, 'host')
            port = config.getint(section, 'port')
            password = config.get(section, 'password') or None

        except (NoSectionError, IOError):
            if projector is None:
                raise KeyError('No default projector defined in %s' % conf_file)

            # no config file, or no projector defined for this host
            # thus, treat the projector as a hostname w/o port
            host = projector
            port = 4352


    return host, port, password

def main():
    parser = make_parser()
    args = parser.parse_args()

    kwargs = dict(args._get_kwargs())
    func = kwargs.pop('__func__')
    kwargs.pop('command', None)

    projector = kwargs.pop('projector')
    host, port, password = resolve_projector(projector)
    encoding = kwargs.pop('encoding')

    if not password:
        password = getpass

    with Projector.from_address(host, port, encoding) as proj:
        rv = proj.authenticate(password)
        if rv is False:
            print_error('Incorrect password.')
            return

        func(proj, **kwargs)

if __name__ == '__main__':
    main()
