#if defined(Hiro_Window)

namespace hiro {

static const unsigned FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER | WS_CLIPCHILDREN;
static const unsigned ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN;

auto pWindow::construct() -> void {
  hwnd = CreateWindow(L"hiroWindow", L"", ResizableStyle, 128, 128, 256, 256, 0, 0, GetModuleHandle(0), 0);
  SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
  setDroppable(state().droppable);
  setGeometry({128, 128, 256, 256});

  windows.append(self().instance);
}

auto pWindow::destruct() -> void {
  if(auto position = windows.find(self().instance)) windows.remove(*position);

  if(hbrush) { DeleteObject(hbrush); hbrush = nullptr; }
  DestroyWindow(hwnd);
}

auto pWindow::append(sLayout layout) -> void {
}

auto pWindow::append(sMenuBar menuBar) -> void {
}

auto pWindow::append(sStatusBar statusBar) -> void {
}

auto pWindow::focused() const -> bool {
  return (GetForegroundWindow() == hwnd);
}

auto pWindow::frameMargin() const -> Geometry {
  unsigned style = state().resizable ? ResizableStyle : FixedStyle;
  if(state().fullScreen) style = 0;
  RECT rc{0, 0, 640, 480};
  AdjustWindowRect(&rc, style, (bool)GetMenu(hwnd));
  signed statusHeight = 0;
  if(auto statusBar = state().statusBar) {
    if(auto self = statusBar->self()) {
      if(statusBar->visible()) {
        RECT src;
        GetClientRect(self->hwnd, &src);
        statusHeight = src.bottom - src.top;
      }
    }
  }
  return {abs(rc.left), abs(rc.top), (rc.right - rc.left) - 640, (rc.bottom - rc.top) + statusHeight - 480};
}

auto pWindow::remove(sLayout layout) -> void {
}

auto pWindow::remove(sMenuBar menuBar) -> void {
}

auto pWindow::remove(sStatusBar statusBar) -> void {
}

auto pWindow::setBackgroundColor(Color color) -> void {
  hbrushColor = CreateRGB(color);
  if(hbrush) { DeleteObject(hbrush); hbrush = nullptr; }
  if(color) hbrush = CreateSolidBrush(hbrushColor);
}

auto pWindow::setDroppable(bool droppable) -> void {
  DragAcceptFiles(hwnd, droppable);
}

auto pWindow::setEnabled(bool enabled) -> void {
  if(auto layout = state().layout) {
    if(auto self = layout->self()) self->setEnabled(layout->enabled(true));
  }
}

auto pWindow::setFocused() -> void {
  if(!self().visible()) self().setVisible(true);
  SetFocus(hwnd);
}

auto pWindow::setFont(const Font& font) -> void {
  if(auto layout = state().layout) {
    if(auto self = layout->self()) self->setFont(layout->font(true));
  }
}

auto pWindow::setFullScreen(bool fullScreen) -> void {
  auto style = GetWindowLongPtr(hwnd, GWL_STYLE) & WS_VISIBLE;
  lock();
  if(fullScreen) {
    windowedGeometry = self().geometry();
    HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
    MONITORINFOEX info;
    memset(&info, 0, sizeof(MONITORINFOEX));
    info.cbSize = sizeof(MONITORINFOEX);
    GetMonitorInfo(monitor, &info);
    RECT rc = info.rcMonitor;
    Geometry geometry = {rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top};
    SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_POPUP);
    Geometry margin = frameMargin();
    self().setGeometry({
      geometry.x() + margin.x(), geometry.y() + margin.y(),
      geometry.width() - margin.width(), geometry.height() - margin.height()
    });
  } else {
    SetWindowLongPtr(hwnd, GWL_STYLE, style | (state().resizable ? ResizableStyle : FixedStyle));
    self().setGeometry(windowedGeometry);
  }
  unlock();
}

auto pWindow::setGeometry(Geometry geometry) -> void {
  lock();
  Geometry margin = frameMargin();
  SetWindowPos(
    hwnd, nullptr,
    geometry.x() - margin.x(), geometry.y() - margin.y(),
    geometry.width() + margin.width(), geometry.height() + margin.height(),
    SWP_NOZORDER | SWP_FRAMECHANGED
  );
  if(auto statusBar = state().statusBar) {
    if(auto self = statusBar->self()) {
      SetWindowPos(self->hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED);
    }
  }
  if(auto layout = state().layout) {
    layout->setGeometry(geometry.setPosition(0, 0));
  }
  unlock();
}

auto pWindow::setModal(bool modality) -> void {
  if(modality) {
    _modalityUpdate();
    while(state().modal) {
      Application::processEvents();
      if(Application::state.onMain) {
        Application::doMain();
      } else {
        usleep(20 * 1000);
      }
    }
    _modalityUpdate();
  }
}

auto pWindow::setResizable(bool resizable) -> void {
  auto style = GetWindowLongPtr(hwnd, GWL_STYLE) & WS_VISIBLE;
  SetWindowLongPtr(hwnd, GWL_STYLE, style | (state().resizable ? ResizableStyle : FixedStyle));
  setGeometry(state().geometry);
}

auto pWindow::setTitle(string text) -> void {
  SetWindowText(hwnd, utf16_t(text));
}

auto pWindow::setVisible(bool visible) -> void {
  ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE);
  if(!visible) setModal(false);

  if(auto layout = state().layout) {
    if(auto self = layout->self()) self->setVisible(layout->visible(true));
  }
}

//

auto pWindow::onClose() -> void {
  if(state().onClose) self().doClose();
  else self().setVisible(false);
  if(state().modal && !self().visible()) self().setModal(false);
}

auto pWindow::onDrop(WPARAM wparam) -> void {
  auto paths = DropPaths(wparam);
  if(paths) self().doDrop(paths);
}

auto pWindow::onEraseBackground() -> bool {
  if(hbrush == 0) return false;
  RECT rc;
  GetClientRect(hwnd, &rc);
  PAINTSTRUCT ps;
  BeginPaint(hwnd, &ps);
  FillRect(ps.hdc, &rc, hbrush);
  EndPaint(hwnd, &ps);
  return true;
}

auto pWindow::onModalBegin() -> void {
  Application::Windows::doModalChange(true);
}

auto pWindow::onModalEnd() -> void {
  Application::Windows::doModalChange(false);
}

auto pWindow::onMove() -> void {
  if(locked()) return;
  state().geometry.setPosition(_geometry().position());
  self().doMove();
}

auto pWindow::onSize() -> void {
  if(locked()) return;
  if(auto statusBar = state().statusBar) {
    if(auto self = statusBar->self()) {
      SetWindowPos(self->hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED);
    }
  }
  state().geometry.setSize(_geometry().size());
  if(auto layout = state().layout) {
    layout->setGeometry(_geometry().setPosition(0, 0));
  }
  self().doSize();
}

//

auto pWindow::_geometry() -> Geometry {
  Geometry margin = frameMargin();

  RECT rc;
  if(IsIconic(hwnd)) {
    //GetWindowRect returns -32000(x),-32000(y) when window is minimized
    WINDOWPLACEMENT wp;
    GetWindowPlacement(hwnd, &wp);
    rc = wp.rcNormalPosition;
  } else {
    GetWindowRect(hwnd, &rc);
  }

  signed x = rc.left + margin.x();
  signed y = rc.top + margin.y();
  signed width = (rc.right - rc.left) - margin.width();
  signed height = (rc.bottom - rc.top) - margin.height();

  return {x, y, width, height};
}

auto pWindow::_modalityCount() -> unsigned {
  unsigned modalWindows = 0;
  for(auto& weak : windows) {
    if(auto object = weak.acquire()) {
      if(auto window = dynamic_cast<mWindow*>(object.data())) {
        if(window->modal()) modalWindows++;
      }
    }
  }
  return modalWindows;
}

auto pWindow::_modalityDisabled() -> bool {
  if(_modalityCount() == 0) return false;
  return !state().modal;
}

auto pWindow::_modalityUpdate() -> void {
  unsigned modalWindows = _modalityCount();
  for(auto& weak : windows) {
    if(auto object = weak.acquire()) {
      if(auto window = dynamic_cast<mWindow*>(object.data())) {
        if(auto self = window->self()) {
          bool enabled = !modalWindows || window->modal();
          if(IsWindowEnabled(self->hwnd) != enabled) {
            EnableWindow(self->hwnd, enabled);
          }
        }
      }
    }
  }
}

}

#endif
