/*
 * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
 */

#include <nostalgia/core/consts.hpp>
#include <nostalgia/core/media.hpp>
#include <nostalgia/geo/point.hpp>

#include "tilesheeteditorview.hpp"

namespace nostalgia::core {

TileSheetEditorView::TileSheetEditorView(Context *ctx, const ox::String &path): m_model(ctx, path), m_pixelsDrawer(&m_model) {
	// build shaders
	oxThrowError(m_pixelsDrawer.buildShader());
	oxThrowError(m_pixelGridDrawer.buildShader());
	m_model.activeSubsheetChanged.connect(this, &TileSheetEditorView::setActiveSubsheet);
}

void TileSheetEditorView::draw() noexcept {
	constexpr Color32 bgColor = 0x717d7e;
	glClearColor(redf(bgColor), greenf(bgColor), bluef(bgColor), 1.f);
	glClear(GL_COLOR_BUFFER_BIT);
	m_pixelsDrawer.draw(updated(), m_scrollOffset);
	m_pixelGridDrawer.draw(updated(), m_scrollOffset);
}

void TileSheetEditorView::scrollV(const geo::Vec2 &paneSz, float wheel, bool zoomMod) noexcept {
	const auto pixelSize = m_pixelsDrawer.pixelSize(paneSz);
	const ImVec2 sheetSize(pixelSize.x * static_cast<float>(m_model.activeSubSheet()->columns) * TileWidth,
	                       pixelSize.y * static_cast<float>(m_model.activeSubSheet()->rows) * TileHeight);
	if (zoomMod) {
		m_pixelSizeMod = ox::clamp(m_pixelSizeMod + wheel * 0.02f, 0.55f, 2.f);
		m_pixelsDrawer.setPixelSizeMod(m_pixelSizeMod);
		m_pixelGridDrawer.setPixelSizeMod(m_pixelSizeMod);
		m_updated = true;
	} else {
		m_scrollOffset.y -= wheel * 0.1f;
	}
	// adjust scroll offset in both cases because the image can be zoomed
	// or scrolled off screen
	m_scrollOffset.y = ox::clamp(m_scrollOffset.y, 0.f, sheetSize.y / 2);
}

void TileSheetEditorView::scrollH(const geo::Vec2 &paneSz, float wheelh) noexcept {
	const auto pixelSize = m_pixelsDrawer.pixelSize(paneSz);
	const ImVec2 sheetSize(pixelSize.x * static_cast<float>(m_model.activeSubSheet()->columns) * TileWidth,
	                       pixelSize.y * static_cast<float>(m_model.activeSubSheet()->rows) * TileHeight);
	m_scrollOffset.x += wheelh * 0.1f;
	m_scrollOffset.x = ox::clamp(m_scrollOffset.x, -(sheetSize.x / 2), 0.f);
}

void TileSheetEditorView::insertTile(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept {
	const auto pt = clickPoint(paneSize, clickPos);
	const auto s = m_model.activeSubSheet();
	const auto tileIdx = ptToIdx(pt, s->columns) / PixelsPerTile;
	m_model.insertTiles(m_model.activeSubSheetIdx(), tileIdx, 1);
}

void TileSheetEditorView::deleteTile(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept {
	const auto pt = clickPoint(paneSize, clickPos);
	const auto s = m_model.activeSubSheet();
	const auto tileIdx = ptToIdx(pt, s->columns) / PixelsPerTile;
	m_model.deleteTiles(m_model.activeSubSheetIdx(), tileIdx, 1);
}

void TileSheetEditorView::clickDraw(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept {
	const auto pt = clickPoint(paneSize, clickPos);
	m_model.drawCommand(pt, m_palIdx);
}

void TileSheetEditorView::clickSelect(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept {
	const auto pt = clickPoint(paneSize, clickPos);
	m_model.select(pt);
}

void TileSheetEditorView::clickFill(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) noexcept {
	const auto pt = clickPoint(paneSize, clickPos);
	m_model.fill(pt, m_palIdx);
}

void TileSheetEditorView::releaseMouseButton() noexcept {
	m_model.endDrawCommand();
	m_model.completeSelection();
}

void TileSheetEditorView::resizeView(const geo::Vec2 &sz) noexcept {
	m_viewSize = sz;
	initView();
}

bool TileSheetEditorView::updated() const noexcept {
	return m_updated || m_model.updated();
}

ox::Error TileSheetEditorView::markUpdated() noexcept {
	m_updated = true;
	return OxError(0);
}

void TileSheetEditorView::ackUpdate() noexcept {
	m_updated = false;
	m_model.ackUpdate();
}

void TileSheetEditorView::initView() noexcept {
	m_pixelsDrawer.initBufferSet(m_viewSize);
	m_pixelGridDrawer.initBufferSet(m_viewSize, *m_model.activeSubSheet());
}

geo::Point TileSheetEditorView::clickPoint(const geo::Vec2 &paneSize, const geo::Vec2 &clickPos) const noexcept {
	auto [x, y] = clickPos;
	const auto pixDrawSz = m_pixelsDrawer.pixelSize(paneSize);
	x /= paneSize.x;
	y /= paneSize.y;
	x += -m_scrollOffset.x / 2;
	y += m_scrollOffset.y / 2;
	x /= pixDrawSz.x;
	y /= pixDrawSz.y;
	return {static_cast<int>(x * 2), static_cast<int>(y * 2)};
}

ox::Error TileSheetEditorView::setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept {
	initView();
	return OxError(0);
}

}
