#!/usr/bin/env python
"""
Tool to build partial debian mirrors (and more)
"""

# debpartial-mirror - partial debian mirror package tool
# (c) 2004 Otavio Salvador <otavio@debian.org>
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# -------
# Imports
# -------
import getopt
import signal
import sys

from debpartial_mirror import Backend
from debpartial_mirror import Config
from debpartial_mirror import Download

# ---------
# Variables
# ---------

# Defaults
version_str = '0.2.95'
date_str = 'Wed,  8 Nov 2006 19:39:01 -0200'

conffile  = "/etc/debpartial-mirror.conf"

# User command descriptions
cmnds_desc = { 
  'all':     'Update, upgrade, merge the selected mirror(s)',
  'update':  'Update selected mirror(s) (download Package and Source lists)',
  'upgrade': 'Upgrade selected mirror(s) (download binary and source packages)',
  'merge': 'Merge selected mirror(s)',
  'clean': 'Clean selected mirror(s)',
}

# Sorted list of user commands
cmnds_list = cmnds_desc.keys()
cmnds_list.sort()

# ---------
# Functions
# ---------

def version():
  """Print package version"""
  print """debpartial-mirror %s - Partial mirroring tool for Debian - %s
This program is free software and was released under the terms of the GNU General Public License
""" % (version_str, date_str)

def usage(ret=2):
  """Print program usage message"""
  global conffile
  global cmnds_desc
  global cmnds_list
  
  cmnds_string = ""
  for c in cmnds_list:
    cmnds_string += "  %-27s %s\n" % (c, cmnds_desc[c])

  print """Usage: debpartial-mirror [OPTIONS] COMMAND [MIRROR]

Where OPTIONS is one of:
  -h, --help                 Display this help end exit
  -c, --configfile=FILE      Select a config file (currently '%s')
  -v, --version              Show program version

COMMAND is one of:
%s
And MIRROR selects which mirror we should work with (all by default).
""" % (conffile, cmnds_string)
  sys.exit(ret)

def sigint_handler(signum, frame):
  d = Download.Download()
  if len (d.d_fetchers_list) > 0:
    d.wait_all()
  print "\n\rInterrupting download due a user request ..."
  sys.exit(1)

def update():
  global mirrors
  for b in mirrors:
    if b.has_key('lock') and b['lock']:
      print "Skipping backend", b._name
    else:
      print "Updating backend", b._name
      b.update()

        
def load():
  global mirrors
  for b in mirrors:
    print "Loading backend", b._name
    b.load()
  return True
    
def process():
  global mirrors
  for b in mirrors:
    print "Processing backend", b._name
    b.process()
    
def upgrade():
  for b in mirrors:
    if b.has_key('lock') and b['lock']:
      print "Skipping backend", b._name
    else:
      print "Upgrading backend", b._name
      b.upgrade()

def clean():
  for b in mirrors:
    if b.has_key('lock') and b['lock']:
      print "Skipping backend", b._name
    else:
      print "Clean backend", b._name
      b.clean()

def merge():
  global merges
  for b in merges:
    print "Merging backend", b._name
    b.merge()

def main():
  """Main program"""
  global conffile
  global cmnds_list
  global mirrors
  global merges
  
  cmnd = None
  sect = None

  # Parse options
  try:
    opts, args = getopt.getopt(sys.argv[1:], 'hvc:', 
                               ["help", "version", "configfile="])
  except getopt.GetoptError:
    print "ERROR reading program options\n"
    usage()

  for o, v in opts:
    if o in ("-h", "--help"):
      usage()
    if o in ("-v", "--version"):
      version()
      sys.exit(0)
    if o in ("-c", "--configfile"):
      if v == '':
        usage()
      conffile = v

  if len(args) > 0:
    cmnd = args[0]
    if cmnd not in cmnds_list:
      print "ERROR: Unknown command '%s'" % cmnd
      usage()
    if len(args) > 1:
      sect = []
      for i in range(1, len(args)):
        sect.append(args[i])
      
  # Load configuration file
  try:
    cnf = Config.Config(conffile)
  except Config.InvalidOption, msg:
    print("Wrong option [%s] found on [%s] section of '%s'."
          % (msg.option, msg.section, conffile))
    sys.exit(1)
  except Config.InvalidSection, msg:
    print("Wrong section [%s] found on '%s'."
          % (msg.section, conffile))
    sys.exit(1)
  except Config.RequiredOptionMissing, msg:
    print("Required option [%s] was missing on [%s] section of '%s'."
          % (msg.option, msg.section, conffile))
    sys.exit(1)

  # Verify if the section is valid
  if sect:
    for s in sect.split():
      if s != None and not cnf.has_section(s):
        print("Unknown MIRROR [%s] on '%s'."
              % (s, conffile))
        sys.exit(1)

  # Get available backends
  mirrors = []
  merges = []
  (cnf_mirrors, cnf_merges) = cnf.get_backends()

  for b in cnf_mirrors:
    if sect == None or sect.__contains__(b.section):
      mirrors.append(Backend.MirrorBackend(b.section, cnf))

  for b in cnf_merges:
    if sect == None or sect.__contains__(b.section):
      merges.append(Backend.MergeBackend(b.section, cnf))

  if cmnd == 'all':
    update()
    if load():
      process()
      upgrade()
      merge()
      clean()
  elif cmnd == 'update':
    update()
  elif cmnd == 'upgrade':
    if load():
      process()
      upgrade()
  elif cmnd == 'merge':
    if load():
      process()
      merge()
  elif cmnd == 'clean':
    if load():
      clean()
  else:
    usage()

# ------------
# Main Program
# ------------
if __name__ == '__main__':
  signal.signal(signal.SIGINT, sigint_handler)
  main()
