#!/usr/bin/env python

import sys
import IPy

from roster_user_tools import cli_record_lib
from roster_user_tools import cli_common_lib
from roster_user_tools import roster_client_lib
from roster_user_tools.data_flags import MassAdd
from roster_user_tools.action_flags import Make


class Args(Make, MassAdd):
  pass


def main(args):
  """Collects command line arguments, writes hosts from file, and
  enumerates any overwritten hosts.
  Inputs:
    args: list of arguments from the commandline

  Outputs: 
    List of IP's and hostnames that were overwritten.
  """
  command = 'add'
  if( args and not args[0].startswith('-') ):
    command = args.pop(0)
  usage = ('\n'
          '\n'
          'To add a list of hosts from a file:\n'
          '\t%s massadd -f <file-name> -v <view-name> -z <zone-name>\n'
          '\nFile example:\n'
          '192.168.1.5 computer1\n'
          '192.168.1.8 host3\n'
          '\n' % tuple(
              [sys.argv[0] for _ in range(1)]))
  args_instance = Args(command, ['add'], args, usage)
  options = args_instance.options

  try:
    cli_common_lib_instance = cli_common_lib.CliCommonLib(options)
  except cli_common_lib.ArgumentError, error:
    print 'ERROR: %s' % error
    sys.exit(1)
  cli_record_lib.CliRecordLib(cli_common_lib_instance)
  try:
    handle = open(options.file)
  except IOError:
    print ('Specified file, %s, does not exist' % (options.file))
    sys.exit(1)
  try:
    file_lines = handle.readlines()
  finally:
    handle.close()
  records_to_remove = []
  records_to_add = []
  for line in file_lines:
    if not line.strip():
      continue
    ip_address = line.rsplit()[0].strip()
    host_name = line.rsplit()[1].strip()
    ## FIND TYPE
    record_type = IPy.IP(ip_address).version()
    if( record_type == 4 ):
      record_type = u'a'
    elif( record_type == 6 ):
      record_type = u'aaaa'
      ip_address = IPy.IP(ip_address).strFullsize()
    ## ADD TO ADDLIST
    record_dict = {
        'record_type': record_type,
        'record_ttl': 3600,
        'record_target': host_name,
        'record_view_dependency': options.view_name,
        'record_zone_name': options.zone_name,
        'record_arguments': {'assignment_ip': ip_address}}
    records_to_add.append(record_dict)

    zone_list = roster_client_lib.RunFunction(
          'ListZones', options.username, credfile=options.credfile,
          server_name=options.server,
          kwargs={'zone_name': options.zone_name})['core_return']
    if( options.zone_name not in zone_list ):
      cli_common_lib.DnsError(
          'Zone "%s" does not exist.' % options.zone_name, 1)
    if( options.view_name not in zone_list[options.zone_name] ):
      cli_common_lib.DnsError('Zone "%s" not found in "%s" view.' % (
          options.zone_name, options.view_name), 1)
    zone_origin = zone_list[options.zone_name][options.view_name][
        'zone_origin']
    if( host_name.rstrip('.').endswith(zone_origin.rstrip('.')) ):
      cli_common_lib.DnsError(
          'Hostname cannot end with domain name.', 1)
    if( zone_origin.endswith('.in-addr.arpa.') or
        zone_origin.endswith('.ip6.arpa.') ):
      cli_common_lib.DnsError(
          'This tool requires a forward zone as an argument. '
          'Reverse zones are handled automatically.', 1)
    reverse_zone_name = roster_client_lib.RunFunction(
        'ListZoneByIPAddress', options.username, credfile=options.credfile,
        server_name=options.server, args=[ip_address])['core_return']
    if( reverse_zone_name == None ):
      cli_common_lib.DnsError(
          'No reverse zone found for ip "%s"' % ip_address, 1)
    zone_cidr = roster_client_lib.RunFunction(
        'ListReverseRangeZoneAssignments',
        options.username, credfile=options.credfile,
        server_name=options.server, kwargs={'zone_name': reverse_zone_name})[
            'core_return'][reverse_zone_name]
    net_mask = int(IPy.IP(zone_cidr).netmask().strDec(0))
    masked_ip = ~net_mask & int(IPy.IP(ip_address).strDec(0))
    ptr_target = IPy.IP(masked_ip).reverseName()
    ptr_target = ptr_target.rstrip('.in-addr.arpa.').rstrip('.ip6.arpa.')
    ptr_target_split = ptr_target.split('.')
    cidr_size = int(zone_cidr.split('/')[1])
    if( record_type == 'a' ):
      ptr_target = '.'.join(ptr_target_split[:4 - (cidr_size / 8)])
    else:
      ptr_target = '.'.join(ptr_target_split[:128 - cidr_size + 1])

    ## ADD TO ADDLIST
    ptr_dict = {
        'record_type': u'ptr',
        'record_target': ptr_target,
        'record_ttl': 3600,
        'record_view_dependency': options.view_name,
        'record_zone_name': reverse_zone_name,
        'record_arguments': {
        'assignment_host': '%s.%s' % (host_name, zone_origin)}}
    records_to_add.append(ptr_dict)

    ## FIND CONFLICTING RECORDS
    records = roster_client_lib.RunFunction(
        "ListRecordsByCIDRBlock",
        options.username, credfile=options.credfile,
        server_name=options.server, args=[ip_address])['core_return']
    ## ADD CONFLICTS TO REMOVELIST
    for view in records:
      for record_ip in records[view]:
        for record in iter(records[view][record_ip]):
          records_to_remove.append(record)
  if not options.commit:
    print('Commit flag not specified. '
        'Changes will not be made to the database.\n')

  print("HOSTS TO BE REMOVED: ")
  zone_list = ['zone']
  print_list = [['#', 'type', 'target', 'zone', 'view']]
  count = 0
  for record in records_to_remove:
    record_list = []
    record_list.append( '%s' % count )
    count += 1
    record_list.append( record['record_type'] )
    record_list.append( record['record_target'] )
    record_list.append( options.zone_name )
    record_list.append( options.view_name )
    print_list.append(record_list)
  print cli_common_lib.PrintColumns(
      print_list=print_list, first_line_header=True)

  print("\nHOSTS TO BE ADDED: ")
  zone_list = ['zone']
  print_list = [['#', 'type', 'target', 'zone', 'view']]
  count = 0
  for record in records_to_add:
    record_list = []
    record_list.append( '%s' % count )
    count += 1
    record_list.append( record['record_type'] )
    record_list.append( record['record_target'] )
    record_list.append( options.zone_name )
    record_list.append( options.view_name )
    print_list.append(record_list)
  print cli_common_lib.PrintColumns(
      print_list=print_list, first_line_header=True)
    
  if options.commit:
    roster_client_lib.RunFunction(
        "ProcessRecordsBatch", options.username, credfile=options.credfile,
        server_name=options.server,
        kwargs={'delete_records': records_to_remove,
            'add_records': records_to_add})
  else:
    print('\nCommit flag not specified. '
        'Changes will not be made to the database.')

if __name__ == "__main__":
  main(sys.argv[1:])
