#pragma once

#include <memory>
#include <Buffer.hpp>
#include <ncurses/ncurses.hpp>
#include <modes/Insert.hpp>
#include <modes/Edit.hpp>
#include <modes/Quit.hpp>
#include <modes/Save.hpp>
#include <unordered_map>

namespace groove {

	enum class Mode {
		INSERT,
		EDIT,
		SAVE,
		QUIT
	};

	enum LineMode {
		NUMBERS,
		RELATIVE,
		NONE,
		COUNT
	};

	class Editor {
		friend class modes::Mode;
		friend class modes::Insert;
		friend class modes::Edit;
		friend class modes::Quit;
		friend class modes::Save;
	private:
		long x, y;
		int offset;
		long vspace;
		char lastChar = 0;
		LineMode lineMode;
		std::unique_ptr<Buffer> buffer;
		Mode mode_;
		std::string clipboard = "";
		std::string filename;
		std::unordered_map<Mode, std::unique_ptr<modes::Mode>> modes;

		bool load();
		bool save();
		void status();
		void scrollUp() {
			if (this->y < this->offset && this->offset > 0)
				this->offset--;
		}
		void scrollDown() {
			if (this->y - this->offset >= LINES - 1 && this->y < this->buffer->size())
				this->offset++;
		}
		void left() {
			if (this->x - 1 >= 0)
				this->x--;
			else {
				if (this->y - 1 >= 0) {
					this->y--;
					this->x = this->buffer->at(this->y).length();
				}
			}
		}
		void right() {
			if (this->x + 1 <= COLS && this->x + 1 <= this->buffer->linebuffer().at(this->y).length())
				this->x++;
			else {
				if (this->y + 1 <= this->buffer->size()) {
					this->y++;
					this->x = this->buffer->at(this->y).size();
				}
			}
		}
		void up() {
			if (this->y - 1 >= 0)
				this->y--;
			else {
				return;
			}
			if (this->x >= this->buffer->linebuffer().at(this->y).length()) {
				long length = this->buffer->linebuffer().at(this->y).length();
				this->x = length > 0 ? length - 1 : 0;
			}
			this->scrollUp();
		}
		void down() {
			if (this->y + 1 < this->buffer->linebuffer().size() )
				this->y++;
			else {
				return;
			}
			if (this->x >= this->buffer->linebuffer().at(this->y).length()) {
				long length = this->buffer->linebuffer().at(this->y).length();
				this->x = length > 0 ? length - 1 : 0;
			}
			this->scrollDown();
		}

		bool movement(int c) {
			switch (c) {
				case KEY_LEFT:
					this->left();
					return true;
				case KEY_RIGHT:
					this->right();
					return true;
				case KEY_UP:
					this->up();
					return true;
				case KEY_DOWN:
					this->down();
					return true;
				default:
					return false;
			}
		}
	public:
		Editor(std::string file = "ubenannt");
		Mode mode() {
			return this->mode_;
		}
		void input(int c);
		void render();
		static long Digits(long number);
	};

}