#!/usr/bin/env python3
# pylint: disable=invalid-name

import argparse
import logging
import os.path
import pprint
import sys

import i3ipc

from i3wsgroups import cli
from i3wsgroups import controller as i3_groups_controller
from i3wsgroups import i3_proxy, logger, workspace_names

_LIST_WORKSPACES_FIELDS = workspace_names.WORKSPACE_NAME_SECTIONS + [
    'window_icons', 'global_name', 'monitor'
]
_LIST_WORKSPACES_FIELDS_HELP = (
    'Comma separated list of fields to output. Options: {}').format(
        ', '.join(_LIST_WORKSPACES_FIELDS))

init_logger = logger.init_logger
logger = logger.logger


def _add_group_args(parser: argparse.ArgumentParser) -> None:
    # The argparse argument group of the workspace group arguments.
    group_arg_group = parser.add_mutually_exclusive_group()
    group_arg_group.add_argument(
        '--group-active',
        action='store_true',
        default=None,
        help=
        'Use the active group for any commands that implicitly assume a group, '
        'such as workspace-next.')
    group_arg_group.add_argument(
        '--group-focused',
        action='store_true',
        default=None,
        help='Use the focused group for any commands that implicitly assume a '
        'group, such as workspace-next.')
    group_arg_group.add_argument('--group-name')


def _add_list_workspaces_args(parser: argparse.ArgumentParser) -> None:
    _add_group_args(parser)
    parser.add_argument('--fields',
                        default=','.join(_LIST_WORKSPACES_FIELDS),
                        help=_LIST_WORKSPACES_FIELDS_HELP)
    parser.add_argument(
        '--focused-only',
        action='store_true',
        help='List only the focused workspace in the given group context.')
    parser.add_argument('--focused-monitor-only',
                        action='store_true',
                        help='List only workspaces on the current monitor.')


def _add_rename_workspace_args(parser: argparse.ArgumentParser) -> None:
    parser.add_argument(
        '--name',
        help='New name to set for the workspace.\n'
        'Note that this is not the same as the workspace number.\n'
        'If not provided, keeps the existing name.')
    parser.add_argument(
        '--number',
        type=int,
        help='New number to set for the workspace.\n'
        'Note that this is not the same as the workspace name.\n'
        'If not provided, keeps the existing number.')
    parser.add_argument(
        '--group',
        help='Group to assign to the focused workspace. If not provided, keeps '
        'the existing group assignment.')


def _create_args_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description='Control i3 workspace groups.')
    cli.add_common_args(parser)
    cli.add_workspace_naming_args(parser)
    subparsers = parser.add_subparsers(dest='command')
    subparsers.required = True
    list_groups_parser = subparsers.add_parser(
        'list-groups', help='List the groups of the current workspaces.')
    list_groups_parser.add_argument(
        '--focused-monitor-only',
        action='store_true',
        help='List only workspaces in the current monitor.')
    list_workspaces_parser = subparsers.add_parser(
        'list-workspaces', help='List workspaces and their group.')
    _add_list_workspaces_args(list_workspaces_parser)
    workspace_number_parser = subparsers.add_parser(
        'workspace-number',
        help='Focus on the workspace with the provided number in the focused '
        'group, similar to i3\'s "workspace number" command')
    workspace_number_parser.add_argument('workspace_relative_number', type=int)
    _add_group_args(workspace_number_parser)
    subparsers.add_parser(
        'workspace-next',
        help='Focus on the next workspace in the focused group, similar to '
        'i3\'s "workspace next" command')
    subparsers.add_parser(
        'workspace-prev',
        help='Focus on the prev workspace in the focused group, similar to '
        'i3\'s "workspace prev" command')
    move_to_number_parser = subparsers.add_parser(
        'move-to-number',
        help='Move the focused container to the workspace with the provided '
        'number in the focused group, similar to i3\'s "move container to '
        'workspace" command')
    move_to_number_parser.add_argument('workspace_relative_number', type=int)
    _add_group_args(move_to_number_parser)
    subparsers.add_parser(
        'move-to-next',
        help='Move the focused container to the next workspace in the focused '
        'group, similar to i3\'s "move container to workspace next" command')
    subparsers.add_parser(
        'move-to-prev',
        help='Move the focused container to the previous workspace in the '
        'focused group, similar to i3\'s "move container to workspace prev" '
        'command')
    switch_active_group_parser = subparsers.add_parser(
        'switch-active-group',
        help='Switches the active group to the one provided.')
    switch_active_group_parser.add_argument('group')
    switch_active_group_parser.add_argument('--focused-monitor-only',
                                            action='store_true')
    rename_workspace_parser = subparsers.add_parser(
        'rename-workspace',
        help='Rename and optionally change the group of the focused workspace')
    _add_rename_workspace_args(rename_workspace_parser)
    subparsers.add_parser(
        'assign-workspace-to-group',
        help='Assign the focused workspace to the provided group.'
    ).add_argument('group')
    # Deprecated commands, will be removed in a future release.
    subparsers.add_parser(
        'workspace-back-and-forth',
        help='[DEPRECATED] Focus on the last focused workspace, similar to '
        'i3\'s "workspace back_and_forth" command.')
    subparsers.add_parser(
        'move-to-back-and-forth',
        help='[DEPRECATED] Move the focused container to the last focused '
        'workspace, similar to i3\'s "move container to back_and_forth" command'
    )
    return parser


def _create_group_context(args):
    if args.group_name:
        return i3_groups_controller.NamedGroupContext(args.group_name)
    if args.group_active:
        return i3_groups_controller.ActiveGroupContext()
    if args.group_focused:
        return i3_groups_controller.FocusedGroupContext()
    return None


def _get_workspace_field(controller, workspace, field):
    if field == 'global_name':
        return workspace.name
    if field == 'monitor':
        con = workspace
        while con.type != 'output':
            con = con.parent
        return con.name
    if field == 'window_icons':
        return controller.icons_resolver.get_workspace_icons(workspace)
    parsed_name = workspace_names.parse_name(workspace.name)
    value = getattr(parsed_name, field)
    if value is None:
        return ''
    return value


def _print_workspaces(controller, args):
    fields = args.fields.split(',')
    for field in fields:
        if field not in _LIST_WORKSPACES_FIELDS:
            sys.exit('Invalid field: "{}". Valid fields: {}'.format(
                field, _LIST_WORKSPACES_FIELDS))
    table = []
    for workspace in controller.list_workspaces(_create_group_context(args),
                                                args.focused_only,
                                                args.focused_monitor_only):
        row = []
        for field in fields:
            row.append(_get_workspace_field(controller, workspace, field))
        table.append(row)
    print('\n'.join('\t'.join(str(e) for e in row) for row in table))


# pylint: disable=too-many-branches
def main():
    args = _create_args_parser().parse_args()
    init_logger(os.path.basename(__file__))
    logger.setLevel(getattr(logging, args.log_level.upper(), None))

    config = cli.get_config_with_overrides(args)
    logger.debug('Using merged config:\n%s', pprint.pformat(config))
    i3_connection = i3ipc.Connection()
    controller = i3_groups_controller.WorkspaceGroupsController(
        i3_proxy.I3Proxy(i3_connection, args.dry_run), config)
    try:
        if args.command == 'list-groups':
            print('\n'.join(controller.list_groups(args.focused_monitor_only)))
        elif args.command == 'list-workspaces':
            _print_workspaces(controller, args)
        elif args.command == 'workspace-number':
            controller.focus_workspace_number(_create_group_context(args),
                                              args.workspace_relative_number)
        elif args.command == 'workspace-next':
            controller.focus_workspace_relative(+1)
        elif args.command == 'workspace-prev':
            controller.focus_workspace_relative(-1)
        elif args.command == 'move-to-number':
            controller.move_to_workspace_number(_create_group_context(args),
                                                args.workspace_relative_number)
        elif args.command == 'move-to-next':
            controller.move_workspace_relative(+1)
        elif args.command == 'move-to-prev':
            controller.move_workspace_relative(-1)
        elif args.command == 'switch-active-group':
            controller.switch_active_group(args.group,
                                           args.focused_monitor_only)
        elif args.command == 'rename-workspace':
            metadata_updates = workspace_names.WorkspaceGroupingMetadata(
                group=args.group,
                static_name=args.name,
                local_number=args.number)
            controller.update_focused_workspace(metadata_updates)
        elif args.command == 'assign-workspace-to-group':
            metadata_updates = workspace_names.WorkspaceGroupingMetadata(
                group=args.group)
            controller.update_focused_workspace(metadata_updates)
        # Deprecated commands, will be removed in a future release.
        elif args.command == 'workspace-back-and-forth':
            logger.warning(
                'workspace-back-and-forth is deprecated, please '
                'migrate to the native i3 "workspace back_and_forth" command')
            controller.i3_proxy.send_i3_command('workspace back_and_forth')
        elif args.command == 'move-to-back-and-forth':
            logger.warning(
                'move-to-back-and-forth is deprecated, please '
                'migrate to the native i3 "workspace back_and_forth" command')
            controller.i3_proxy.send_i3_command('move workspace back_and_forth')
    except i3_groups_controller.WorkspaceGroupsError as ex:
        sys.exit(ex)


if __name__ == '__main__':
    main()
