# ###################################################
# Copyright (C) 2008-2017 The Unknown Horizons Team
# team@unknown-horizons.org
# This file is part of Unknown Horizons.
#
# Unknown Horizons is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# ###################################################


from horizons.command.building import Build
from horizons.component.storagecomponent import StorageComponent
from horizons.constants import BUILDINGS, PRODUCTION
from horizons.scheduler import Scheduler
from horizons.util.shapes import RadiusRect, Rect
from horizons.world.building.buildable import (
	BuildableSingle, BuildableSingleOnCoast, BuildableSingleOnDeposit, BuildableSingleOnOcean)
from horizons.world.building.building import BasicBuilding
from horizons.world.building.buildingresourcehandler import BuildingResourceHandler
from horizons.world.building.nature import Field
from horizons.world.production.producer import Producer


class ProductionBuilding(BuildingResourceHandler, BuildableSingle, BasicBuilding):
	pass


class PastryShop(ProductionBuilding):
	def get_providers(self):
		reach = RadiusRect(self.position, self.radius)
		resources = self.get_consumed_resources(include_inactive=True)
		providers = self.island.get_providers_in_range(reach, reslist=resources)
		return [provider for provider in providers]


class Farm(ProductionBuilding):
	def get_providers(self):
		reach = RadiusRect(self.position, self.radius)
		resources = self.get_consumed_resources(include_inactive=True)
		providers = self.island.get_providers_in_range(reach, reslist=resources)
		return [provider for provider in providers if isinstance(provider, Field)]


class CoastalProducer(BuildingResourceHandler, BuildableSingleOnOcean, BasicBuilding):
	"""E.g. salt ponds"""
	pass


class Fisher(BuildingResourceHandler, BuildableSingleOnCoast, BasicBuilding):
	"""
	Old selection workaround (only color fish) removed in b69c72aeef0174c42dec4039eed7b81f96f6dcaa.
	"""

	def get_non_paused_utilization(self):
		total = 0
		productions = self.get_component(Producer).get_productions()
		for production in productions:
			if production.get_age() < PRODUCTION.STATISTICAL_WINDOW * 1.5:
				return 1
			state_history = production.get_state_history_times(True)
			total += state_history[PRODUCTION.STATES.producing.index]
		return total / float(len(productions))


class Mine(BuildingResourceHandler, BuildableSingleOnDeposit, BasicBuilding):
	def __init__(self, inventory, deposit_class, *args, **kwargs):
		"""
		@param inventory: inventory dump of deposit (collected by get_prebuild_data())
		@param deposit_class: class num of deposit for later reconstruction (collected by get_prebuild_data())
		"""
		# needs to be inited before super(), since that will call the _on_production_changed hook
		super().__init__(*args, **kwargs)
		self.__inventory = inventory
		self.__deposit_class = deposit_class

	def initialize(self, deposit_class, inventory, **kwargs):
		super().initialize(**kwargs)
		self.__init(deposit_class=deposit_class)
		for res, amount in inventory.items():
			# bury resources from mountain in mine
			self.get_component(StorageComponent).inventory.alter(res, amount)

	@classmethod
	def get_loading_area(cls, building_id, rotation, pos):
		if building_id in [BUILDINGS.MOUNTAIN, BUILDINGS.MINE]:
			if rotation == 45:
				return Rect.init_from_topleft_and_size(pos.origin.x, pos.origin.y + 1, 1, 3)
			elif rotation == 135:
				return Rect.init_from_topleft_and_size(pos.origin.x + 1, pos.origin.y + pos.height - 1, 3, 1)
			elif rotation == 225:
				return Rect.init_from_topleft_and_size(pos.origin.x + pos.width - 1, pos.origin.y + 1, 1, 3)
			elif rotation == 315:
				return Rect.init_from_topleft_and_size(pos.origin.x + 1, pos.origin.y, 3, 1)
			assert False
		else:
			return pos

	def __init(self, deposit_class):
		self.__deposit_class = deposit_class

		# setup loading area
		# TODO: for now we assume that a mine building is 5x5 with a 3x1 entry on 1 side
		#       this needs to be generalized, possibly by defining the loading tiles in the db
		self.loading_area = self.get_loading_area(deposit_class, self.rotation, self.position)

	@classmethod
	def get_prebuild_data(cls, session, position):
		"""Returns dict containing inventory of deposit, which is needed for the mine build"""
		deposit = session.world.get_building(position.center)
		data = {}
		data["inventory"] = deposit.get_component(StorageComponent).inventory.get_dump()
		data["deposit_class"] = deposit.id
		return data

	def remove(self):
		# build the deposit back here after remove() is finished
		deposit_build_data = {'inventory': self.get_component(StorageComponent).inventory.get_dump()}
		build_cmd = Build(self.__deposit_class, self.position.origin.x, self.position.origin.y,
		                  self.island, rotation=self.rotation, ownerless=True, data=deposit_build_data)
		Scheduler().add_new_object(build_cmd, build_cmd, run_in=0)

		super().remove()

	def save(self, db):
		super().save(db)
		db("INSERT INTO mine(rowid, deposit_class) VALUES(?, ?)",
		   self.worldid, self.__deposit_class)

	def load(self, db, worldid):
		super().load(db, worldid)
		deposit_class = db("SELECT deposit_class FROM mine WHERE rowid = ?", worldid)[0][0]
		self.__init(deposit_class)
