direct2d 0.2.0

A safe abstraction for drawing with Direct2D
use enums::{AlphaMode, FeatureLevel, PresentOptions, RenderTargetType, RenderTargetUsage};
use error::D2DResult;
use factory::Factory;
use math;
use render_target::{GenericRenderTarget, RenderTarget};

use std::ptr;

use dxgi::Format;
use winapi::shared::windef::HWND;
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::d2d1::{D2D1_HWND_RENDER_TARGET_PROPERTIES, D2D1_RENDER_TARGET_PROPERTIES,
                       D2D1_SIZE_U, ID2D1HwndRenderTarget, ID2D1RenderTarget};
use winapi::um::dcommon::D2D1_PIXEL_FORMAT;
use wio::com::ComPtr;

#[derive(Clone)]
pub struct HwndRenderTarget {
    ptr: ComPtr<ID2D1HwndRenderTarget>,
}

impl HwndRenderTarget {
    #[inline]
    pub fn create<'a>(factory: &'a Factory) -> HwndRenderTargetBuilder<'a> {
        HwndRenderTargetBuilder::new(factory)
    }

    #[inline]
    pub fn as_generic(&self) -> GenericRenderTarget {
        unsafe {
            let ptr = self.get_raw();
            (*ptr).AddRef();
            GenericRenderTarget::from_raw(ptr as *mut _)
        }
    }

    #[inline]
    pub fn resize(&self, pixel_size: math::SizeU) -> D2DResult<()> {
        unsafe {
            let hr = self.ptr.Resize(&pixel_size.0);
            if SUCCEEDED(hr) {
                Ok(())
            } else {
                Err(hr.into())
            }
        }
    }

    #[inline]
    pub fn get_hwnd(&self) -> HWND {
        unsafe { self.ptr.GetHwnd() }
    }

    #[inline]
    pub unsafe fn from_ptr(ptr: ComPtr<ID2D1HwndRenderTarget>) -> Self {
        Self { ptr }
    }

    #[inline]
    pub unsafe fn from_raw(raw: *mut ID2D1HwndRenderTarget) -> Self {
        HwndRenderTarget {
            ptr: ComPtr::from_raw(raw),
        }
    }

    #[inline]
    pub unsafe fn get_raw(&self) -> *mut ID2D1HwndRenderTarget {
        self.ptr.as_raw()
    }
}

impl RenderTarget for HwndRenderTarget {
    #[inline]
    unsafe fn rt<'a>(&self) -> &'a mut ID2D1RenderTarget {
        &mut *(self.ptr.as_raw() as *mut _)
    }
}

unsafe impl Send for HwndRenderTarget {}
unsafe impl Sync for HwndRenderTarget {}

pub struct HwndRenderTargetBuilder<'a> {
    factory: &'a Factory,
    rt_props: D2D1_RENDER_TARGET_PROPERTIES,
    hwnd_props: D2D1_HWND_RENDER_TARGET_PROPERTIES,
}

const DEFAULT_PROPS: D2D1_RENDER_TARGET_PROPERTIES = D2D1_RENDER_TARGET_PROPERTIES {
    _type: RenderTargetType::Hardware as u32,
    pixelFormat: D2D1_PIXEL_FORMAT {
        format: Format::B8G8R8A8Unorm as u32,
        alphaMode: AlphaMode::Premultiplied as u32,
    },
    dpiX: 0.0,
    dpiY: 0.0,
    usage: 0,
    minLevel: 0,
};

const DEFAULT_HWND_PROPS: D2D1_HWND_RENDER_TARGET_PROPERTIES = D2D1_HWND_RENDER_TARGET_PROPERTIES {
    hwnd: ptr::null_mut(),
    pixelSize: D2D1_SIZE_U {
        width: 0,
        height: 0,
    },
    presentOptions: 0,
};

impl<'a> HwndRenderTargetBuilder<'a> {
    #[inline]
    pub fn new(factory: &'a Factory) -> Self {
        HwndRenderTargetBuilder {
            factory,
            rt_props: DEFAULT_PROPS,
            hwnd_props: DEFAULT_HWND_PROPS,
        }
    }

    #[inline]
    pub fn build(self) -> D2DResult<HwndRenderTarget> {
        unsafe {
            let mut ptr = ptr::null_mut();
            let hr = (*self.factory.get_raw()).CreateHwndRenderTarget(
                &self.rt_props,
                &self.hwnd_props,
                &mut ptr,
            );

            if SUCCEEDED(hr) {
                Ok(HwndRenderTarget::from_raw(ptr))
            } else {
                Err(hr.into())
            }
        }
    }

    #[inline]
    pub fn with_target_type(mut self, target_type: RenderTargetType) -> Self {
        self.rt_props._type = target_type as u32;
        self
    }

    #[inline]
    pub fn with_format(mut self, format: Format) -> Self {
        self.rt_props.pixelFormat.format = format as u32;
        self
    }

    #[inline]
    pub fn with_alpha_mode(mut self, alpha_mode: AlphaMode) -> Self {
        self.rt_props.pixelFormat.alphaMode = alpha_mode as u32;
        self
    }

    #[inline]
    pub fn with_dpi(mut self, dpi_x: f32, dpi_y: f32) -> Self {
        self.rt_props.dpiX = dpi_x;
        self.rt_props.dpiY = dpi_y;
        self
    }

    #[inline]
    pub fn with_usage(mut self, usage: RenderTargetUsage) -> Self {
        self.rt_props.usage = usage.0;
        self
    }

    #[inline]
    pub fn with_feature_level(mut self, level: FeatureLevel) -> Self {
        self.rt_props.minLevel = level as u32;
        self
    }

    #[inline]
    pub fn with_hwnd(mut self, hwnd: HWND) -> Self {
        self.hwnd_props.hwnd = hwnd;
        self
    }

    #[inline]
    pub fn with_pixel_size(mut self, width: u32, height: u32) -> Self {
        self.hwnd_props.pixelSize.width = width;
        self.hwnd_props.pixelSize.height = height;
        self
    }

    #[inline]
    pub fn with_present_options(mut self, options: PresentOptions) -> Self {
        self.hwnd_props.presentOptions = options.0;
        self
    }
}