#ifndef KBDPARSER_H
#define KBDPARSER_H
#include "piflags.h"
#include <Arduino.h>

enum SpecialKey {
	Tab /** Tab key */ = 0x09,
	Return /** Enter key */ = 0x0a,
	Esc /** Escape key */ = 0x1b,
	Space /** Space key */ = 0x20,
	Backspace /** Backspace key */ = 0x7f,
	UpArrow /** Up arrow key */ = -1,
	DownArrow /** Down arrow key */ = -2,
	RightArrow /** Right arrow key */ = -3,
	LeftArrow /** Left arrow key */ = -4,
	Home /** Home key */ = -5,
	End /** End key */ = -6,
	PageUp /** Page up key */ = -7,
	PageDown /** Page down key */ = -8,
	Insert /** Insert key */ = -9,
	Delete /** Delete key */ = -10,
//	F1  /** F1 key */ = -11,
//	F2  /** F2 key */ = -12,
//	F3  /** F3 key */ = -13,
//	F4  /** F4 key */ = -14,
//	F5  /** F5 key */ = -15,
//	F6  /** F6 key */ = -16,
//	F7  /** F7 key */ = -17,
//	F8  /** F8 key */ = -18,
//	F9  /** F9 key */ = -19,
//	F10 /** F10 key */ = -20,
//	F11 /** F11 key */ = -21,
//	F12 /** F12 key */ = -22,
//	CenterArrow /** Center arrow key */ = -23,
};

//! Keyboard modifiers
enum KeyModifier {
	Ctrl /** Control key */ = 0x1,
	Shift /** Shift key */ = 0x2,
	Alt /** Alt key */ = 0x4
	//Meta /** Meta (windows) key */ = 0x8
};

struct EscSeq {
	const char * seq;
	int key;
	unsigned char mod;
	// 1 - shift
	// 2 - alt
	// 4 - ctrl
};
static const EscSeq esc_seq[] = {
//	{"[1A",  UpArrow, 0},
	{"[A",   UpArrow, 0},
	{"OA",   UpArrow, 4},
//	{"[1B",  DownArrow, 0},
	{"[B",   DownArrow, 0},
	{"OB",   DownArrow, 4},
//	{"[1C",  RightArrow, 0},
	{"[C",   RightArrow, 0},
	{"OC",   RightArrow, 4},
//	{"[1D",  LeftArrow, 0},
	{"[D",   LeftArrow, 0},
	{"OD",   LeftArrow, 4},
//	{"[G",   CenterArrow, 0},
//	{"OG",   CenterArrow, 4},
	{"[H",   Home, 0},
//	{"[1H",  Home, 0},
	{"[1~",  Home, 0},
	{"[F",   End, 0},
//	{"[1F",  End, 0},
	{"[4~",  End, 0},
	{"[2~",  Insert, 0},
	{"[3~",  Delete, 0},
	{"[5~",  PageUp, 0},
	{"[6~",  PageDown, 0},
	{"[Z",   Tab, 1},
//	{"OP",   F1, 0},
//	{"[[A",  F1, 0},
//	{"[11~", F1, 0},
//	{"OQ",   F2, 0},
//	{"[[B",  F2, 0},
//	{"[12~", F2, 0},
//	{"OR",   F3, 0},
//	{"[[C",  F3, 0},
//	{"[13~", F3, 0},
//	{"[25~", F3, 1},
//	{"OS",   F4, 0},
//	{"[[D",  F4, 0},
//	{"[14~", F4, 0},
//	{"[26~", F4, 1},
//	{"[[E",  F5, 0},
//	{"OT",   F5, 0},
//	{"[15~", F5, 0},
//	{"[28~", F5, 1},
//	{"OU",   F6, 0},
//	{"[17~", F6, 0},
//	{"[29~", F6, 1},
//	{"OV",   F7, 0},
//	{"[18~", F7, 0},
//	{"[31~", F7, 1},
//	{"OW",   F8, 0},
//	{"[19~", F8, 0},
//	{"[32~", F8, 1},
//	{"OX",   F9, 0},
//	{"[20~", F9, 0},
//	{"[33~", F9, 1},
//	{"OY",   F10, 0},
//	{"[21~", F10, 0},
//	{"[34~", F10, 1},
//	{"OZ",   F11, 0},
//	{"[23~", F11, 0},
//	{"O[",   F12, 0},
//	{"[24~", F12, 0},
	// End
	{0, 0, 0},
};

typedef PIFlags<KeyModifier> KeyModifiers;

//! This struct contains information about pressed keyboard key
struct KeyEvent {
	KeyEvent(int k = 0, KeyModifiers m = 0) {key = k; modifiers = m;}
	
	//! Pressed key. It can be simple \b char or special key (see PIKbdListener::SpecialKey)
	int key;
	
	//! Active keyboard modifiers. It contains PIKbdListener::KeyModifier bitfields
	KeyModifiers modifiers;
};

typedef void (*KBFunc)(KeyEvent);

void parseKey(int rc[8], int ret, KBFunc slot) {
	KeyEvent ke;
	if (ret == 1) ke.key = rc[0];
	int mod(0);
	// 2 - shift			1
	// 3 - alt				2
	// 4 - alt+shift		3
	// 5 - ctrl				4
	// 8 - ctrl+alt+shift	7x
	if (rc[0] == '\e' && ret > 1) {
		if (ret == 2) { // search for Alt
			mod = 2;
			ke.key = rc[1];
		} else {// escape-seq
			if (rc[1] == '\e') { // search for Alt
				for (int i = 1; i < 7; ++i) rc[i] = rc[i + 1];
				ret--;
				mod = 2;
			}
			if (rc[1] == '[') {
				for (int i = 2; i < 7; ++i) // search for modifier
					if (rc[i] == ';') {
						mod |= rc[i + 1] - '0' - 1;
						for (int j = i; j < 6; ++j) rc[j] = rc[j + 2];
						rc[6] = rc[7] = 0;
						ret -= 2;
						break;
					}
			}
//			if (ret >= 3 && rc[1] == 'O') { // search for modifier (F1-F4)
//				if (rc[2] >= '1' && rc[2] <= '8') {
//					mod |= rc[2] - '0' - 1;
//					for (int j = 2; j < 6; ++j) rc[j] = rc[j + 1];
//					rc[7] = 0;
//					ret -= 1;
//				}
//			}
			String src = String();
			for (int i=1; i<ret; i++) src += String(char(rc[i]));
			for (int i = 0; ; ++i) {
				if (!esc_seq[i].seq) break;
				if (String(esc_seq[i].seq) == src) {
					ke.key = esc_seq[i].key;
					mod |= esc_seq[i].mod;
					break;
				}
			}
		}
		if (mod >= 0 && mod <= 15) {
			if (mod & 0x1) ke.modifiers |= Shift;
			if (mod & 0x2) ke.modifiers |= Alt;
			if (mod & 0x4) ke.modifiers |= Ctrl;
		}
//		if (ke.key == 0) {
//			Serial.print("{");
//			for (int i=0; i<8; i++) {
//				if (rc[i] == '\e') {
//					Serial.print("\\e");
//					continue;
//				}
//				if (rc[i] < 0) break;
//				Serial.write(char(rc[i]));
//			}
//			Serial.print("}");
//		}
	}
	if (ke.key == 8) ke.key = Backspace;
	if ((rc[0] == '\n' || rc[0] == '\r') && ret == 1)
		ke.key = Return;
	if (ret > 0)
		if (slot != 0) slot(ke);
}

void clearLine() {Serial.print("\e[K");}
void showCursor() {Serial.print("\e[?25h");}
void hideCursor() {Serial.print("\e[?25l");}
void leftCursor(int n=1) {if(n==0) return; Serial.print("\e["); Serial.print(n); Serial.print("D");}
void rightCursor(int n=1) {if(n==0) return; Serial.print("\e["); Serial.print(n); Serial.print("C");}

#endif //KBDPARSER_H
