wintf 0.0.2

Windows Tategaki Framework - Rust UI library with Japanese vertical text support
use crate::com::d2d::*;
use crate::com::d3d11::*;
use crate::com::dwrite::*;
use bevy_ecs::prelude::*;
use tracing::{debug, info};
use windows::Win32::Foundation::*;
use windows::Win32::Graphics::Direct2D::*;
use windows::Win32::Graphics::Direct3D::*;
use windows::Win32::Graphics::Direct3D11::*;
use windows::Win32::Graphics::DirectWrite::*;
use windows::Win32::Graphics::Dxgi::*;
use windows::core::{Interface, Result};

#[derive(Debug)]
struct GraphicsCoreInner {
    pub d3d: ID3D11Device,
    pub dxgi: IDXGIDevice4,
    pub d2d_factory: ID2D1Factory,
    pub d2d: ID2D1Device,
    pub d2d_device_context: ID2D1DeviceContext, // グローバル共有DeviceContext
    pub dwrite_factory: IDWriteFactory2,
}

#[derive(Resource, Debug)]
pub struct GraphicsCore {
    inner: Option<GraphicsCoreInner>,
}

// SAFETY 条件: 保持する COM ポインタの跨スレッド移動(Send)・参照共有(Sync)の健全性は
// 各 API の同期特性に依拠する。d2d_factory は D2D1_FACTORY_TYPE_MULTI_THREADED で生成され、
// 同一ファクトリ系列のオブジェクト(d2d / d2d_device_context)への COM 呼び出しは D2D が
// 内部で直列化する。d3d は D3D11_CREATE_DEVICE_SINGLETHREADED を指定せず生成されるため
// ID3D11Device はスレッドセーフ(dxgi は同一オブジェクトへの別インターフェース)。
// dwrite_factory(DWRITE_FACTORY_TYPE_SHARED)もスレッドセーフ。
// よって全フィールドが内部同期を持ち、外部同期前提の DComp 系(dcomp_resource.rs)とは
// 異なり ECS スケジュール構成への追加前提を要しない。
unsafe impl Send for GraphicsCore {}
unsafe impl Sync for GraphicsCore {}

impl GraphicsCore {
    pub fn new() -> Result<Self> {
        info!("[GraphicsCore] Initialization started");

        let d3d = create_device_3d()?;
        let dxgi = d3d.cast()?;
        let d2d_factory = create_d2d_factory()?;
        let d2d = d2d_create_device(&dxgi)?;

        // グローバル共有DeviceContextを作成
        let d2d_device_context = d2d.create_device_context(D2D1_DEVICE_CONTEXT_OPTIONS_NONE)?;
        debug!("[GraphicsCore] Global DeviceContext created");

        let dwrite_factory = dwrite_create_factory(DWRITE_FACTORY_TYPE_SHARED)?;

        info!("[GraphicsCore] Initialization completed");

        Ok(Self {
            inner: Some(GraphicsCoreInner {
                d3d,
                dxgi,
                d2d_factory,
                d2d,
                d2d_device_context,
                dwrite_factory,
            }),
        })
    }

    pub fn invalidate(&mut self) {
        self.inner = None;
    }

    pub fn is_valid(&self) -> bool {
        self.inner.is_some()
    }

    pub fn d2d_factory(&self) -> Option<&ID2D1Factory> {
        self.inner.as_ref().map(|i| &i.d2d_factory)
    }

    pub fn d2d_device(&self) -> Option<&ID2D1Device> {
        self.inner.as_ref().map(|i| &i.d2d)
    }

    pub fn dwrite_factory(&self) -> Option<&IDWriteFactory2> {
        self.inner.as_ref().map(|i| &i.dwrite_factory)
    }

    /// グローバル共有DeviceContextへの参照を取得
    pub fn device_context(&self) -> Option<&ID2D1DeviceContext> {
        self.inner.as_ref().map(|i| &i.d2d_device_context)
    }

    pub fn d3d(&self) -> Option<&ID3D11Device> {
        self.inner.as_ref().map(|i| &i.d3d)
    }

    pub fn dxgi(&self) -> Option<&IDXGIDevice4> {
        self.inner.as_ref().map(|i| &i.dxgi)
    }
}

/// D2DFactoryを作成(マルチスレッド対応)
fn create_d2d_factory() -> Result<ID2D1Factory> {
    #[allow(unused_imports)]
    use windows::Win32::Graphics::Direct2D::Common::*;

    unsafe { D2D1CreateFactory::<ID2D1Factory>(D2D1_FACTORY_TYPE_MULTI_THREADED, None) }
}

fn create_device_3d() -> Result<ID3D11Device> {
    #[cfg(debug_assertions)]
    let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;

    #[cfg(not(debug_assertions))]
    let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

    d3d11_create_device(
        None,
        D3D_DRIVER_TYPE_HARDWARE,
        HMODULE::default(),
        flags,
        None,
        D3D11_SDK_VERSION,
        None,
        None,
    )
}

// ============================================================
// FrameTime - 高精度フレーム時刻リソース
// ============================================================

/// FrameTime - 高精度フレーム時刻リソース(f64 秒、OS起動時=0秒)
///
/// `FrameCount` と同じ設計パターン。ワールド外(`try_tick_world()` 実行前)で
/// `dola::runtime::clock::now()` を呼び出して値を更新する。
/// ワールド内の全システムは同じフレームの時刻値(`.0`)を参照する。
///
/// 利点:
/// - フレーム内でタイミングが完全に一貫
/// - テスト時に任意の時刻を注入可能
/// - システム実行中に時刻が進まない(安定性向上)
///
/// 時刻基準: `QueryPerformanceCounter / QueryPerformanceFrequency`
#[derive(Resource, Default, Debug, Clone, Copy)]
pub struct FrameTime(pub f64);