import os
from collections import OrderedDict, namedtuple
from itertools import chain
from gooey.gui.lang.i18n import _
from gooey.gui.util.quoting import quote

import wx

ArgumentGroup = namedtuple('ArgumentGroup', 'name command required_args optional_args')


class MyWidget(object):
  # TODO: Undumbify damn
  # TODO: Undumbify _value/value access

  def __init__(self, type, title, help, default, nargs, commands, choices):
    self.type = type
    self.title = title
    self.help = help
    self.default = default
    self._value = default
    self.nargs = nargs
    self.commands = commands
    self.choices = choices

  @property
  def value(self):
    # TODO: split into stategy or subclass thingie
    if self.type == 'CheckBox':
      return self.commands[0] if self._value else None
    if self.type == 'RadioGroup':
      try:
        return self.commands[self._value.index(True)][0]
      except ValueError:
        return None
    if self.type == 'MultiFileChooser':
      value = ' '.join(quote(x) for x in self._value.split(os.pathsep) if x)
      if self.commands and value:
        return u'{} {}'.format(self.commands[0], value)
      return value or None
    if self.type == 'Textarea':
      if self.commands and self._value:
        return '{} {}'.format(self.commands[0], quote(self._value.encode('unicode_escape')))
      else:
        return quote(self._value.encode('unicode_escape')) if self._value else ''
    if self.type == 'CommandField':
      if self.commands and self._value:
        return u'{} {}'.format(self.commands[0], self._value)
      else:
        return self._value or None

    if self.type == 'Counter':
      '''
      Returns
        str(option_string * DropDown Value)
        e.g.
        -vvvvv
      '''
      if not str(self._value).isdigit():
        return None
      arg = str(self.commands[0]).replace('-', '')
      repeated_args = arg * int(self._value)
      return '-' + repeated_args
    if self.type == 'Listbox':
      if self.commands and self._value:
        return u'{} {}'.format(self.commands[0], ' '.join(map(quote, self._value)))
      else:
        return ' '.join(map(quote, self._value)) if self._value else ''
    if self.type == 'Dropdown':
      if self._value == 'Select Option':
        return None
      elif self.commands and self._value:
        return u'{} {}'.format(self.commands[0], quote(self._value))
      else:
        return quote(self._value) if self._value else ''
    else:
      if self.commands and self._value:
        if not self.nargs:
          v = quote(self._value)
        else:
          v = self._value
        return u'{0} {1}'.format(self.commands[0], v)
      else:
        if not self._value:
          return None
        elif not self.nargs:
          return quote(self._value)
        else:
          return self._value

  @value.setter
  def value(self, val):
    self._value = val

  @classmethod
  def from_dict(cls, data):
    def maybe_unpack(collection, attr):
      # TODO: RadioGroups need to support defaults
      try:
        if isinstance(collection, list):
          return [item[attr] for item in collection]
        return collection[attr]
      except:
        return None

    details = data['data']
    return cls(
      data['type'],
      maybe_unpack(details, 'display_name'),
      maybe_unpack(details, 'help'),
      maybe_unpack(details, 'default'),
      maybe_unpack(details, 'nargs'),
      maybe_unpack(details, 'commands'),
      maybe_unpack(details, 'choices')
    )




class States(object):
  CONFIGURING = 'configuring'
  RUNNNING = 'running'
  SUCCESS = 'success'
  ERROR = 'error'
  STOPPED = 'stopped'



class MyModel(object):
  '''
  '''

  def wrap(self, groups):
    output = OrderedDict()
    for name, group in groups.items():
      output[name] = ArgumentGroup(
        name,
        group['command'],
        *self.group_arguments(group['contents'])
      )
    return output


  def __init__(self, build_spec):

    self.current_state = States.CONFIGURING

    self.build_spec = build_spec
    self.layout_type = self.build_spec.get('layout_type')

    self.auto_start = self.build_spec.get('auto_start')
    self.progress_regex = self.build_spec.get('progress_regex')
    self.progress_expr = self.build_spec.get('progress_expr')
    self.disable_progress_bar_animation = self.build_spec['disable_progress_bar_animation']

    self.program_name = self.build_spec.get('program_name')
    self.default_size = self.build_spec.get('default_size')

    self.heading_title = _("settings_title")
    self.heading_subtitle = self.build_spec['program_description'] or ''

    self.use_monospace_font = self.build_spec.get('monospace_display')
    self.stop_button_disabled = self.build_spec['disable_stop_button']

    self.argument_groups = self.wrap(self.build_spec.get('widgets', {}))
    self.active_group = next(iter(self.argument_groups))

    self.num_required_cols = self.build_spec['num_required_cols']
    self.num_optional_cols = self.build_spec['num_optional_cols']

    self.text_states = {
      States.CONFIGURING: {
        'title': _("settings_title"),
        'subtitle': self.build_spec['program_description'] or ''
      },
      States.RUNNNING: {
        'title': _("running_title"),
        'subtitle': _('running_msg')
      },
      States.SUCCESS: {
        'title': _('finished_title'),
        'subtitle': _('finished_msg')
      },
      States.ERROR: {
        'title': _('finished_title'),
        'subtitle': _('finished_error')
      }
    }

  @property
  def required_args(self):
    return self.argument_groups[self.active_group].required_args

  @property
  def optional_args(self):
    return self.argument_groups[self.active_group].optional_args

  def update_state(self, state):
    self.current_state = state

    text = self.text_states[state]
    self.heading_title = text['title']
    self.heading_subtitle = text['subtitle']

  def is_valid(self):
    # TODO: fix skipping_config.. whatever that did
    # currently breaks when you supply it as a decorator option
    # return self.skipping_config() and self.required_section_complete()
    return self.is_required_section_complete()

  def skipping_config(self):
    return self.build_spec['manual_start']

  def is_required_section_complete(self):
    error_found = False
    for arg in self.required_args:
      widget = arg.widget_instance.widget_pack.widget
      if arg.value:
        widget.SetBackgroundColour(wx.NullColour)
      else:
        if not error_found:
          error_found = True
          widget.SetFocus()
        widget.SetBackgroundColour("#EF9A9A")

      widget.Refresh()

    return not error_found

  def build_command_line_string(self):
    optional_args = [arg.value for arg in self.optional_args]
    required_args = [c.value for c in self.required_args if c.commands]
    position_args = [c.value for c in self.required_args if not c.commands]
    if position_args:
      position_args.insert(0, "--")
    cmd_string = ' '.join(list(filter(None, chain(required_args, optional_args, position_args))))
    if self.layout_type == 'column':
      cmd_string = u'{} {}'.format(self.argument_groups[self.active_group].command, cmd_string)
    return u'{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_string)

  def group_arguments(self, widget_list):
    is_required = lambda widget: widget['required']
    not_checkbox = lambda widget: widget['type'] != 'CheckBox'

    required_args, optional_args  = self.partition(widget_list, is_required)
    if self.build_spec['group_by_type']:
      optional_args = chain(*self.partition(optional_args, not_checkbox))
    return list(map(self.to_object, required_args)), list(map(self.to_object, optional_args))

  @staticmethod
  def partition(collection, condition):
    return list(filter(condition, collection)), list(filter(lambda x: not condition(x), collection))

  def to_object(self, data):
    details = data['data']
    return MyWidget(
      data['type'],
      self.maybe_unpack(details, 'display_name'),
      self.maybe_unpack(details, 'help'),
      self.maybe_unpack(details, 'default'),
      self.maybe_unpack(details, 'nargs'),
      self.maybe_unpack(details, 'commands'),
      self.maybe_unpack(details, 'choices')
    )

  @staticmethod
  def maybe_unpack(collection, attr):
    # TODO: RadioGroups need to support defaults
    try:
      if isinstance(collection, list):
        return [item[attr] for item in collection]
      return collection[attr]
    except:
      return None




import os
from collections import OrderedDict, namedtuple
from itertools import chain
from gooey.gui.lang.i18n import _
from gooey.gui.util.quoting import quote

import wx

ArgumentGroup = namedtuple('ArgumentGroup', 'name command required_args optional_args')


class MyWidget(object):
  # TODO: Undumbify damn
  # TODO: Undumbify _value/value access

  def __init__(self, type, title, help, default, nargs, commands, choices):
    self.type = type
    self.title = title
    self.help = help
    self.default = default
    self._value = default
    self.nargs = nargs
    self.commands = commands
    self.choices = choices

  @property
  def value(self):
    # TODO: split into stategy or subclass thingie
    if self.type == 'CheckBox':
      return self.commands[0] if self._value else None
    if self.type == 'RadioGroup':
      try:
        return self.commands[self._value.index(True)][0]
      except ValueError:
        return None
    if self.type == 'MultiFileChooser':
      value = ' '.join(quote(x) for x in self._value.split(os.pathsep) if x)
      if self.commands and value:
        return u'{} {}'.format(self.commands[0], value)
      return value or None
    if self.type == 'Textarea':
      if self.commands and self._value:
        return '{} {}'.format(self.commands[0], quote(self._value.encode('unicode_escape')))
      else:
        return quote(self._value.encode('unicode_escape')) if self._value else ''
    if self.type == 'CommandField':
      if self.commands and self._value:
        return u'{} {}'.format(self.commands[0], self._value)
      else:
        return self._value or None

    if self.type == 'Counter':
      '''
      Returns
        str(option_string * DropDown Value)
        e.g.
        -vvvvv
      '''
      if not str(self._value).isdigit():
        return None
      arg = str(self.commands[0]).replace('-', '')
      repeated_args = arg * int(self._value)
      return '-' + repeated_args
    if self.type == 'Listbox':
      if self.commands and self._value:
        return u'{} {}'.format(self.commands[0], ' '.join(map(quote, self._value)))
      else:
        return ' '.join(map(quote, self._value)) if self._value else ''
    if self.type == 'Dropdown':
      if self._value == 'Select Option':
        return None
      elif self.commands and self._value:
        return u'{} {}'.format(self.commands[0], quote(self._value))
      else:
        return quote(self._value) if self._value else ''
    else:
      if self.commands and self._value:
        if not self.nargs:
          v = quote(self._value)
        else:
          v = self._value
        return u'{0} {1}'.format(self.commands[0], v)
      else:
        if not self._value:
          return None
        elif not self.nargs:
          return quote(self._value)
        else:
          return self._value

  @value.setter
  def value(self, val):
    self._value = val

  @classmethod
  def from_dict(cls, data):
    def maybe_unpack(collection, attr):
      # TODO: RadioGroups need to support defaults
      try:
        if isinstance(collection, list):
          return [item[attr] for item in collection]
        return collection[attr]
      except:
        return None

    details = data['data']
    return cls(
      data['type'],
      maybe_unpack(details, 'display_name'),
      maybe_unpack(details, 'help'),
      maybe_unpack(details, 'default'),
      maybe_unpack(details, 'nargs'),
      maybe_unpack(details, 'commands'),
      maybe_unpack(details, 'choices')
    )




class States(object):
  CONFIGURING = 'configuring'
  RUNNNING = 'running'
  SUCCESS = 'success'
  ERROR = 'error'
  STOPPED = 'stopped'



class MyModel(object):
  '''
  '''

  def wrap(self, groups):
    output = OrderedDict()
    for name, group in groups.items():
      output[name] = ArgumentGroup(
        name,
        group['command'],
        *self.group_arguments(group['contents'])
      )
    return output


  def __init__(self, build_spec):

    self.current_state = States.CONFIGURING

    self.build_spec = build_spec
    self.layout_type = self.build_spec.get('layout_type')

    self.auto_start = self.build_spec.get('auto_start')
    self.progress_regex = self.build_spec.get('progress_regex')
    self.progress_expr = self.build_spec.get('progress_expr')
    self.disable_progress_bar_animation = self.build_spec['disable_progress_bar_animation']

    self.program_name = self.build_spec.get('program_name')
    self.default_size = self.build_spec.get('default_size')

    self.heading_title = _("settings_title")
    self.heading_subtitle = self.build_spec['program_description'] or ''

    self.use_monospace_font = self.build_spec.get('monospace_display')
    self.stop_button_disabled = self.build_spec['disable_stop_button']

    self.argument_groups = self.wrap(self.build_spec.get('widgets', {}))
    self.active_group = next(iter(self.argument_groups))

    self.num_required_cols = self.build_spec['num_required_cols']
    self.num_optional_cols = self.build_spec['num_optional_cols']

    self.text_states = {
      States.CONFIGURING: {
        'title': _("settings_title"),
        'subtitle': self.build_spec['program_description'] or ''
      },
      States.RUNNNING: {
        'title': _("running_title"),
        'subtitle': _('running_msg')
      },
      States.SUCCESS: {
        'title': _('finished_title'),
        'subtitle': _('finished_msg')
      },
      States.ERROR: {
        'title': _('finished_title'),
        'subtitle': _('finished_error')
      }
    }

  @property
  def required_args(self):
    return self.argument_groups[self.active_group].required_args

  @property
  def optional_args(self):
    return self.argument_groups[self.active_group].optional_args

  def update_state(self, state):
    self.current_state = state

    text = self.text_states[state]
    self.heading_title = text['title']
    self.heading_subtitle = text['subtitle']

  def is_valid(self):
    # TODO: fix skipping_config.. whatever that did
    # currently breaks when you supply it as a decorator option
    # return self.skipping_config() and self.required_section_complete()
    return self.is_required_section_complete()

  def skipping_config(self):
    return self.build_spec['manual_start']

  def is_required_section_complete(self):
    error_found = False
    for arg in self.required_args:
      widget = arg.widget_instance.widget_pack.widget
      if arg.value:
        widget.SetBackgroundColour(wx.NullColour)
      else:
        if not error_found:
          error_found = True
          widget.SetFocus()
        widget.SetBackgroundColour("#EF9A9A")

      widget.Refresh()

    return not error_found

  def build_command_line_string(self):
    optional_args = [arg.value for arg in self.optional_args]
    required_args = [c.value for c in self.required_args if c.commands]
    position_args = [c.value for c in self.required_args if not c.commands]
    if position_args:
      position_args.insert(0, "--")
    cmd_string = ' '.join(list(filter(None, chain(required_args, optional_args, position_args))))
    if self.layout_type == 'column':
      cmd_string = u'{} {}'.format(self.argument_groups[self.active_group].command, cmd_string)
    return u'{} --ignore-gooey {}'.format(self.build_spec['target'], cmd_string)

  def group_arguments(self, widget_list):
    is_required = lambda widget: widget['required']
    not_checkbox = lambda widget: widget['type'] != 'CheckBox'

    required_args, optional_args  = self.partition(widget_list, is_required)
    if self.build_spec['group_by_type']:
      optional_args = chain(*self.partition(optional_args, not_checkbox))
    return list(map(self.to_object, required_args)), list(map(self.to_object, optional_args))

  @staticmethod
  def partition(collection, condition):
    return list(filter(condition, collection)), list(filter(lambda x: not condition(x), collection))

  def to_object(self, data):
    details = data['data']
    return MyWidget(
      data['type'],
      self.maybe_unpack(details, 'display_name'),
      self.maybe_unpack(details, 'help'),
      self.maybe_unpack(details, 'default'),
      self.maybe_unpack(details, 'nargs'),
      self.maybe_unpack(details, 'commands'),
      self.maybe_unpack(details, 'choices')
    )

  @staticmethod
  def maybe_unpack(collection, attr):
    # TODO: RadioGroups need to support defaults
    try:
      if isinstance(collection, list):
        return [item[attr] for item in collection]
      return collection[attr]
    except:
      return None




# TODO: Undumbify damn # TODO: Undumbify _value/value access # TODO: split into stategy or subclass thingie Returns
         str(option_string * DropDown Value)
         e.g.
         -vvvvv # TODO: RadioGroups need to support defaults # TODO: fix skipping_config.. whatever that did # currently breaks when you supply it as a decorator option # return self.skipping_config() and self.required_section_complete() # TODO: RadioGroups need to support defaults