#include "thin-provisioning/mapping_tree.h"

#include "persistent-data/data-structures/btree_damage_visitor.h"
#include "persistent-data/space_map.h"

using namespace persistent_data;

//----------------------------------------------------------------

namespace thin_provisioning {
	namespace mapping_tree_detail {
		space_map_ref_counter::space_map_ref_counter(space_map::ptr sm)
			: sm_(sm)
		{
		}

		void
		space_map_ref_counter::inc(block_address b)
		{
			sm_->inc(b);
		}

		void
		space_map_ref_counter::dec(block_address b)
		{
			sm_->dec(b);
		}

		//--------------------------------

		block_time_ref_counter::block_time_ref_counter(space_map::ptr sm)
			: sm_(sm)
		{
		}

		void
		block_time_ref_counter::inc(block_time bt)
		{
			sm_->inc(bt.block_);
		}

		void
		block_time_ref_counter::dec(block_time bt)
		{
			sm_->dec(bt.block_);
		}

		//--------------------------------

		void
		block_traits::unpack(disk_type const &disk, value_type &value)
		{
			uint64_t v = to_cpu<uint64_t>(disk);
			value.block_ = v >> 24;
			value.time_ = v & ((1 << 24) - 1);
		}

		void
		block_traits::pack(value_type const &value, disk_type &disk)
		{
			uint64_t v = (value.block_ << 24) | value.time_;
			disk = base::to_disk<base::le64>(v);
		}

		//--------------------------------

		mtree_ref_counter::mtree_ref_counter(transaction_manager &tm)
			: tm_(tm)
		{
		}

		void
		mtree_ref_counter::set(block_address const &b, uint32_t rc)
		{
			tm_.get_sm()->set_count(b, rc);
		}

		void
		mtree_ref_counter::inc(block_address const &b)
		{
			tm_.get_sm()->inc(b);
		}

		void
		mtree_ref_counter::dec(block_address const &b)
		{
			tm_.get_sm()->dec(b);
		}
		
		//--------------------------------

		void
		mtree_traits::unpack(disk_type const &disk, value_type &value)
		{
			value = base::to_cpu<uint64_t>(disk);
		}

		void
		mtree_traits::pack(value_type const &value, disk_type &disk)
		{
			disk = base::to_disk<base::le64>(value);
		}

		//--------------------------------

		missing_devices::missing_devices(std::string const &desc, run<uint64_t> const &keys)
		: desc_(desc),
		  keys_(keys)
		{
		}

		void
		missing_devices::visit(damage_visitor &v) const
		{
			v.visit(*this);
		}

		//--------------------------------

		missing_mappings::missing_mappings(std::string const &desc, uint64_t thin_dev,
						   run<uint64_t> const &keys)
			: desc_(desc),
			thin_dev_(thin_dev),
			keys_(keys)
		{
		}

		void
		missing_mappings::visit(damage_visitor &v) const
		{
			v.visit(*this);
		}
	}
}

//----------------------------------------------------------------

namespace {
	using namespace thin_provisioning;
	using namespace mapping_tree_detail;

	struct noop_block_time_visitor : public mapping_tree_detail::mapping_visitor {
		virtual void visit(btree_path const &, block_time const &) {
		}
	};

	struct noop_block_visitor : public mapping_tree_detail::device_visitor {
		virtual void visit(btree_path const &, uint64_t) {
		}
	};

	class dev_tree_damage_visitor {
	public:
		dev_tree_damage_visitor(damage_visitor &v)
		: v_(v) {
		}

		virtual void visit(btree_path const &path, btree_detail::damage const &d) {
			switch (path.size()) {
			case 0:
				v_.visit(missing_devices(d.desc_, d.lost_keys_));
				break;

			case 1:
				v_.visit(missing_mappings(d.desc_, path[0], d.lost_keys_));
				break;

			default:
				throw std::runtime_error("dev_tree_damage_visitor: path too long");
			}
		}

	private:
		damage_visitor &v_;
	};

	class mapping_tree_damage_visitor {
	public:
		mapping_tree_damage_visitor(damage_visitor &v)
		: v_(v) {
		}

		virtual void visit(btree_path const &path, btree_detail::damage const &d) {
			switch (path.size()) {
			case 0:
				v_.visit(missing_devices(d.desc_, d.lost_keys_));
				break;

			case 1:
				v_.visit(missing_mappings(d.desc_, path[0], d.lost_keys_));
				break;

			default:
				throw std::runtime_error("mapping_tree_damage_visitor: path too long");
			}
		}

	private:
		damage_visitor &v_;
	};

	class single_mapping_tree_damage_visitor {
	public:
		single_mapping_tree_damage_visitor(damage_visitor &v,
						   uint64_t dev_id)
		: v_(v),
		  dev_id_(dev_id) {
		}

		virtual void visit(btree_path const &path, btree_detail::damage const &d) {
			switch (path.size()) {
			case 0:
				v_.visit(missing_mappings(d.desc_, dev_id_, d.lost_keys_));
				break;

			default:
				throw std::runtime_error("single_mapping_tree_damage_visitor: path too long");
			}
		}

	private:
		damage_visitor &v_;
		uint64_t dev_id_;
	};

}

void
thin_provisioning::walk_mapping_tree(dev_tree const &tree,
				     mapping_tree_detail::device_visitor &dev_v,
				     mapping_tree_detail::damage_visitor &dv,
				     bool ignore_non_fatal)
{
	dev_tree_damage_visitor ll_dv(dv);
	btree_visit_values(tree, dev_v, ll_dv, ignore_non_fatal);
}

void
thin_provisioning::check_mapping_tree(dev_tree const &tree,
				      mapping_tree_detail::damage_visitor &visitor,
				      bool ignore_non_fatal)
{
	noop_block_visitor dev_v;
	walk_mapping_tree(tree, dev_v, visitor, ignore_non_fatal);
}

void
thin_provisioning::walk_mapping_tree(mapping_tree const &tree,
				     mapping_tree_detail::mapping_visitor &mv,
				     mapping_tree_detail::damage_visitor &dv,
				     bool ignore_non_fatal)
{
	mapping_tree_damage_visitor ll_dv(dv);
	btree_visit_values(tree, mv, ll_dv, ignore_non_fatal);
}

void
thin_provisioning::check_mapping_tree(mapping_tree const &tree,
				      mapping_tree_detail::damage_visitor &visitor,
				      bool ignore_non_fatal)
{
	noop_block_time_visitor mv;
	walk_mapping_tree(tree, mv, visitor, ignore_non_fatal);
}

void
thin_provisioning::walk_mapping_tree(single_mapping_tree const &tree,
				     uint64_t dev_id,
				     mapping_tree_detail::mapping_visitor &mv,
				     mapping_tree_detail::damage_visitor &dv,
				     bool ignore_non_fatal)
{
	single_mapping_tree_damage_visitor ll_dv(dv, dev_id);
	btree_visit_values(tree, mv, ll_dv, ignore_non_fatal);
}

void
thin_provisioning::check_mapping_tree(single_mapping_tree const &tree,
				      uint64_t dev_id,
				      mapping_tree_detail::damage_visitor &visitor,
				      bool ignore_non_fatal)
{
	noop_block_time_visitor mv;
	walk_mapping_tree(tree, dev_id, mv, visitor, ignore_non_fatal);
}

//----------------------------------------------------------------
