# This file is part of Elide, frontend to Lisien, a framework for life simulation games.
# Copyright (c) Zachary Spector, public@zacharyspector.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
from kivy.clock import Clock, triggered
from kivy.logger import Logger
from kivy.properties import (
	ListProperty,
	NumericProperty,
	ObjectProperty,
	StringProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen
from kivy.uix.scrollview import ScrollView

from .kivygarden.texturestack import ImageStack
from .pallet import Pallet, PalletBox
from .util import load_string_once

# TODO: let the user import their own sprite art


def trigger(func):
	return triggered()(func)


class SpriteSelector(BoxLayout):
	prefix = StringProperty()
	pallets = ListProperty()
	imgpaths = ListProperty([])
	default_imgpaths = ListProperty()
	preview = ObjectProperty()

	def on_prefix(self, *_):
		if "textbox" not in self.ids:
			Clock.schedule_once(self.on_prefix, 0)
			return
		self.ids.textbox.text = self.prefix

	def on_imgpaths(self, *_):
		if not self.preview:
			Logger.debug("SpriteSelector: no preview")
			Clock.schedule_once(self.on_imgpaths, 0)
			return
		if hasattr(self, "_imgstack"):
			self._imgstack.paths = self.imgpaths
		else:
			self._imgstack = ImageStack(paths=self.imgpaths)
			self._imgstack.bind(
				pos=self._position_imgstack, size=self._position_imgstack
			)
			self.preview.add_widget(self._imgstack)

	@trigger
	def _position_imgstack(self, *_):
		self._imgstack.x = self.preview.center_x - self._imgstack.height / 2
		self._imgstack.y = self.preview.center_y - self._imgstack.width / 2

	def on_pallets(self, *_):
		for pallet in self.pallets:
			pallet.fbind("selection", self._upd_imgpaths)

	def _upd_imgpaths(self, *_):
		imgpaths = []
		for pallet in self.pallets:
			if pallet.selection:
				for selected in pallet.selection:
					imgpaths.append(
						"atlas://{}/{}".format(pallet.filename, selected.text)
					)
		self.imgpaths = imgpaths if imgpaths else self.default_imgpaths


class SpriteBuilder(ScrollView):
	prefix = StringProperty()
	imgpaths = ListProperty()
	default_imgpaths = ListProperty()
	data = ListProperty()
	labels = ListProperty()
	pallets = ListProperty()

	def __init__(self, **kwargs):
		super().__init__(**kwargs)
		self.bind(data=self._trigger_update)

	def update(self, *args):
		if self.data is None:
			return
		if not self.canvas:
			Clock.schedule_once(self.update, 0)
			return
		if not hasattr(self, "_palbox"):
			self._palbox = PalletBox(orientation="vertical", size_hint_y=None)
			self.add_widget(self._palbox)
		else:
			self._palbox.clear_widgets()
		if hasattr(self._palbox, "_bound_width"):
			for uid in self._palbox._bound_width:
				self._palbox.unbind_uid("width", uid)
			del self._palbox._bound_width
		self.labels = []
		for pallet in self.pallets:
			if hasattr(pallet, "_bound_minimum_height"):
				pallet.unbind_uid(
					"minimum_height", pallet._bound_minimum_height
				)
				del pallet._bound_minimum_height
			if hasattr(pallet, "_bound_height"):
				pallet.unbind_uid("height", pallet._bound_height)
				del pallet._bound_height
		self.pallets = []
		for text, filename in self.data:
			label = Label(text=text, size_hint=(None, None), halign="center")
			label.texture_update()
			label.height = label.texture.height
			label.width = self._palbox.width
			pallet = Pallet(filename=filename, size_hint=(None, None))
			pallet.width = self._palbox.width
			self._palbox._bound_width = [
				self._palbox.fbind("width", label.setter("width")),
				self._palbox.fbind("width", pallet.setter("width")),
			]
			pallet.height = pallet.minimum_height
			pallet._bound_minimum_height = (
				pallet.fbind("minimum_height", pallet.setter("height")),
			)
			pallet._bound_height = pallet.fbind(
				"height", self._trigger_reheight
			)
			self.labels.append(label)
			self.pallets.append(pallet)
		n = len(self.labels)
		assert n == len(self.pallets)
		for i in range(0, n):
			self._palbox.add_widget(self.labels[i])
			self._palbox.add_widget(self.pallets[i])

	_trigger_update = trigger(update)

	def reheight(self, *args):
		self._palbox.height = sum(
			wid.height for wid in self.labels + self.pallets
		)

	_trigger_reheight = trigger(reheight)


class SpriteDialog(BoxLayout):
	toggle = ObjectProperty()
	prefix = StringProperty()
	imgpaths = ListProperty()
	default_imgpaths = ListProperty()
	data = ListProperty()
	pallet_box_height = NumericProperty()

	def pressed(self):
		self.prefix = self.ids.selector.prefix
		self.imgpaths = self.ids.selector.imgpaths
		self.toggle()


class PawnConfigDialog(SpriteDialog):
	pass


class SpotConfigDialog(SpriteDialog):
	pass


class PawnConfigScreen(Screen):
	toggle = ObjectProperty()
	data = ListProperty()
	imgpaths = ListProperty()


class SpotConfigScreen(Screen):
	toggle = ObjectProperty()
	data = ListProperty()
	imgpaths = ListProperty()


load_string_once("""
<SpriteDialog>:
	orientation: 'vertical'
	SpriteBuilder:
		id: builder
		prefix: root.prefix
		default_imgpaths: root.default_imgpaths
		imgpaths: root.imgpaths
		data: root.data
	SpriteSelector:
		id: selector
		textbox: textbox
		size_hint_y: 0.1
		prefix: root.prefix
		default_imgpaths: root.default_imgpaths
		imgpaths: root.imgpaths
		pallets: builder.pallets
		preview: preview
		TextInput:
			id: textbox
			multiline: False
			write_tab: False
			hint_text: 'Enter name prefix'
		Widget:
			id: preview
			canvas:
				Color:
					rgba: 1, 1, 1, 1
		Button:
			text: 'OK'
			on_release: root.pressed()
<PawnConfigScreen>:
	name: 'pawncfg'
	imgpaths: dialog.imgpaths
	PawnConfigDialog:
		id: dialog
		toggle: root.toggle
		default_imgpaths: ['atlas://rltiles/base/unseen']
		data: root.data
<SpotConfigScreen>:
	name: 'spotcfg'
	imgpaths: dialog.imgpaths
	SpotConfigDialog:
		id: dialog
		toggle: root.toggle
		default_imgpaths: ['atlas://rltiles/floor/floor-stone']
		data: root.data
""")
