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

#include <imgui.h>

#include "clawviewer.hpp"

namespace studio {

ClawEditor::ClawEditor(Context &sctx, ox::StringParam path):
	Editor(sctx, std::move(path)),
	m_obj(sctx.project->loadObj<ox::ModelObject>(itemPath()).unwrapThrow()) {
}

void ClawEditor::draw(Context&) noexcept {
	ImGui::BeginChild("PaletteEditor");
	static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
	if (ImGui::BeginTable("ObjTree", 3, flags)) {
		ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 100);
		ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 250);
		ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoHide);
		ImGui::TableHeadersRow();
		ObjPath objPath;
		drawTree(objPath, m_obj);
		ImGui::EndTable();
	}
	ImGui::EndChild();
}

void ClawEditor::drawRow(ox::ModelValue const &value) noexcept {
	using Str = ox::BasicString<100>;
	Str val, type;
	switch (value.type()) {
		case ox::ModelValue::Type::Undefined:
			val = "undefined";
			type = "undefined";
			break;
		case ox::ModelValue::Type::Bool:
			val = value.get<bool>() ? "true" : "false";
			type = "bool";
			break;
		case ox::ModelValue::Type::UnsignedInteger8:
			val = ox::sfmt<Str>("{}", value.get<uint8_t>());
			type = "uint8";
			break;
		case ox::ModelValue::Type::UnsignedInteger16:
			val = ox::sfmt<Str>("{}", value.get<uint16_t>());
			type = "uint16";
			break;
		case ox::ModelValue::Type::UnsignedInteger32:
			val = ox::sfmt<Str>("{}", value.get<uint32_t>());
			type = "uint32";
			break;
		case ox::ModelValue::Type::UnsignedInteger64:
			val = ox::sfmt<Str>("{}", value.get<uint64_t>());
			type = "uint64";
			break;
		case ox::ModelValue::Type::SignedInteger8:
			val = ox::sfmt<Str>("{}", value.get<int8_t>());
			type = "int8";
			break;
		case ox::ModelValue::Type::SignedInteger16:
			val = ox::sfmt<Str>("{}", value.get<int16_t>());
			type = "int16";
			break;
		case ox::ModelValue::Type::SignedInteger32:
			val = ox::sfmt<Str>("{}", value.get<int32_t>());
			type = "int32";
			break;
		case ox::ModelValue::Type::SignedInteger64:
			val = ox::sfmt<Str>("{}", value.get<int64_t>());
			type = "int64";
			break;
		case ox::ModelValue::Type::String:
			val = ox::sfmt<Str>("\"{}\"", value.get<ox::String>());
			type = "string";
			break;
		case ox::ModelValue::Type::Object:
			type = value.get<ox::ModelObject>().type()->typeName.c_str();
			break;
		case ox::ModelValue::Type::Union:
			type = "union";
			break;
		case ox::ModelValue::Type::InlineArray:
		case ox::ModelValue::Type::Vector:
			type = "list";
			break;
	}
	ImGui::TableNextColumn();
	ImGui::Text("%s", type.c_str());
	ImGui::TableNextColumn();
	ImGui::Text("%s", val.c_str());
}

void ClawEditor::drawVar(ObjPath &path, ox::StringViewCR name, ox::ModelValue const &value) noexcept {
	using Str = ox::BasicString<100>;
	path.push_back(name);
	if (value.type() == ox::ModelValue::Type::Object) {
		drawTree(path, value.get<ox::ModelObject>());
	} else if (value.type() == ox::ModelValue::Type::Vector) {
		auto const &vec = value.get<ox::ModelValueVector>();
		auto const pathStr = ox::join<Str>("##", path).unwrap();
		auto const lbl = ox::sfmt<Str>("{}##{}", name, pathStr);
		auto const flags = ImGuiTreeNodeFlags_SpanFullWidth
		                 | ImGuiTreeNodeFlags_OpenOnArrow
		                 | (vec.size() ? 0 : ImGuiTreeNodeFlags_Leaf)
		                 | (false ? ImGuiTreeNodeFlags_Selected : 0);
		auto const open = ImGui::TreeNodeEx(lbl.c_str(), flags);
		ImGui::SameLine();
		drawRow(value);
		if (open) {
			for (auto i = 0lu; auto const &e: vec) {
				auto const iStr = ox::sfmt<Str>("{}", i);
				path.push_back(iStr);
				ImGui::TableNextRow(0, 5);
				ImGui::TableNextColumn();
				drawVar(path, ox::sfmt<Str>("[{}]", i), e);
				path.pop_back();
				++i;
			}
			ImGui::TreePop();
		}
	} else {
		auto const pathStr = ox::join<Str>("##", path).unwrap();
		auto const lbl = ox::sfmt<Str>("{}##{}", name, pathStr);
		auto const flags = ImGuiTreeNodeFlags_SpanFullWidth
		                   | ImGuiTreeNodeFlags_OpenOnArrow
		                   | ImGuiTreeNodeFlags_Leaf
		                   | (false ? ImGuiTreeNodeFlags_Selected : 0);
		auto const open = ImGui::TreeNodeEx(lbl.c_str(), flags);
		ImGui::SameLine();
		drawRow(value);
		if (open) {
			ImGui::TreePop();
		}
	}
	path.pop_back();
}

void ClawEditor::drawTree(ObjPath &path, ox::ModelObject const &obj) noexcept {
	using Str = ox::BasicString<100>;
	for (auto const &c : obj) {
		ImGui::TableNextRow(0, 5);
		auto pathStr = ox::join<Str>("##", path).unwrap();
		auto lbl = ox::sfmt<Str>("{}##{}", c->name, pathStr);
		auto const rowSelected = false;
		auto const hasChildren = c->value.type() == ox::ModelValue::Type::Object
		                                || c->value.type() == ox::ModelValue::Type::Vector;
		auto const flags = ImGuiTreeNodeFlags_SpanFullWidth
		                   | ImGuiTreeNodeFlags_OpenOnArrow
		                   | (hasChildren ? 0 : ImGuiTreeNodeFlags_Leaf)
		                   | (rowSelected ? ImGuiTreeNodeFlags_Selected : 0);
		ImGui::TableNextColumn();
		//if (ImGui::IsItemClicked()) {
		//}
		//if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) {
		//}
		path.push_back(c->name);
		if (c->value.type() == ox::ModelValue::Type::Object) {
			auto const open = ImGui::TreeNodeEx(lbl.c_str(), flags);
			ImGui::SameLine();
			drawRow(c->value);
			if (open) {
				drawTree(path, c->value.get<ox::ModelObject>());
				ImGui::TreePop();
			}
		} else {
			drawVar(path, c->name, c->value);
		}
		path.pop_back();
	}
}

}
