#!/usr/bin/python
import sys
import os
import datetime, time
import xml.etree.ElementTree as ElementTree
import pprint
import re
import shutil

from optparse import OptionParser

sub_pkg_edges={}
pkg_id={}
main_sub_pkg={}
pkg_print_index={}
sub_main_pkg={}
dep_edges=set()
sorted_packages=[]
main_pkg_level={}
reduced_edges={}
reduced_edges_reverse={}

git_obs_mapping={}

obs_api_url=""
obs_project=""
obs_repo=""
obs_arch=""
obs_username=""
obs_pw=""

mysql_ip=""
mysql_username=""
mysql_pw=""
mysql_db=""
mysql_build_target_id=""
get_build_status_from=""
#-------------------------------------------------------------------------------
def MakeGitOBSMapping():
  if len(git_obs_mapping) > 0:
    return

  git_obs_mapping_file="git-obs-mapping/profiles/tizen_mobile.xml"
  if not os.path.isfile(git_obs_mapping_file):
    print "If you want use --git-packages we need :",git_obs_mapping_file
    exit(0)
  tree = ElementTree.parse(git_obs_mapping_file)
  root = tree.getroot()
  for proj in root.iter('project'):
    git_name=proj.get('name')
    obs_name=proj.get('OBS_package')
    if obs_name is not None:
      git_obs_mapping[git_name]=obs_name

#-------------------------------------------------------------------------------
def TranslateGitOBSMapping(git_packages):
  MakeGitOBSMapping()
  obs_packages=[]
  for p in git_packages:
    if p not in git_obs_mapping:
      print p,": No such git project."
      exit(0)
    obs_packages.append(git_obs_mapping[p])
  return obs_packages

#-------------------------------------------------------------------------------
def GetBuiltPackageList(filename):
  package_list=[]
  f = open(filename, 'r')
  for line in f:
    package_list.append(line.split("\n")[0])
  f.close()

  return package_list

#-------------------------------------------------------------------------------
def MergeSets(set_list):
  list_len = len(set_list)
  changed=1
  while(changed):
    changed=0
    i = 0
    while ( i < list_len ):
      j=i+1
      while ( j < list_len ):
        if len(set_list[i] & set_list[j]) > 0:
          changed=1
          set_list[i] = set_list[i] | set_list[j]
          set_list.remove(set_list[j])
          list_len = len(set_list)
        j=j+1
      i=i+1

  return set_list

#-------------------------------------------------------------------------------
def FindPath(src_pkg, dst_pkg, edges_local):
  visited=set()
  path=set()

  def visit(pkg):
    if pkg in visited:
      return False

    if pkg == dst_pkg:
      return True

    visited.add(pkg)
    path.add(pkg)
    if pkg in edges_local:
      for d_pkg in edges_local[pkg]:
        visit(d_pkg)
    path.remove(pkg)

    return False

  return visit(src_pkg)
  

#-------------------------------------------------------------------------------
def MakeEdges(nodes, sorted_packages, dep_packages, cycle_edges, reduced_edges):
  edges=set()

  level=0
  while( level < len(sorted_packages)-1):
    for src_pkg in sorted_packages[level]:
      next_level = level+1
      for dst_pkg in sorted_packages[next_level]:
        if src_pkg in dep_packages and dst_pkg in dep_packages[src_pkg]:
          edges.add((src_pkg, dst_pkg, 'false'))
      #insert reduced edge if not found.
      if src_pkg in reduced_edges:
        for e in reduced_edges[src_pkg]:
          if e[0] == src_pkg and e not in edges:
            edges.add(e)
    level=level+1


  for src_pkg,dst_pkgs in cycle_edges.items():
    for dp in dst_pkgs:
      if src_pkg in nodes and dp in nodes:
        edges.add((src_pkg, dp, 'true'))

  return edges

#-------------------------------------------------------------------------------
def MakeFullEdgePackageLevel(nodes, level0_packages, dep_packages, cycle_edges):
  full_package_level={}
  inserted_pkgs=set()
  total_pkgs=len(nodes)
  pkg_count=0
  level_packages=[]

  level=0
  level_packages.append([])
  for pkg in level0_packages:
    full_package_level[pkg]=level
    level_packages[level].append(pkg)
    inserted_pkgs.add(pkg)
    pkg_count=pkg_count+1

  while( pkg_count < total_pkgs ):
    #print "[", level, "] pkgs: ", pkg_count, ", ", total_pkgs
    level = level + 1
    last_level = level - 1
    level_packages.append([])
    for pkg in level_packages[last_level]:
      if pkg in dep_packages:
        for dst_pkg in dep_packages[pkg]:
          if dst_pkg not in inserted_pkgs and dst_pkg in nodes:
            full_package_level[dst_pkg]=level
            level_packages[level].append(dst_pkg)
            inserted_pkgs.add(dst_pkg)
            pkg_count=pkg_count+1

      if pkg in cycle_edges:
        for dst_pkg in cycle_edges[pkg]:
          if dst_pkg not in inserted_pkgs and dst_pkg in nodes:
            full_package_level[dst_pkg]=level
            level_packages[level].append(dst_pkg)
            inserted_pkgs.add(dst_pkg)
            pkg_count=pkg_count+1

  return full_package_level

#-------------------------------------------------------------------------------
def MakeFullEdges(nodes, dep_packages, cycle_edges):
  edges=set()

  for pkg in nodes:
    if pkg in dep_packages:
      for dst_pkg in dep_packages[pkg]:
        if dst_pkg in nodes:
          edges.add((pkg, dst_pkg, 'false'))

  for src_pkg,dst_pkgs in cycle_edges.items():
    for dp in dst_pkgs:
      if src_pkg in nodes and dp in nodes:
        edges.add((src_pkg, dp, 'true'))

  return edges

#-------------------------------------------------------------------------------
def CalcBuildLevel(pkg_begin, nodes, dep_packages, in_edge_count):
  level=0
  level_packages=[]
  total_pkg_count=len(nodes)
  pkg_count=0
  pkgs_processed=set()
  level2_pkg_count=0

  # for level0 packages.
  level_packages.append([])
  for pkg in nodes:
    if pkg not in in_edge_count or in_edge_count[pkg] == 0:
      level_packages[level].append(pkg)
      pkg_count = pkg_count + 1
      pkgs_processed.add(pkg)

  while(pkg_count < total_pkg_count):
    level=level+1
    last_level=level-1
    level_packages.append([])
    for pkg in level_packages[last_level]:
      if pkg in dep_packages:
        for dst_pkg in dep_packages[pkg]:
          if dst_pkg not in pkgs_processed:
            level_packages[level].append(dst_pkg)
            pkgs_processed.add(dst_pkg)
            pkg_count=pkg_count+1
            if level > 1:
              level2_pkg_count = level2_pkg_count + 1


  print pkg_begin, ',', level, ',', level2_pkg_count, ',', total_pkg_count

#-------------------------------------------------------------------------------
def TopologySortPackages(nodes, dep_packages, in_edge_count, cycle_edges, reduced_edges):
  level=0
  pkg_count=0
  total_pkg_count=len(nodes)
  sorted_packages=[]
  pkg_level={}
  # loop until all packages are inserted to sorted_packages
  while( pkg_count < total_pkg_count):
    sorted_packages.append([])
    # find packages that have zero in_edge_count
    for pkg in nodes:
      if pkg not in in_edge_count or in_edge_count[pkg] == 0:
        #print "level("+str(level)+") pkg="+pkg
        sorted_packages[level].append(pkg)
        in_edge_count[pkg]=-1
        pkg_level[pkg]=level
        pkg_count=pkg_count+1

    # if no packages in this level, but pkg_count < total_pkg_count,
    # It is the case there is a cycle. Currently, we cannot solve this case.
    if( len(sorted_packages[level]) == 0 and pkg_count < total_pkg_count ):
      print 'there is a cycle!!! Cycles should be removed before calling TopologySortPackages!'
      exit(0)

    #decrease in_edge_count for target packages
    for pkg in sorted_packages[level]:
      if pkg in dep_packages:
        for dep_pkg in dep_packages[pkg]:
          in_edge_count[dep_pkg] = in_edge_count[dep_pkg] - 1
    level = level+1

  # compensate nodes.
  # if a node is in cycle_edges, insert it into the nodes.
  for src,dst_pkgs in cycle_edges.items():
    if src not in nodes:
      continue
    for d in dst_pkgs:
      if d not in nodes:
        nodes.add(d)
        pkg_level[d]=pkg_level[src]+1

  edges = MakeEdges(nodes, sorted_packages, dep_packages, cycle_edges, reduced_edges)
  full_edges = MakeFullEdges(nodes, dep_packages, cycle_edges)
  #full_edge_pkg_level = MakeFullEdgePackageLevel(nodes, sorted_packages[0], dep_packages, cycle_edges)
  #return nodes, edges, pkg_level, full_edges, full_edge_pkg_level
  return nodes, edges, pkg_level, full_edges

#-------------------------------------------------------------------------------
def IncreaseInEdgesCount(pkg_name,in_edge_count):
  if not pkg_name in in_edge_count:
    in_edge_count[pkg_name] = 0
  in_edge_count[pkg_name] = in_edge_count[pkg_name] + 1

#-------------------------------------------------------------------------------
def InsertPackage(pkg_name):
  if not pkg_name in pkg_id:
    pkg_id[pkg_name] = len(pkg_id)
    pkg_print_index[pkg_name] = 0

#-------------------------------------------------------------------------------
def InsertNode(pkg_id, pkg_name, nodes):
  nodes.add((pkg_id, pkg_name))

#-------------------------------------------------------------------------------
pkg_group_id={}
def GetGroupId(pkg_name):
  return pkg_group_id[pkg_name]

#-------------------------------------------------------------------------------
max_group_id=0
def SetGroupId(pkg_name, group_id):
  if not pkg_group_id.has_key(pkg_name):
    pkg_group_id[pkg_name] = group_id
  elif pkg_group_id[pkg_name] < group_id:
    pkg_group_id[pkg_name] = group_id
  elif pkg_group_id[pkg_name] == group_id:
    pkg_group_id[pkg_name] = group_id+1

  global max_group_id
  if max_group_id < pkg_group_id[pkg_name]:
    max_group_id = pkg_group_id[pkg_name]

#-------------------------------------------------------------------------------
def MakeIndexedPackageName(pkg_name):
  #indexed_pkg_name = pkg_name
  indexed_pkg_name = pkg_name + '(' + str(pkg_print_index[pkg_name]) + ')'
  InsertPackage(indexed_pkg_name)
  indexed_pkg_name_id = pkg_id[indexed_pkg_name]
  return indexed_pkg_name, indexed_pkg_name_id

#-------------------------------------------------------------------------------
def FindMainPackageName(sub_pkg_name):
  # If it is a main package, we cannot find it in sub_main_pkg.
  # In this case, just return the package name
  if sub_pkg_name in main_sub_pkg.keys():
    return sub_pkg_name

  if not sub_pkg_name in sub_main_pkg:
    return None

  return sub_main_pkg[sub_pkg_name]

#-------------------------------------------------------------------------------
def InsertSubPackage(pkg_name, sub_pkg_name):
  if not pkg_name in main_sub_pkg:
    main_sub_pkg[pkg_name]=[]
  main_sub_pkg[pkg_name].append(sub_pkg_name)

  if sub_pkg_name in sub_main_pkg:
    print 'Subpackage ' + sub_pkg_name + ' is related to one or more main ' + 'packages(' + sub_main_pkg[sub_pkg_name] + ','+ pkg_name + ')!\n'
  sub_main_pkg[sub_pkg_name] = pkg_name

  pkg_print_index[sub_pkg_name] = 0

#-------------------------------------------------------------------------------
def InsertEdge(pkg_name, dep_pkg_name):
  if not dep_pkg_name in sub_pkg_edges:
    sub_pkg_edges[dep_pkg_name]=[]
  InsertPackage(dep_pkg_name)
  InsertPackage(pkg_name)
  sub_pkg_edges[dep_pkg_name].append(pkg_name)
  #pprint.PrettyPrinter(indent=4).pprint(sub_pkg_edges)

#-------------------------------------------------------------------------------
def RemoveCycle(main_pkg_edges, full_in_edge_count):
  cycle_edges={}
  visited=set()
  path=set()

  def visit(level, node):
    if node in visited:
      return
    if node not in main_pkg_edges:
      return

    #for i in range(1,level):
      #print " ",
    #print "("+str(level)+")visiting "+node
    visited.add(node)
    path.add(node)
    dst_pkgs=main_pkg_edges[node].copy()
    for dst in dst_pkgs:
      if dst in path:
        #cycle!
        print "removing cycle (" + node + "->"+dst+")"
        if node not in cycle_edges:
          cycle_edges[node]=set()
        cycle_edges[node].add(dst)
        main_pkg_edges[node].remove(dst)
        full_in_edge_count[dst]=full_in_edge_count[dst]-1
      else:
        visit(level+1, dst)
    path.remove(node)

  for pkg in main_pkg_edges.keys():
    visit(0, pkg)

  return main_pkg_edges, cycle_edges, full_in_edge_count

#-------------------------------------------------------------------------------
def MakeSubGraphMultiPackages(pkgs_to_start, main_pkg_edges, cycle_edges):
  pkg_status={}
  nodes=set()
  dep_packages={}
  in_edge_count={}

  for pkg_to_start in pkgs_to_start:
    pkg_name = FindMainPackageName(pkg_to_start)
    if pkg_name is None:
      continue
    more_packages=1
    while(more_packages):
      more_packages=0
      #print 'adding pkg '+pkg_name
      if pkg_name not in nodes:
        nodes.add(pkg_name)
        if pkg_name in main_pkg_edges:
          for dst_pkg_name in main_pkg_edges[pkg_name]:
            if pkg_name not in dep_packages:
              dep_packages[pkg_name]=[]
            dep_packages[pkg_name].append(dst_pkg_name)
            if dst_pkg_name not in in_edge_count:
              in_edge_count[dst_pkg_name] = 0
            in_edge_count[dst_pkg_name] = in_edge_count[dst_pkg_name] + 1
            if dst_pkg_name not in pkg_status:
              #print 'pkg_status['+dst_pkg_name+']=visited'
              pkg_status[dst_pkg_name]='visited'
      if pkg_name in cycle_edges:
        for dst_pkg_name in cycle_edges[pkg_name]:
          if pkg_name not in dep_packages:
            dep_packages[pkg_name]=[]
          dep_packages[pkg_name].append(dst_pkg_name)
          if dst_pkg_name not in pkg_status:
            #print 'pkg_status['+dst_pkg_name+']=visited'
            pkg_status[dst_pkg_name]='visited'

      pkg_status[pkg_name] = 'printed'

      for p in pkg_status.keys():
        if pkg_status[p] == 'visited':
          pkg_name = p
          more_packages=1
          break

  #for n in nodes:
    #if n in in_edge_count:
      #print 'in_edges['+n+']='+str(in_edge_count[n])
    #else:
      #print 'in_edges['+n+']=0'
#
  return nodes, dep_packages, in_edge_count

#-------------------------------------------------------------------------------
def MakeSubGraph(pkg_to_start, main_pkg_edges, cycle_edges):
  pkg_status={}
  nodes=set()
  dep_packages={}
  in_edge_count={}

  pkg_name = FindMainPackageName(pkg_to_start)
  more_packages=1
  while(more_packages):
    more_packages=0
    #print 'adding pkg '+pkg_name
    nodes.add(pkg_name)
    if pkg_name in main_pkg_edges:
      for dst_pkg_name in main_pkg_edges[pkg_name]:
        if pkg_name not in dep_packages:
          dep_packages[pkg_name]=[]
        dep_packages[pkg_name].append(dst_pkg_name)
        if dst_pkg_name not in in_edge_count:
          in_edge_count[dst_pkg_name] = 0
        in_edge_count[dst_pkg_name] = in_edge_count[dst_pkg_name] + 1
        if dst_pkg_name not in pkg_status:
          #print 'pkg_status['+dst_pkg_name+']=visited'
          pkg_status[dst_pkg_name]='visited'
    if pkg_name in cycle_edges:
      for dst_pkg_name in cycle_edges[pkg_name]:
        if pkg_name not in dep_packages:
          dep_packages[pkg_name]=[]
        dep_packages[pkg_name].append(dst_pkg_name)
        if dst_pkg_name not in pkg_status:
          #print 'pkg_status['+dst_pkg_name+']=visited'
          pkg_status[dst_pkg_name]='visited'

    pkg_status[pkg_name] = 'printed'

    for p in pkg_status.keys():
      if pkg_status[p] == 'visited':
        pkg_name = p
        more_packages=1
        break

  return nodes, dep_packages, in_edge_count

#-------------------------------------------------------------------------------
def PrintVisFormat(filename, nodes, edges, pkg_level, reverse, built_packages=[]):

  level_count={}
  distance_y = 100
  distance_x = 300

  f = open(filename, 'w')

  f.write("var nodes = [\n")
  for pkg_name in nodes:
    my_pkg_level = pkg_level[pkg_name]
    if my_pkg_level not in level_count:
      level_count[my_pkg_level] = -1
    level_count[my_pkg_level] = level_count[my_pkg_level] + 1
    pos_y = level_count[my_pkg_level] * distance_y
    pos_x = my_pkg_level * distance_x
    color_string="undefined"
    if len(built_packages) > 0 and pkg_name not in built_packages:
      color_string="'rgba(230,230,230,0.5)'"
    option_color_str=",color:"+color_string+",set_color:"+color_string
    f.write("{id: " + str(pkg_id[pkg_name]) + ", label: '" + pkg_name + 
    "', group:" + str(my_pkg_level) + ", level:" + str(my_pkg_level) + 
    ", x:"+str(pos_x) + ", y:" + str(pos_y) + option_color_str + "},\n")
  f.write("];\n")

  f.write("var edges = new vis.DataSet([\n")
  for i in edges:
    f.write("{from: " + str(pkg_id[i[0]]) + ", to: " + str(pkg_id[i[1]]) + 
    ", dashes:"+i[2]+", arrows:")
    if reverse:
      f.write("'from'")
    else:
      f.write("'to'")
    f.write("},\n")
  f.write("]);\n")

#-------------------------------------------------------------------------------
def PrintHTML(html_out, js_out, pkg_name, template_filename, reverse, full_edges, 
              vis_dir_str, dep_graph_dir_str):
  template_f = open(template_filename, 'r')
  HTML_f = open(html_out, 'w')

  graph_str="Dependency graph"
  if reverse:
    graph_str="Reverse dependency graph"

  full_edges_str=""
  if full_edges:
    full_edges_str="_full"

  for line in template_f:
    replaced_str=re.sub('%src.js%', '"'+js_out+'"', line)
    replaced_str=re.sub('%package%', pkg_name, replaced_str)
    replaced_str=re.sub('%graph%', graph_str, replaced_str)
    replaced_str=re.sub('%full_edges%', full_edges_str, replaced_str)
    replaced_str=re.sub('%vis_dir%', vis_dir_str, replaced_str)
    replaced_str=re.sub('%dep_graph_dir%', dep_graph_dir_str, replaced_str)
    replaced_str=re.sub('%obs_api_url%', obs_api_url, replaced_str)
    replaced_str=re.sub('%obs_project%', obs_project, replaced_str)
    replaced_str=re.sub('%obs_repo%', obs_repo, replaced_str)
    replaced_str=re.sub('%obs_arch%', obs_arch, replaced_str)
    replaced_str=re.sub('%obs_username%', obs_username, replaced_str)
    replaced_str=re.sub('%obs_pw%', obs_pw, replaced_str)
    replaced_str=re.sub('%mysql_ip%', mysql_ip, replaced_str)
    replaced_str=re.sub('%mysql_username%', mysql_username, replaced_str)
    replaced_str=re.sub('%mysql_pw%', mysql_pw, replaced_str)
    replaced_str=re.sub('%mysql_db%', mysql_db, replaced_str)
    replaced_str=re.sub('%mysql_build_target_id%', mysql_build_target_id, replaced_str)
    replaced_str=re.sub('%get_build_status_from%', get_build_status_from, replaced_str)

    HTML_f.write(replaced_str)

  HTML_f.close()
  template_f.close()

#-------------------------------------------------------------------------------
def GenerateOutput(dir_name, pkg_name, nodes, edges, pkg_level, full_edges, full_edge_pkg_level,
    reverse, template_filename, vis_dir_str, dep_graph_dir_str, built_packages):

  js_postfix=".vis_input.js"
  php_postfix=".html"
  if reverse:
    js_postfix="_reverse"+js_postfix
    php_postfix="_reverse"+php_postfix

  pkg_js=pkg_name+js_postfix
  js_out=dir_name+"/"+pkg_js
  html_out=dir_name+"/"+pkg_name+php_postfix

  #print "printing ", pkg_js, "..."
  PrintVisFormat(js_out, nodes, edges, pkg_level, reverse, built_packages)
  PrintHTML(html_out, pkg_js, pkg_name, template_filename, reverse, False, vis_dir_str, dep_graph_dir_str)

  js_postfix=".full_edges.vis_input.js"
  php_postfix="_full_edges.html"
  if reverse:
    js_postfix="_reverse"+js_postfix
    php_postfix="_reverse"+php_postfix

  pkg_js=pkg_name+js_postfix
  js_out=dir_name+"/"+pkg_js
  html_out=dir_name+"/"+pkg_name+php_postfix

  #print "printing ", pkg_js, "..."
  PrintVisFormat(js_out, nodes, full_edges, full_edge_pkg_level, reverse, built_packages)
  PrintHTML(html_out, pkg_js, pkg_name, template_filename, reverse, True, vis_dir_str, dep_graph_dir_str)

#-------------------------------------------------------------------------------
def MakeOptionParser():
  parser = OptionParser("Usage: %prog [OPTIONS] <dependency file (xml)>")
  parser.add_option('-d', '--dir', action='store', type='string', dest='dest_dir',
                    help="Specify the destination directory.",
                    default="build_dep")
  parser.add_option('-c', '--calc-build-level', action='store_true', dest='calc_build_level',
                    help="Calculate build level for each packages.",
                    default=False)
  parser.add_option('-p', '--packages', action='store', type='string', dest='dep_multi_pkgs',
                    help="Print number of nodes for specified packages",
                    default="")
  parser.add_option('-g', '--git-packages', action='store', type='string', dest='git_dep_multi_pkgs',
                    help="Print number of nodes for specified packages using gitpath",
                    default="")
  parser.add_option('-v', '--vis-dir', action='store', type='string', 
                    dest='vis_dir_str',
                    help="Specifies the location of vis source code",
                    default="../vis")
  parser.add_option('-t', '--template-file', action='store', type='string', 
                    dest='template_filename',
                    help="Specifies the template file name",
                    default="dep_graph.php.template")
  parser.add_option('-e', '--dep-graph-dir', action='store', type='string', 
                    dest='dep_graph_dir_str',
                    help="Specifies the location of dep_graph source code",
                    default="../dep_graph")
  parser.add_option('-b', '--built-packages-file', action='store', type='string', 
                    dest='build_packages_file',
                    help="Specifies a file containing a list of built packages",
                    default="")
  parser.add_option('-n', '--print-num-nodes', action='store_true', dest='print_num_nodes',
                    help="Print the number of nodes of the graph and the number of directly connected nodes",
                    default=False)
  return parser

def set_obs_environment(_obs_api_url, _obs_username, _obs_pw, _obs_project, _obs_repo, _obs_arch):
  global obs_api_url
  global obs_username
  global obs_pw
  global obs_project
  global obs_repo
  global obs_arch
  global get_build_status_from

  obs_api_url = _obs_api_url
  obs_username = _obs_username
  obs_pw = _obs_pw
  obs_project = _obs_project
  obs_repo = _obs_repo
  obs_arch = _obs_arch
  get_build_status_from="get_build_status_from_obs"


def set_mysql_environment(_mysql_ip, _mysql_username, _mysql_pw, _mysql_db, _mysql_build_target_id):
  global mysql_ip
  global mysql_username
  global mysql_pw
  global mysql_db
  global mysql_build_target_id
  global get_build_status_from

  mysql_ip = _mysql_ip
  mysql_username = _mysql_username
  mysql_pw = _mysql_pw
  mysql_db = _mysql_db
  mysql_build_target_id = _mysql_build_target_id
  get_build_status_from="get_build_status_from_mysql"

def make_dep_graph(input_file, dest_dir_name,
    vis_dir_str, dep_graph_dir_str, template_filename,
    dep_multi_pkgs=[], built_packages=[],
    print_num_nodes=False, calc_build_level=False):
  global sub_pkg_edges
  global pkg_id
  global main_sub_pkg
  global pkg_print_index
  global sub_main_pkg
  global dep_edges
  global sorted_packages
  global main_pkg_level
  global reduced_edges
  global reduced_edges_reverse
  global git_obs_mapping

  sub_pkg_edges={}
  pkg_id={}
  main_sub_pkg={}
  pkg_print_index={}
  sub_main_pkg={}
  dep_edges=set()
  sorted_packages=[]
  main_pkg_level={}
  reduced_edges={}
  reduced_edges_reverse={}
  git_obs_mapping={}

  if(not os.path.isfile(input_file)):
    print os.path.basename(sys.argv[0]) + ': ' + input_file + ': No such file.'
    sys.exit()

  tree = ElementTree.parse(input_file)
  root = tree.getroot()
  for package in root:
    if package.tag != 'package':
      continue
    pkg_name = package.attrib['name']
    InsertPackage(pkg_name)
    dep_pkg_list = []

    for child in package:
      if(child.tag == 'pkgdep'):
        dep_pkg_name = child.text
        dep_pkg_list.append(dep_pkg_name)
      if(child.tag == 'subpkg'):
        sub_pkg_name = child.text
        InsertSubPackage(pkg_name, sub_pkg_name)

    # if there are no sub packages, insert itself.
    if not pkg_name in main_sub_pkg:
      main_sub_pkg[pkg_name]=[]
      main_sub_pkg[pkg_name].append(pkg_name)

    # make dependence (dep_pkg_list -> sub_pkg_name)
    for dep_pkg_name in dep_pkg_list:
      for sub_pkg_name in main_sub_pkg[pkg_name]:
        InsertEdge(sub_pkg_name, dep_pkg_name)

  main_pkg_edges={}
  full_in_edge_count={}
  main_pkg_reverse_edges={}
  full_in_reverse_edge_count={}
  #generate main_pkg_edges using sub_pkg_edges
  for src,dst_pkgs in sub_pkg_edges.items():
    src_main = FindMainPackageName(src)
    if src_main is None:
      continue
    src_main_id = pkg_id[src_main]
    for dst in dst_pkgs:
      dst_main = FindMainPackageName(dst)
      if dst_main is None:
        continue
      dst_main_id = pkg_id[dst_main]

      #for main_pkg_edges
      if not src_main in main_pkg_edges:
        main_pkg_edges[src_main]=set()
      if dst_main not in main_pkg_edges[src_main]:
        main_pkg_edges[src_main].add(dst_main)
        if dst_main not in full_in_edge_count:
          full_in_edge_count[dst_main]=0
        full_in_edge_count[dst_main]=full_in_edge_count[dst_main]+1

      # for main_pkg_reverse_edges
      if not dst_main in main_pkg_reverse_edges:
        main_pkg_reverse_edges[dst_main]=set()
      if src_main not in main_pkg_reverse_edges[dst_main]:
        main_pkg_reverse_edges[dst_main].add(src_main)
        if src_main not in full_in_reverse_edge_count:
          full_in_reverse_edge_count[src_main]=0
        full_in_reverse_edge_count[src_main]=full_in_reverse_edge_count[src_main]+1


  #print 'Removing cycles...'
  main_pkg_edges, cycle_edges, full_in_edge_count = RemoveCycle(main_pkg_edges, full_in_edge_count)
  main_pkg_reverse_edges, cycle_reverse_edges, full_in_reverse_edge_count = RemoveCycle(main_pkg_reverse_edges, full_in_reverse_edge_count)

  ## for dependency graph
  #make build_dep
  shutil.rmtree(dest_dir_name, ignore_errors=True)
  os.makedirs(dest_dir_name)

  if len(dep_multi_pkgs) > 0:
    nodes, dep_packages, in_edge_count=MakeSubGraphMultiPackages(dep_multi_pkgs, main_pkg_edges, cycle_edges)
    print "num_nodes=",len(nodes)
    nodes, edges, pkg_level, full_edges = TopologySortPackages(nodes, dep_packages, in_edge_count, cycle_edges, reduced_edges)
    GenerateOutput(dest_dir_name, 'index', nodes, edges, pkg_level, full_edges, pkg_level, False, template_filename, vis_dir_str, dep_graph_dir_str, built_packages)
    return

  #make a dependency graph for each package.
  for pkg in main_sub_pkg.keys():
    #print 'processing package for dependence graph: ' + pkg
    nodes, dep_packages, in_edge_count=MakeSubGraph(pkg, main_pkg_edges, cycle_edges)
    if calc_build_level:
      CalcBuildLevel(pkg, nodes, dep_packages, in_edge_count)
    nodes, edges, pkg_level, full_edges = TopologySortPackages(nodes, dep_packages, in_edge_count, cycle_edges, reduced_edges)
    reduced_edges[pkg]=edges.copy()
    if print_num_nodes:
      num_direct_pkgs=1
      node_names=""
      if pkg in main_pkg_edges:
        num_direct_pkgs=num_direct_pkgs+len(main_pkg_edges[pkg])
        node_names=','.join(main_pkg_edges[pkg])
      print pkg+"|"+str(len(nodes))+"|"+str(num_direct_pkgs)+"|"+node_names
    GenerateOutput(dest_dir_name, pkg, nodes, edges, pkg_level, full_edges, pkg_level, False, template_filename, vis_dir_str, dep_graph_dir_str, built_packages)

  #make a full dependency graph
  #print 'printing full package dependency graph...'
  nodes, edges, pkg_level, full_edges = TopologySortPackages(main_sub_pkg.keys(), main_pkg_edges, full_in_edge_count, cycle_edges, reduced_edges)
  GenerateOutput(dest_dir_name, 'index', nodes, edges, pkg_level, full_edges, pkg_level, False, template_filename, vis_dir_str, dep_graph_dir_str, built_packages)

  #--------------------------------------------------------------------------------
  ## for reverse dependency graph

  #make a reverse dependency graph for each package.
  for pkg in main_sub_pkg.keys():
    #print 'processing package for reverse dependence graph: ' + pkg
    nodes, dep_packages, in_edge_count=MakeSubGraph(pkg, main_pkg_reverse_edges, cycle_reverse_edges)
    nodes, edges, pkg_level, full_edges = TopologySortPackages(nodes, dep_packages, in_edge_count, cycle_reverse_edges, reduced_edges_reverse)
    reduced_edges_reverse[pkg]=edges.copy()
    GenerateOutput(dest_dir_name, pkg, nodes, edges, pkg_level, full_edges, pkg_level, True, template_filename, vis_dir_str, dep_graph_dir_str, built_packages)

  #make a full dependency graph
  #print 'printing full package reverse dependency graph...'
  nodes, edges, pkg_level, full_edges = TopologySortPackages(main_sub_pkg.keys(), main_pkg_reverse_edges, full_in_reverse_edge_count, cycle_reverse_edges, reduced_edges_reverse)
  GenerateOutput(dest_dir_name, 'index', nodes, edges, pkg_level, full_edges, pkg_level, True, template_filename, vis_dir_str, dep_graph_dir_str, built_packages)


#-------------------------------------------------------------------------------
# !!main!!
def main(argv):
  parser = MakeOptionParser()

  (options, args) = parser.parse_args(argv)
  dest_dir_name=options.dest_dir
  calc_build_level=options.calc_build_level
  dep_multi_pkgs=[]
  built_packages=[]
  print_num_nodes=options.print_num_nodes
  vis_dir_str=options.vis_dir_str
  dep_graph_dir_str=options.dep_graph_dir_str
  template_filename=options.template_filename

  if len(options.dep_multi_pkgs) > 0 and len(options.git_dep_multi_pkgs) > 0:
    print "You cannot use both options --packages and --git-packages!!"
    exit(0)

  if len(options.dep_multi_pkgs) > 0:
    dep_multi_pkgs=options.dep_multi_pkgs.split(",")

  if len(options.git_dep_multi_pkgs) > 0:
    git_packages=options.git_dep_multi_pkgs.split(",")
    dep_multi_pkgs=TranslateGitOBSMapping(git_packages)

  if len(options.build_packages_file) > 0:
    if len(dep_multi_pkgs) == 0:
      print "option --built-packages should be combined with --packages! exiting...\n"
      exit(0)
    built_packages=GetBuiltPackageList(options.build_packages_file)

  if( len(args) != 2 ):
    parser.print_help()
    sys.exit()

  input_file=args[1]

  make_dep_graph(input_file, dest_dir_name, vis_dir_str, dep_graph_dir_str,
    template_filename, dep_multi_pkgs, built_packages, print_num_nodes, calc_build_level)

if __name__ == '__main__':
  sys.exit(main(sys.argv))
