# 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 functools import partial

from kivy.clock import Clock, triggered
from kivy.properties import (
	BooleanProperty,
	ObjectProperty,
	ReferenceListProperty,
)
from kivy.uix.boxlayout import BoxLayout

from lisien.proxy import CharStatProxy

from .graph.arrow import GraphArrowWidget
from .util import dummynum, logwrap, store_kv


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


class CharMenu(BoxLayout):
	screen = ObjectProperty()
	reciprocal_portal = BooleanProperty(True)
	revarrow = ObjectProperty(None, allownone=True)
	dummyplace = ObjectProperty()
	dummything = ObjectProperty()
	toggle_gridview = ObjectProperty()
	toggle_timestream = ObjectProperty()
	dummies = ReferenceListProperty(dummyplace, dummything)

	@property
	def app(self):
		if not self.screen:
			raise AttributeError("No screen, therefore no app")
		return self.screen.app

	@property
	def engine(self):
		if not self.screen or not self.screen.app:
			raise AttributeError("Can't get engine from screen")
		return self.screen.app.engine

	@logwrap(section="CharMenu")
	def on_screen(self, *_):
		if not (
			self.screen
			and self.screen.boardview
			and self.screen.app
			and "emptyleft" in self.ids
			and "emptyright" in self.ids
		):
			Clock.schedule_once(self.on_screen, 0)
			return
		self.forearrow = GraphArrowWidget(
			board=self.screen.boardview.board,
			origin=self.ids.emptyleft,
			destination=self.ids.emptyright,
		)
		self.ids.portaladdbut.add_widget(self.forearrow)
		self.ids.emptyleft.bind(pos=self.forearrow._trigger_repoint)
		self.ids.emptyright.bind(pos=self.forearrow._trigger_repoint)
		if self.reciprocal_portal:
			assert self.revarrow is None
			self.revarrow = GraphArrowWidget(
				board=self.screen.boardview.board,
				origin=self.ids.emptyright,
				destination=self.ids.emptyleft,
			)
			self.ids.portaladdbut.add_widget(self.revarrow)
			self.ids.emptyleft.bind(pos=self.revarrow._trigger_repoint)
			self.ids.emptyright.bind(pos=self.revarrow._trigger_repoint)
		self.bind(
			reciprocal_portal=self.screen.boardview.setter("reciprocal_portal")
		)

	@logwrap(section="CharMenu")
	def spot_from_dummy(self, dummy):
		if self.screen.boardview.parent != self.screen.mainview:
			return
		if dummy.collide_widget(self):
			return
		name = dummy.name
		self.screen.boardview.spot_from_dummy(dummy)
		graphboard = self.screen.graphboards[self.app.character_name]
		if name not in graphboard.spot:
			graphboard.add_spot(name)
		gridboard = self.screen.gridboards[self.app.character_name]
		if (
			name not in gridboard.spot
			and isinstance(name, tuple)
			and len(name) == 2
		):
			gridboard.add_spot(name)

	@logwrap(section="CharMenu")
	def pawn_from_dummy(self, dummy):
		name = dummy.name
		if not self.screen.mainview.children[0].pawn_from_dummy(dummy):
			return
		graphboard = self.screen.graphboards[self.app.character_name]
		if name not in graphboard.pawn:
			graphboard.add_pawn(name)
		gridboard = self.screen.gridboards[self.app.character_name]
		if (
			name not in gridboard.pawn
			and self.app.character.thing[name]["location"] in gridboard.spot
		):
			gridboard.add_pawn(name)

	@logwrap(section="CharMenu")
	def toggle_chars_screen(self, *_):
		"""Display or hide the list you use to switch between characters."""
		# TODO: update the list of chars
		self.app.chars.toggle()

	@logwrap(section="CharMenu")
	def toggle_rules(self, *_):
		"""Display or hide the view for constructing rules out of cards."""
		if self.app.manager.current != "rules" and not isinstance(
			self.app.selected_proxy, CharStatProxy
		):
			self.app.rules.entity = self.app.selected_proxy
			self.app.rules.rulebook = self.app.selected_proxy.rulebook
		if isinstance(self.app.selected_proxy, CharStatProxy):
			self.app.charrules.character = self.app.selected_proxy
			self.app.charrules.toggle()
		else:
			self.app.rules.toggle()

	@logwrap(section="CharMenu")
	def toggle_funcs_editor(self):
		"""Display or hide the text editing window for functions."""
		self.app.funcs.toggle()

	@logwrap(section="CharMenu")
	def toggle_strings_editor(self):
		self.app.strings.toggle()

	@logwrap(section="CharMenu")
	def toggle_spot_cfg(self):
		"""Show the dialog where you select graphics and a name for a place,
		or hide it if already showing.

		"""
		if self.app.manager.current == "spotcfg":
			dummyplace = self.screendummyplace
			self.ids.placetab.remove_widget(dummyplace)
			dummyplace.clear()
			if self.app.spotcfg.prefix:
				dummyplace.prefix = self.app.spotcfg.prefix
				dummyplace.num = (
					dummynum(self.app.character, dummyplace.prefix) + 1
				)
			if self.app.spotcfg.imgpaths:
				dummyplace.paths = self.app.spotcfg.imgpaths
			else:
				dummyplace.paths = ["atlas://rltiles/floor/floor-stone"]
			dummyplace.center = self.ids.placetab.center
			self.ids.placetab.add_widget(dummyplace)
		else:
			self.app.spotcfg.prefix = self.ids.dummyplace.prefix
		self.app.spotcfg.toggle()

	@logwrap(section="CharMenu")
	def toggle_pawn_cfg(self):
		"""Show or hide the pop-over where you can configure the dummy pawn"""
		if self.app.manager.current == "pawncfg":
			dummything = self.app.dummything
			self.ids.thingtab.remove_widget(dummything)
			dummything.clear()
			if self.app.pawncfg.prefix:
				dummything.prefix = self.app.pawncfg.prefix
				dummything.num = (
					dummynum(self.app.character, dummything.prefix) + 1
				)
			if self.app.pawncfg.imgpaths:
				dummything.paths = self.app.pawncfg.imgpaths
			else:
				dummything.paths = ["atlas://rltiles/base/unseen"]
			self.ids.thingtab.add_widget(dummything)
		else:
			self.app.pawncfg.prefix = self.ids.dummything.prefix
		self.app.pawncfg.toggle()

	@logwrap(section="CharMenu")
	def toggle_reciprocal(self):
		"""Flip my ``reciprocal_portal`` boolean, and draw (or stop drawing)
		an extra arrow on the appropriate button to indicate the
		fact.

		"""
		self.reciprocal_portal = (
			self.screen.boardview.reciprocal_portal
		) = not self.screen.boardview.reciprocal_portal
		if self.screen.boardview.reciprocal_portal:
			assert self.revarrow is None
			self.revarrow = GraphArrowWidget(
				board=self.screen.boardview.board,
				origin=self.ids.emptyright,
				destination=self.ids.emptyleft,
			)
			self.ids.portaladdbut.add_widget(self.revarrow)
			self.ids.emptyright.bind(pos=self.revarrow._trigger_repoint)
			self.ids.emptyleft.bind(pos=self.revarrow._trigger_repoint)
		else:
			if hasattr(self, "revarrow"):
				self.ids.portaladdbut.remove_widget(self.revarrow)
				self.revarrow = None

	@logwrap(section="CharMenu")
	def new_character(self, but):
		name = self.app.chars.ids.newname.text
		try:
			charn = self.app.engine.unpack(name)
		except (TypeError, ValueError):
			charn = name
		self.app.select_character(self.app.engine.new_character(charn))
		self.app.chars.ids.newname.text = ""
		self.app.chars.charsview.adapter.data = list(
			self.engine.character.keys()
		)
		Clock.schedule_once(self.toggle_chars_screen, 0.01)

	@logwrap(section="CharMenu")
	def on_dummyplace(self, *_):
		if not self.dummyplace.paths:
			self.dummyplace.paths = ["atlas://rltiles/floor.atlas/floor-stone"]

	@logwrap(section="CharMenu")
	def on_dummything(self, *_):
		if not self.dummything.paths:
			self.dummything.paths = ["atlas://rltiles/base.atlas/unseen"]

	@trigger
	@logwrap(section="CharMenu")
	def _trigger_deselect(self, *_):
		if hasattr(self.app.selection, "selected"):
			self.app.selection.selected = False
		self.app.selection = None


store_kv(
	__name__,
	"""
<CharMenu>:
	orientation: 'vertical'
	dummyplace: dummyplace
	dummything: dummything
	portaladdbut: portaladdbut
	portaldirbut: portaldirbut
	Button:
		text: 'Delete'
		disabled: app.edit_locked
		on_release: app.delete_selection()
	Button:
		id: timestreambut
		text: 'Timestream'
		disabled: app.edit_locked
		on_release: root.toggle_timestream()
	Button:
		id: gridviewbut
		text: 'Toggle grid'
		on_release: root.toggle_gridview()
	Button:
		text: 'Strings'
		disabled: app.edit_locked
		on_release: root.toggle_strings_editor()
	Button:
		text: 'Python'
		disabled: app.edit_locked
		on_release: root.toggle_funcs_editor()
	Button:
		text: 'Rules'
		disabled: app.edit_locked
		on_release: root.toggle_rules()
	Button:
		text: 'Characters'
		on_release: root.toggle_chars_screen()
	BoxLayout:
		Widget:
			id: placetab
			Dummy:
				id: dummyplace
				center: placetab.center
				prefix: 'place'
				disabled: app.edit_locked
				on_pos_up: root.spot_from_dummy(self)
		Button:
			text: 'cfg'
			disabled: app.edit_locked
			on_release: root.toggle_spot_cfg()
	BoxLayout:
		Widget:
			id: thingtab
			Dummy:
				id: dummything
				center: thingtab.center
				prefix: 'thing'
				disabled: app.edit_locked
				on_pos_up: root.pawn_from_dummy(self)
		Button:
			text: 'cfg'
			disabled: app.edit_locked
			on_release: root.toggle_pawn_cfg()
	BoxLayout:
		orientation: 'vertical'
		ToggleButton:
			id: portaladdbut
			disabled: app.edit_locked
			on_state: root._trigger_deselect()
			Widget:
				id: emptyleft
				center_x: portaladdbut.x + portaladdbut.width / 3
				center_y: portaladdbut.center_y
				size: (0, 0)
			Widget:
				id: emptyright
				center_x: portaladdbut.right - portaladdbut.width / 3
				center_y: portaladdbut.center_y
				size: (0, 0)
		Button:
			id: portaldirbut
			text: 'One-way' if root.reciprocal_portal else 'Two-way'
			disabled: app.edit_locked
			on_release: root.toggle_reciprocal()
	Button:
		text: 'Quit'
		on_release: app.close_game()
""",
)
