{ pkgs }: rec {
  xml = import ./xml.nix;
  # generate = import ./generate.nix;

  settingsPass = name: contents: xml.elem "settings" [ (xml.attr "pass" name) ] contents;

  component = name: subelems: xml.elem "component" [
    (xml.attr "name" name)
    (xml.attr "processorArchitecture" "amd64")
    (xml.attr "publicKeyToken" "31bf3856ad364e35")
    (xml.attr "language" "neutral")
    (xml.attr "versionScope" "nonSxS")
    (xml.attr "xmlns:wcm" "http://schemas.microsoft.com/WMIConfig/2002/State")
    (xml.attr "xmlns:xsi" "http://www.w3.org/2001/XMLSchema-instance")
  ] subelems;

  listElem = n: v: xml.elem n [ (xml.attr "wcm:action" "add") ] v;
  list = name: subname: items: xml.elem name [] (map (item: listElem subname item) items);

  elem = name: contents: xml.elem name [] contents;

  typeCheck = tname: test: x:
    if test x then x else builtins.abort ("expected " + tname + ", found " + builtins.typeOf x);
  typeConvert = tname: test: conv: x: conv (typeCheck tname test x);
  typeString = typeConvert "string" builtins.isString (x: x);
  typeInt = typeConvert "int" builtins.isInt builtins.toString;
  typeBool = typeConvert "bool" builtins.isBool (t: if t then "true" else "false");
  typeBoolAlwaysNever = typeConvert "bool" builtins.isBool (t: if t then "Always" else "Never");
  
  mkElem = name: type: option: elem name (type option);

  optional = config: name: elem: xml.opt (builtins.hasAttr name config) elem;
  
  process = config: xml.elem "unattend" [ (xml.attr "xmlns" "urn:schemas-microsoft-com:unattend") ] [
    (settingsPass "specialize" [
      (component "Microsoft-Windows-Shell-Setup" [
        (optional config "oemInfo" (mkElem "OEMInformation" [
          (mkElem "Manufacturer" typeString config.oemInfo.manufacturer)
          (mkElem "Model" typeString config.oemInfo.model)
        ]))
        (mkElem "ComputerName" typeString config.name)
        (optional config "productKey" (mkElem "ProductKey" typeString config.productKey))
        (mkElem "TimeZone" typeString config.locale.timeZone)
      ])
      
      (component "Microsoft-Windows-Security-SPP-UX" [
        (elem "SkipAutoActivation" "true")
      ])
      
      (component "Microsoft-Windows-Deployment" [
        (list "RunSynchronous" "RunSynchronousCommand" [
          [
            (elem "Description" "Notify Host")
            (elem "Order" "1")
            (elem "Path" "cmd.exe /C echo specialize>COM1")
          ]
        ])
      ])
    ])
    
    (settingsPass "windowsPE" [
      (component "Microsoft-Windows-Setup" [
        (elem "DiskConfiguration" [
          (mkElem "WillShowUI" typeBoolAlwaysNever config.diskConfig.showUI)
          (xml.elem "Disk" [ (xml.attr "wcm:action" "add") ] [
            (list "CreatePartitions" "CreatePartition" [
              [
                (elem "Order" "1")
                (elem "Size" "100")
                (elem "Type" "Primary")
              ]
              [
                (elem "Order" "2")
                (elem "Extend" "true")
                (elem "Type" "Primary")
              ]
            ])
            (list "ModifyPartitions" "ModifyPartition" [
              [
                (elem "Format" "NTFS")
                (elem "Label" "System Reserved")
                (elem "Order" "1")
                (elem "Active" "true")
                (elem "PartitionID" "1")
                (elem "TypeID" "0x27")
              ]
              [
                (elem "Format" "NTFS")
                (elem "Label" "Local Disk")
                (elem "Order" "2")
                (elem "Active" "true")
                (elem "PartitionID" "1")
                (elem "Letter" "C")
              ]
            ])
            (elem "DiskID" "0")
            (elem "WillWipeDisk" "true")
          ])
        ])
        (elem "DynamicUpdate" [
          (mkElem "WillShowUI" typeBoolAlwaysNever config.dynamicUpdate.showUI)
        ])
        (elem "ImageInstall" [
          (elem "OSImage" [
            (elem "InstallTo" [
              (elem "DiskID" "0")
              (elem "PartitionID" "2")
            ])
            (elem "InstallToAvailablePartition" "false")
            (elem "WillShowUI" "Never")
          ])
        ])
        (elem "UserData" [
          (elem "ProductKey" [
            (optional config "productKey" (mkElem "Key" typeString config.productKey))
          ])
          (mkElem "AcceptEula" typeBool config.userData.acceptEula)
          (mkElem "FullName" typeString config.userData.fullName)
          (mkElem "Organization" typeString config.userData.organization)
        ])
      ])
      (component "Microsoft-Windows-International-Core-WinPE" [
        (elem "SetupUILanguage" [
          (mkElem "UILanguage" typeString config.locale.language)
          (mkElem "WillShowUI" typeBoolAlwaysNever config.locale.showUI)
        ])
        (mkElem "InputLocale" typeString config.locale.input)
        (mkElem "SystemLocale" typeString config.locale.language)
        (mkElem "UILanguage" typeString config.locale.language)
        (mkElem "UserLocale" typeString config.locale.language)
      ])
    ])

    (settingsPass "generalize" [
      (component "Microsoft-Windows-Security-SPP" [
        (elem "SkipRearm" "1")
      ])
    ])

    (settingsPass "oobeSystem" [
      (component "Microsoft-Windows-International-Core" [
        (mkElem "InputLocale" typeString config.locale.input)
        (mkElem "UILanguage" typeString config.locale.language)
        (mkElem "UserLocale" typeString config.locale.language)
      ])

      (component "Microsoft-Windows-Shell-Setup" [
        (mkElem "RegisteredOwner" typeString config.userData.fullName)
        (mkElem "RegisteredOrganization" typeString config.userData.organization)
        (elem "DisableAutoDaylightTimeSet" "false")
        (elem "OOBE" [
          (elem "HideEULAPage" "true")
          (elem "HideWirelessSetupInOOBE" "true")
          (elem "NetworkLocation" "Home")
          (elem "ProtectYourPC" "3")
          (elem "SkipMachineOOBE" "true")
          (elem "SkipUserOOBE" "true")
        ])
        (list "FirstLogonCommands" "SynchronousCommand" (map (item: [
          (mkElem "Description" typeString item.description)
          (mkElem "CommandLine" typeString item.command)
          (mkElem "Order" typeInt item.order)
        ]) config.firstLogonCommands))
        # TODO make autologin reference the users list
        (if builtins.hasAttr "autoLogon" config then
          (elem "AutoLogon" [
            (elem "Enabled" "true")
            (mkElem "Username" typeString config.autoLogon.username)
            (elem "Password" [
              (mkElem "Value" typeString config.autoLogon.password)
              (elem "PlainText" "true")
            ])
          ])
          else
          (elem "AutoLogon" [ (elem "Enabled" "false") ])
        )
        (xml.elem "UserAccounts" [] [
          (list "LocalAccounts" "LocalAccount" (map (user: [
            (elem "DisplayName" user.displayName)
            (elem "Name" user.name)
            (elem "Group" user.group)
            (if user.password.enable then
              (elem "Password" [
                (elem "Value" user.password.value)
                (elem "PlainText" user.password.plainText)
              ])
              else
              (elem "Password" [
                (elem "Value" "")
                (elem "PlainText" "true")
              ])
            )
          ]) config.users))
        ])
      ])
    ])
  ];

  default = let
    user = {
      name = "idiot";
      displayName = "Idiot";
      group = "Administrators";
      password.enable = false;
    };
  in {
    name = user.displayName + "-PC";
    locale = {
      language = "en-US";
      timeZone = "Mountain Standard Time";
      input = "1033:00000409";
      showUI = false;
    };
    users = [ user ];
    skipAutoActivation = true;
    diskConfig = {
      showUI = false;
    };
    dynamicUpdate.showUI = false;
    userData = {
      acceptEula = true;
      fullName = user.displayName;
      organization = "Dumbass Inc.";
    };
    firstLogonCommands = [
      {
        order = 1;
        description = "Disable Auto Updates";
        command = "reg add \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\" /v AUOptions /t REG_DWORD /d 1 /f";
      }
      {
        order = 2;
        description = "Notify Host";
        command = "cmd.exe /C echo ready>COM1";
      }
    ];
    autoLogon = {
      username = "idiot";
      password = "";
    };
  };

  genConfig = config: builtins.concatStringsSep "" [
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
    (xml.toText (process config))
  ];
}