#!/usr/bin/python

# Copyright (C) 2010,2011 Javier Palacios
# 
# 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.


__version__ = "2.0"

__usage__ = """%prog reponame [reponame ...]"""


import sys

import repolib


import optparse

def option_parser () :
    version_string = "%%prog %s" % __version__
    parser = optparse.OptionParser( usage=__usage__ , version=version_string )

    parser.add_option( "--quiet" , action="store_true" ,
        help="Issue only critical messages" )
    parser.add_option( "--verbose" , action="store_true" ,
        help="Run in descriptive mode" )

    parser.add_option( "--component" , metavar="COMPNAME" ,
        help="Select component to operate on" )

    modes = optparse.OptionGroup( parser , "Operation modes" ,
        "Operation modes available for mirroring" )
    modes.add_option( "--init" , action="count" , default=0 ,
        help="complete repository download" )
    modes.add_option( "--update" , action="count" , default=0 ,
        help="download modified metadata and new packages" )
    modes.add_option( "--metadata" , action="count" , default=0 ,
        help="download repository metadata [default]" )
    modes.add_option( "--check" , action="count" , default=0 ,
        help="check repository status" )

    parser.add_option_group( modes )

    return parser


def main () :

    parser = option_parser()
    opts , args = parser.parse_args()

    if not args :
        parser.print_help()
        sys.exit(1)

#    if opts.check and not opts.component :
#        parser.error( "Check mode requires supplying one component" )

    if opts.component and len(args)>1 :
        parser.error( "Supply a single repository for component based operation" )

    if opts.init + opts.update + opts.metadata + opts.check > 1 :
        parser.error( "Only one operation mode can be specified at a time" )

    if opts.verbose and opts.quiet :
        repolib.logger.warning( "Specified verbose and quiet simultaneously, switching to quiet" )

    repolist = []

    while args :

      repo_name = args.pop()

      try :
        repo = repolib.MirrorRepository.new( repo_name )
        if opts.component :
            repo.select_component( opts.component )
        if opts.init :
            repo.set_mode( "init" )
        elif opts.update :
            repo.set_mode( "update" )
        elif opts.check :
            repo.set_mode( "keep" )
        else :
            repo.set_mode( "metadata" )
      except Exception , ex :
        repolib.logger.error( "Repo %s : %s" % ( repo_name , ex ) )
        continue

      repolist.append( repo )

    if opts.check :
        repolib.logger.setLevel( repolib.logging.CRITICAL )

    if opts.verbose :
        repolib.logger.setLevel( repolib.logging.INFO )
    if opts.quiet :
        if not opts.check :
            repolib.logger.setLevel( repolib.logging.ERROR )

    return repolist


def do_1st ( repo ) :

  meta_files = repo.get_metafile()

  if meta_files.values().count( False ) == len(meta_files) :
    # FIXME : why not clean temporaries from meta_files
    return False

  # After verify all the mirroring parameters, it is safe to create directory tree
  repo.build_local_tree()
  
  # Once created, we move in the primary metadata file
  local_repodata = repo.write_master_file( meta_files )
  
  repo.info( local_repodata , repolib.logger.info )

  return local_repodata


def do_2nd ( repo , local_repodata ) :

  download_pkgs = repo.get_download_list()
  download_pkgs.start()
  missing_pkgs = {}

  for name,subrepo in repo.subrepos.iteritems() :

    packages = subrepo.get_metafile( local_repodata )

    if subrepo.mode == "keep" :
        if not isinstance(packages,bool) :
            repolib.logger.error( "Returned non-boolean '%s' while in check mode" % (packages,) )
        if packages :
            print "%s [ %s ] - subrepo is up to date" % ( repo , subrepo )
        else :
            print "%s [ %s ] - outdated subrepo" % ( repo , subrepo )

    if isinstance(packages,bool) or repo.mode == "metadata" :
        continue

    repolib.logger.info( "Scanning %s" % name )

    download , missing = subrepo.get_package_list( packages , {} , repo.filters )
    download_pkgs.extend( download )
    missing_pkgs.update( dict.fromkeys( missing ) )

  size = download_pkgs.weight / 1024 / 1024
  if size > 2048 :
    repolib.logger.info( "Total size to download : %.1f Gb" % ( size / 1024 ) )
  else :
    repolib.logger.info( "Total size to download : %.1f Mb" % size )

  return download_pkgs , missing_pkgs


if __name__ == "__main__" :

  repolist = main()
  downloaders = []
  for repo in repolist :
    local_repodata = do_1st( repo )
    if not local_repodata :
        repolib.logger.critical( "No valid metadata files found for %s" % repo )
        continue
    download_pkgs , missing_pkgs = do_2nd( repo , local_repodata )
    downloaders.append( download_pkgs )
    if not download_pkgs :
        repolib.logger.info( "Nothing to download for %s" % repo )
    if missing_pkgs :
        repolib.logger.warning( "There are %d missing requirements : %s " % ( len(missing_pkgs) , missing_pkgs[:5] ) )
    download_pkgs.finish()

  for downloader in downloaders :
    downloader.join()

