import xml.etree.ElementTree as ET
from os.path import realpath, dirname

import basetype
import handle
import enum
import struct
import typeid
import commands
import funcptr
import features
import exttypes


tree = ET.parse(dirname(realpath(__file__))+"/vk.xml")
root = tree.getroot()


types = typeid.Types(root)

enum.parse_enums(root, types)
apiconst = enum.parse_api_constants(root)
struct.parse_impldbg(types)

cmds = commands.Commands(root)



features.require_feature(root, "VK_VERSION_1_0", types, cmds)
features.require_feature(root, "VK_VERSION_1_1", types, cmds)

exceptext = []

extnames = typeid.Type("EXTENSION_NAMES", "enum", enum.En("", []), [])
for ext in root.findall("./extensions/extension"):
    if features.is_empty_extension(ext): continue
    if ext.attrib["name"] in exceptext: continue
    features.require_extension(root, ext.attrib["name"], types, cmds, extnames)



f = open(dirname(realpath(__file__)) + "/../src/lib.rs", "w")

def write_crate_doc():
    s = []
    s.append("# nobs-vk\n")
    s.append("no bullshit vulkan bindings.\n")
    s.append("\n")
    s.append("This crate is auto generated by python scripts and provides types, constants and functions for [vulkan](https://www.khronos.org/vulkan/).\n")
    s.append("\n")
    s.append("1. [Existential questions](#existential-questions)\n")
    s.append("2. [Examples](#examples)\n")
    s.append("    1. [Vulkan core initialisation](#vulkan-core-initialisation)\n")
    s.append("    2. [Instance extension loading](#instance-extension-loading)\n")
    s.append("3. [Details](#details)\n")
    s.append("    1. [Namespaces](#namespaces)\n")
    s.append("    2. [Function pointers](#function-pointers)\n")
    s.append("    3. [Traits for rich instance and device wrapper](#traits-for-rich-instance-and-device-wrapper)\n")
    s.append("\n")
    s.append("## Existential questions\n")
    s.append("Why does nobs-vk exists? nobs-vk...\n")
    s.append("1. is used how the vulkan api is documented\n")
    s.append("2. is auto generated from a python script that sources the vk.xml from the vulkan registry\n")
    s.append("3. gives you the freedom to do as you please in your design decisions (but therefore doesn't protect you from your own stupidity)\n")
    s.append("4. is not a full blown window creating bloat library in the back, just to execute some small compute shader with a headless vulkan build\n")
    s.append("\n")
    s.append("While more involved wrappers for vulkan do exist they also strife to completely hide the vulkan api behind another layer of rust code and might force you into design decisions you would normally try to avoid. This library tries to be as simple as possible and just exposes callable functions to vulkan.\n")
    s.append("\n")
    s.append("## Examples\n")
    s.append("### Vulkan core initialisation\n")
    s.append("This is a simple example that retrieves the version of vulkan that is installed on the system\n")
    s.append("```rust\n")
    s.append("#[macro_use] extern crate nobs_vk as vk;\n")
    s.append("//...\n")
    s.append("\n")
    s.append("# fn main() {\n")
    s.append("// loads vulkan core\n")
    s.append("let _vk_lib = vk::Core::new();\n")
    s.append("\n")
    s.append("// good to go from here, we can use any vulkan function that is supported by this system\n")
    s.append("// make sure _vk_lib lives throughout the time that vulkan is used and is dropped afterwards\n")
    s.append("\n")
    s.append("// global vulkan version\n")
    s.append("let mut inst_ver: u32 = 0;\n")
    s.append("if vk::EnumerateInstanceVersion(&mut inst_ver) != vk::SUCCESS { \n")
    s.append("  panic!(\"something went terribly wrong\");\n")
    s.append("}\n")
    s.append("\n")
    s.append("assert_eq!(1, version_major!(inst_ver));\n")
    s.append("assert_eq!(1, version_minor!(inst_ver));\n")
    s.append("assert_eq!(0, version_patch!(inst_ver));\n")
    s.append("# }\n")
    s.append("```\n")
    s.append("\n")
    s.append("### Instance extension loading\n")
    s.append("After we created a vulkan instance we can load extensions\n")
    s.append("```rust\n")
    s.append("#[macro_use] extern crate nobs_vk as vk;\n")
    s.append("use std::ffi::CString;\n")
    s.append("use std::ffi::CStr;\n")
    s.append("use std::ptr;\n")
    s.append("use std::os::raw::c_char;\n")
    s.append("# fn main() {\n")
    s.append("// ...\n")
    s.append("let _vk_lib = vk::Core::new();\n")
    s.append("\n")
    s.append("// Define some extensions and convert them to c-strings\n")
    s.append("let ext_names = vec![vk::KHR_SURFACE_EXTENSION_NAME, vk::KHR_XLIB_SURFACE_EXTENSION_NAME];\n")
    s.append("let ext_names_cstr = ext_names\n")
    s.append("  .iter()\n")
    s.append("  .map(|e| CString::new(*e).unwrap())\n")
    s.append("  .collect::<Vec<CString>>();\n")
    s.append("let ext_names_ptr = ext_names_cstr\n")
    s.append("  .iter()\n")
    s.append("  .map(|e| e.as_ptr())\n")
    s.append("  .collect::<Vec<*const c_char>>();\n")
    s.append("\n")
    s.append("// create the instance\n")
    s.append("let appinfo = vk::InstanceCreateInfo {\n")
    s.append("  sType: vk::STRUCTURE_TYPE_INSTANCE_CREATE_INFO,\n")
    s.append("  pNext: ptr::null(),\n")
    s.append("  flags: 0,\n")
    s.append("  pApplicationInfo: ptr::null(),\n")
    s.append("  enabledLayerCount: 0,\n")
    s.append("  ppEnabledLayerNames: ptr::null(),\n")
    s.append("  enabledExtensionCount: ext_names.len() as u32,   // <- extension names go here\n")
    s.append("  ppEnabledExtensionNames: ext_names_ptr.as_ptr(), // <-\n")
    s.append("};\n")
    s.append("\n")
    s.append("let mut inst = vk::NULL_HANDLE;\n")
    s.append("vk::CreateInstance(&appinfo, ptr::null(), &mut inst);\n")
    s.append("\n")
    s.append("// not an extension, so we can call it no matter what\n")
    s.append("let mut num_devices: u32 = 0;\n")
    s.append("vk::EnumeratePhysicalDevices(inst, &mut num_devices, ptr::null_mut());\n")
    s.append("println!(\"num devices:  {}\", num_devices);\n")
    s.append("\n")
    s.append("// load extensions\n")
    s.append("// note that only extensions are loaded, that have been specified in ext_names\n")
    s.append("let ie = vk::InstanceExtensions::new(inst);\n")
    s.append("// we can now use instence extensions, e.g.:\n")
    s.append("// ie.CreateXlibSurfaceKHR(...)\n")
    s.append("\n")
    s.append("// don't forget to clean up\n")
    s.append("vk::DestroyInstance(inst, ptr::null());\n")
    s.append("# }\n")
    s.append("```\n")
    s.append("\n")
    s.append("## Details\n")
    s.append("### Function pointers\n")
    s.append("Entry points to vulkan commands are placed in objects containing funcion pointes. After loading the function pointers contain the address to the respective vulkan API call. Vulkan commands are either core or extension. We provide two different mechanisms to load function pointers:\n")
    s.append("\n")
    s.append("**Core commands** are loaded with [nobs_vk::Core::new](struct.Core.html#method.new) or [nobs_vk::Core::with_version](struct.Core.html#method.with_version). The returned object holds entry points to core vulkan commands and defines member function to call them. Additionaly global functions in the `nobs_vk` namespace are defined that may be called as long as a valid instance of `nobs_vk::Core` exists. *Warning: do not create multiple instances of `nobs_vk::Core`.*\n")
    s.append("\n")
    s.append("**Instance and device extensions** are loaded with\n")
    s.append("`nobs_vk::[InstanceExtensions|DeviceExtensions]`. The two types hold entry points to vulkan extensions and define member functions to call them. Only those extension are loaded, that have been specified during instance/device creation (`enabledExtensionCount` and `ppEnabledExtensionNames` fields of fields of [VkInstanceCreateInfo](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkInstanceCreateInfo.html) and [VkDeviceCreateInfo](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkDeviceCreateInfo.html)).\n")
    s.append("\n")
    s.append("Objects with function pointes define all commands that are listed the vulkan reference. In case a specific feature level is selected as well as platform or hardware vendor specific extensions the definitions for the functions or methods are not removed. However calling such a function, that is not supported will always cause a panic.\n")
    s.append("### Namespaces\n")
    s.append("Name prefixes of the C-types, enums and functions are removed in favor of the module namespace. \n")
    s.append("For example `VK_Result` becomes `vk::Result`, `VK_SUCCESS` becomes `vk::SUCCESS`, `vkCreateInstance()` becomes `vk::CreateInstance()`.\n")
    s.append("\n")
    s.append("### Instance and device wrapper\n")
    s.append("This library introduces exactly one feature for more convenience while using the API:  the traits [InstanceWreapper](trait.InstanceWrapper.html) and [DeviceWreapper](trait.DeviceWrapper.html).\n")
    s.append("\n")
    s.append("With these two traits we divide vulkan commands into functions, that need a valid device and those that do not. Vulkan commands are then attached to an object as methods of the respective trait. Such a richer wrapper around a vulkan instance or device can be easily implemented by only providing a single method for the trait.\n")
    s.append("\n")
    s.append("## Vulkan reference\n")
    s.append("For documentation of the defined enums, structs and funcions see the \n")
    s.append("[vulkan reference](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/).\n")
    return "//! " + "//! ".join(s)
f.write(write_crate_doc() + "\n\n")

f.write("#![allow(non_upper_case_globals)]\n")
f.write("#![allow(non_snake_case)]\n")
f.write("#![allow(non_camel_case_types)]\n")
f.write("\n")
f.write("use std::os::raw::c_char;\n")
f.write("use std::os::raw::c_ushort;\n")
f.write("use std::os::raw::c_ulong;\n")
f.write("use std::os::raw::c_void;\n")
f.write("use std::mem;\n")
f.write("\n")

f.write("\n")
f.write("\n")

for t in types.get_types(["basetype"], True):
    f.write(basetype.write_basetype(types, t))

f.write("\n")
f.write("\n")
f.write(enum.write_enum(types, apiconst))

f.write("\n")
f.write("\n")
f.write(enum.write_enum(types, extnames))

f.write("\n")
f.write("\n")

f.write(handle.write_null_handle())
for t in types.get_types(["handle"], True):
    f.write(handle.write_handle(types, t))

f.write("\n")
f.write("\n")

for t in types.get_types(["funcpointer"], True):
    f.write(funcptr.write_ptr(t) + "\n")

f.write("\n")
f.write("\n")

for t in types.get_types(["bitmask", "enum"], True):
    f.write(enum.write_enum(types, t) + "\n")

f.write("\n")
f.write("\n")

for t in types.get_types(["struct", "union"], True):
    f.write(struct.write_struct(types, t))

f.write("\n")
f.write("\n")

for t in types.get_types(["exttype"], True):
    f.write(exttypes.write_type(types, t))

f.write("\n")
f.write("\n")

f.write(cmds.write(types))

f.write("\n")
f.close()



