# Wizard Kit: Functions - Windows Setup

from functions.data import *
from functions.disk import *
from settings.windows_setup import *


def find_windows_image(windows_version):
  """Search for a Windows source image file, returns dict.

  Searches on local disks and then the WINDOWS_SERVER share."""
  image = {}
  imagefile = windows_version['Image File']
  imagename = windows_version['Image Name']

  # Search local source
  set_thread_error_mode(silent=True) # Prevents "No disk" popups
  for d in psutil.disk_partitions():
    for ext in ['esd', 'wim', 'swm']:
      path = '{}images\{}.{}'.format(d.mountpoint, imagefile, ext)
      if os.path.isfile(path) and wim_contains_image(path, imagename):
        image['Path'] = path
        image['Letter'] = d.mountpoint[:1].upper()
        image['Local'] = True
        if ext == 'swm':
          image['Glob'] = '--ref="{}*.swm"'.format(image['Path'][:-4])
        break
  set_thread_error_mode(silent=False) # Return to normal

  # Check for network source
  if not image:
    mount_windows_share()
    if WINDOWS_SERVER['Mounted']:
      for ext in ['esd', 'wim', 'swm']:
        path = r'\\{}\{}\images\{}.{}'.format(
          WINDOWS_SERVER['IP'],
          WINDOWS_SERVER['Share'],
          imagefile,
          ext)
        if os.path.isfile(path) and wim_contains_image(path, imagename):
          image['Path'] = path
          image['Letter'] = None
          image['Local'] = False
          if ext == 'swm':
            image['Glob'] = '--ref="{}*.swm"'.format(
              image['Path'][:-4])
          break

  # Display image to be used (if any) and return
  if image:
    print_info('Using image: {}'.format(image['Path']))
    return image
  else:
    print_error('Failed to find Windows source image for {}'.format(
      windows_version['Name']))
    raise GenericAbort


def format_disk(disk, use_gpt):
  """Format disk for use as a Windows OS disk."""
  if use_gpt:
    format_gpt(disk)
  else:
    format_mbr(disk)


def format_gpt(disk):
  """Format disk for use as a Windows OS disk using the GPT layout."""
  script = [
    # Partition table
    'select disk {}'.format(disk['Number']),
    'clean',
    'convert gpt',

    # System partition
    # NOTE: ESP needs to be >= 260 for Advanced Format 4K disks
    'create partition efi size=500',
    'format quick fs=fat32 label="System"',
    'assign letter="S"',

    # Microsoft Reserved (MSR) partition
    'create partition msr size=128',

    # Windows partition
    'create partition primary',
    'format quick fs=ntfs label="Windows"',
    'assign letter="W"',

    # Recovery Tools partition
    'shrink minimum=500',
    'create partition primary',
    'format quick fs=ntfs label="Recovery Tools"',
    'assign letter="T"',
    'set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"',
    'gpt attributes=0x8000000000000001',
    ]

  # Run
  run_diskpart(script)


def format_mbr(disk):
  """Format disk for use as a Windows OS disk using the MBR layout."""
  script = [
    # Partition table
    'select disk {}'.format(disk['Number']),
    'clean',

    # System partition
    'create partition primary size=100',
    'format fs=ntfs quick label="System Reserved"',
    'active',
    'assign letter="S"',

    # Windows partition
    'create partition primary',
    'format fs=ntfs quick label="Windows"',
    'assign letter="W"',

    # Recovery Tools partition
    'shrink minimum=500',
    'create partition primary',
    'format quick fs=ntfs label="Recovery"',
    'assign letter="T"',
    'set id=27',
    ]

  # Run
  run_diskpart(script)


def mount_windows_share():
  """Mount the Windows images share unless already mounted."""
  if not WINDOWS_SERVER['Mounted']:
    # Mounting read-write in case a backup was done in the same session
    # and the server was left mounted read-write. This avoids throwing an
    # error by trying to mount the same server with multiple credentials.
    mount_network_share(WINDOWS_SERVER, read_write=True)


def select_windows_version():
  """Select Windows version from a menu, returns dict."""
  actions = [
    {'Name': 'Main Menu', 'Letter': 'M'},
    ]

  # Menu loop
  selection = menu_select(
    title = 'Which version of Windows are we installing?',
    main_entries = WINDOWS_VERSIONS,
    action_entries = actions)

  if selection.isnumeric():
    return WINDOWS_VERSIONS[int(selection)-1]
  elif selection == 'M':
    raise GenericAbort


def setup_windows(windows_image, windows_version):
  """Apply a Windows image to W:"""
  cmd = [
    global_vars['Tools']['wimlib-imagex'],
    'apply',
    windows_image['Path'],
    windows_version['Image Name'],
    'W:\\']
  if 'Glob' in windows_image:
    cmd.extend(windows_image['Glob'])
  run_program(cmd)


def setup_windows_re(windows_version, windows_letter='W', tools_letter='T'):
  """Setup the WinRE partition."""
  win = r'{}:\Windows'.format(windows_letter)
  winre = r'{}\System32\Recovery\WinRE.wim'.format(win)
  dest = r'{}:\Recovery\WindowsRE'.format(tools_letter)

  # Copy WinRE.wim
  os.makedirs(dest, exist_ok=True)
  shutil.copy(winre, r'{}\WinRE.wim'.format(dest))

  # Set location
  cmd = [
    r'{}\System32\ReAgentc.exe'.format(win),
    '/setreimage',
    '/path', dest,
    '/target', win]
  run_program(cmd)


def update_boot_partition(system_letter='S', windows_letter='W', mode='ALL'):
  """Setup the Windows boot partition."""
  cmd = [
    r'{}\Windows\System32\bcdboot.exe'.format(
      global_vars['Env']['SYSTEMDRIVE']),
    r'{}:\Windows'.format(windows_letter),
    '/s', '{}:'.format(system_letter),
    '/f', mode]
  run_program(cmd)


def wim_contains_image(filename, imagename):
  """Check if an ESD/WIM contains the specified image, returns bool."""
  cmd = [
    global_vars['Tools']['wimlib-imagex'],
    'info',
    filename,
    imagename]
  try:
    run_program(cmd)
  except subprocess.CalledProcessError:
    return False

  return True


if __name__ == '__main__':
  print("This file is not meant to be called directly.")

# vim: sts=2 sw=2 ts=2
