#!/usr/bin/env python3
# -*- coding: utf8 -*-

# Copyright (C) 2012-2016 Xyne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# (version 2) as published by the Free Software Foundation.
#
#
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.



################################################################################
# Temporary code. A patch has been submitted upstream to pyalpm
# Update: never got a reply after 2? years. Looks like it's here to stay.
################################################################################

from collections import OrderedDict
from pycman.config import PacmanConfEnumeratorSession, _logmask, cb_log, LIST_OPTIONS, BOOLEAN_OPTIONS
import os
import pyalpm


class PacmanConfig(OrderedDict):
  def __init__(self, conf = None, options = None):
    super(self.__class__, self).__init__()
    self['options'] = OrderedDict()
    self.options = self['options']
    self.repos = OrderedDict()
    self.options["RootDir"] = "/"
    self.options["DBPath"]  = "/var/lib/pacman"
    self.options["GPGDir"]  = "/etc/pacman.d/gnupg/"
    self.options["LogFile"] = "/var/log/pacman.log"
    self.options["IgnorePkg"] = list()
    self.options["IgnoreGroup"] = list()
    self.options["SigLevel"] = 'Optional'
    self.options["Architecture"] = os.uname()[-1]
    self.options["Color"] = 'never'
    if conf is not None:
      self.load_from_file(conf)
    if options is not None:
      self.load_from_options(options)

  def load_from_file(self, filename):
    with PacmanConfEnumeratorSession(filename) as pacman_conf_enumerator:
      for section, key, value in pacman_conf_enumerator():
        if key == 'Architecture' and value == 'auto':
          continue
        elif key == 'Color':
          self[section][key] = 'auto'
          continue
        self.setdefault(section, OrderedDict())
        if key in LIST_OPTIONS:
          self[section].setdefault(key, []).append(value)
        else:
          self[section][key] = value
      if "CacheDir" not in self.options:
        self.options["CacheDir"]= ["/var/cache/pacman/pkg"]
      for key, value in self.items():
        # For backwards compabilitility
        if key != 'options':
          self.repos[key] = self[key]['Server']

          if 'LocalFileSigLevel' not in value:
            value['LocalFileSigLevel'] = self.options['SigLevel']
          if 'RemoteFileSigLevel' not in value:
            value['RemoteFileSigLevel'] = self.options['SigLevel']

        if 'SigLevel' not in value:
          value['SigLevel'] = self.options['SigLevel']

  def load_from_options(self, options):
    global _logmask
    if options.root is not None:
      self.options["RootDir"] = options.root
    if options.dbpath is not None:
      self.options["DBPath"] = options.dbpath
    if options.gpgdir is not None:
      self.options["GPGDir"] = options.gpgdir
    if options.arch is not None:
      self.options["Architecture"] = options.arch
    if options.logfile is not None:
      self.options["LogFile"] = options.logfile
    if options.cachedir is not None:
      self.options["CacheDir"] = [options.cachedir]
    if options.debug:
      _logmask = 0xffff

  def apply(self, h):
    h.arch = self.options["Architecture"]
    h.logfile = self.options["LogFile"]
    h.gpgdir = self.options["GPGDir"]
    h.cachedirs = self.options["CacheDir"]
    h.logcb = cb_log

    # set sync databases
    for repo, servers in self.repos.items():
      db = h.register_syncdb(repo, 0)
      db_servers = []
      for rawurl in servers:
        url = rawurl.replace("$repo", repo)
        url = url.replace("$arch", self.options["Architecture"])
        db_servers.append(url)
      db.servers = db_servers

  def initialize_alpm(self):
    h = pyalpm.Handle(self.options["RootDir"], self.options["DBPath"])
    self.apply(h)
    return h

  def __str__(self):
    conf = ''
    for section, options in self.items():
      conf += '[%s]\n' % section
      for key, value in options.items():
        if key in LIST_OPTIONS:
          for v in value:
            conf += '%s = %s\n' % (key, v)
        elif key in BOOLEAN_OPTIONS:
          conf += key + '\n'
        else:
          conf += '%s = %s\n' % (key, value)
      conf += '\n'
    return conf



# TODO
# Use readline, curses or dialog to improve this.
def select_grp(grpname, pkgs, ignored=None):
  '''
  Present the user with a dialogue to select packages from a group.
  '''
  if not pkgs.pkgs:
    return pkgs
  selected = dict()
  for p in pkgs.pkgs:
    if ignored and p in ignored:
      selected[p] = False
    else:
      selected[p] = True
  names = sorted(selected)
  n = len(names)
  fmt = '[{}] {:' + str(len(str(n+1))) + 'd} {}'
  while True:
    print('Select packages from group "{}"'.format(grpname))
    for i in range(n):
      name = names[i]
      if selected[name]:
        marker = '*'
      else:
        marker = ' '
      print(fmt.format(marker, i+1, name))
    changes = input('Use +n to select and -n to deselect, where n is the package number.\nEnter nothing or "x" to finalize selection.\nMultiple entries are permitted.\nModify current selection: ')
    finalized = not changes
    for number in changes.split():
      if number == 'x':
        finalized = True
        continue
      try:
        number = int(number)
        index = abs(number) - 1
        if index >= n:
          raise ValueError
      except ValueError:
        logging.error('invalid number [{:d}]'.format(number))
        finalized = False
        break
      selected[names[index]] = (number > 0)
    print()
    if finalized:
      break

  for k, v in selected.items():
    if not v:
      del pkgs.pkgs[k]
  return pkgs



def needs_sig(siglevel, insistence, prefix):
  '''
  Determine if a signature should be downloaded. The siglevel is the pacman.conf
  SigLevel for the given repo. The insistence is an integer. Anything below 1
  will return false, anything above 1 will return true, and 1 will check if the
  siglevel is required or optional. The prefix is either "Database" or
  "Package".
  '''
  if insistence > 1:
    return True
  elif insistence == 1 and siglevel:
    sl = 'Never'
    if siglevel == sl or siglevel == prefix + sl:
      return False
    else:
      for sl in ('Required', 'Optional'):
        if siglevel == sl or siglevel == prefix + sl:
          return True
  return False



def split_commas(words):
  '''
  Split multi-argument fields from Pacman configuration file.
  '''
  for word in words:
    for w in word.split(','):
      if w:
        yield w
