direct2d 0.2.0

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

use std::ptr;

use dxgi::surface::Surface as DxgiSurface;
use dxgi::Format;
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::d2d1::{D2D1_RENDER_TARGET_PROPERTIES, ID2D1RenderTarget};
use winapi::um::dcommon::D2D1_PIXEL_FORMAT;
use wio::com::ComPtr;

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

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

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

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

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

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

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

unsafe impl Send for DxgiSurfaceRenderTarget {}
unsafe impl Sync for DxgiSurfaceRenderTarget {}

pub struct DxgiSurfaceRenderTargetBuilder<'a> {
    factory: &'a Factory,
    surface: Option<&'a DxgiSurface>,
    rt_props: D2D1_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::Unknown as u32,
        alphaMode: AlphaMode::Premultiplied as u32,
    },
    dpiX: 0.0,
    dpiY: 0.0,
    usage: 0,
    minLevel: 0,
};

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

    #[inline]
    pub fn build(self) -> D2DResult<DxgiSurfaceRenderTarget> {
        let surface = self.surface.expect("`surface` must be specified");
        unsafe {
            let mut ptr = ptr::null_mut();
            let hr = (*self.factory.get_raw()).CreateDxgiSurfaceRenderTarget(
                surface.get_raw(),
                &self.rt_props,
                &mut ptr,
            );

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

    #[inline]
    pub fn with_surface(mut self, surface: &'a DxgiSurface) -> Self {
        self.surface = Some(surface);
        self
    }

    #[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
    }
}