#!/usr/bin/python
#    Copyright (C) 2014  Yubico AB
#
#    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/>.

from u2flib_host import u2f, exc
from u2flib_host.constants import APDU_USE_NOT_SATISFIED
from u2flib_host.utils import u2str
import time
import json
import argparse
import sys


def register(devices, params, facet):
    """
    Interactively registers a single U2F device, given the RegistrationRequest.
    """
    for device in devices[:]:
        try:
            device.open()
        except:
            devices.remove(device)

    sys.stderr.write('\nTouch the U2F device you wish to register...\n')
    try:
        while devices:
            removed = []
            for device in devices:
                try:
                    return u2f.register(device, params, facet)
                except exc.APDUError as e:
                    if e.code == APDU_USE_NOT_SATISFIED:
                        pass
                    else:
                        removed.append(device)
                except exc.DeviceError:
                    removed.append(device)
            devices = [d for d in devices if d not in removed]
            for d in removed:
                d.close()
            time.sleep(0.25)
    finally:
        for device in devices:
            device.close()
    sys.stderr.write('\nUnable to register with any U2F device.\n')
    sys.exit(1)


def parse_args():
    parser = argparse.ArgumentParser(
        description="Registers a U2F device.\n"
        "Takes a JSON formatted RegisterRequest object on stdin, and returns "
        "the resulting RegistrationResponse on stdout.",
        add_help=True
    )
    parser.add_argument('facet', help='the facet for registration')
    parser.add_argument('-i', '--infile', help='specify a file to read '
                        'RegistrationRequest from, instead of stdin')
    parser.add_argument('-o', '--outfile', help='specify a file to write '
                        'the RegistrationResponse to, instead of stdout')
    parser.add_argument('-s', '--soft', help='Specify a soft U2F device file '
                        'to use')
    return parser.parse_args()


if __name__ == '__main__':
    args = parse_args()

    facet = unicode(args.facet, sys.stdin.encoding or sys.getdefaultencoding())
    if args.infile:
        with open(args.infile, 'r') as f:
            data = f.read()
    else:
        if sys.stdin.isatty():
            sys.stderr.write('Enter RegistrationRequest JSON data...\n')
        data = sys.stdin.read()
    params = json.loads(data, object_hook=u2str)

    if args.soft:
        from u2flib_host.soft import SoftU2FDevice
        devices = [SoftU2FDevice(args.soft)]
    else:
        devices = u2f.list_devices()
    result = register(devices, params, facet)

    if args.outfile:
        with open(args.outfile, 'w') as f:
            json.dump(result, f)
        sys.stderr.write('Output written to %s\n' % args.outfile)
    else:
        sys.stderr.write('\n---Result---\n')
        print json.dumps(result)
