/*
 * ReadlinePromptHistory.cpp
 *
 *  Created on: 26. 1. 2019
 *	  Author: Jan Travnicek
 */

#include "ReadlinePromptHistory.h"

#include <readline/history.h>

#include <cstring>

char ReadlinePromptHistory::esc_char [] = { '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\' };
char ReadlinePromptHistory::essc_str [] = {  'a',  'b',  'f',  'n',  'r',  't',  'v', '\\' };
size_t ReadlinePromptHistory::esc_char_size = sizeof ( ReadlinePromptHistory::esc_char );

char * ReadlinePromptHistory::descape ( const char * buffer ) {
	size_t l = strlen ( buffer );

	char * dest = ( char * ) malloc ( ( l + 1 ) * sizeof ( char ) );
	char * ptr = dest;
	for ( size_t i = 0; i < l; ++ i ) {
		if ( buffer [ i ] == '\\' ) {
			++ i;
			size_t j = std::find ( essc_str, essc_str + esc_char_size, buffer [ i ] ) - essc_str;
			if ( j == esc_char_size ) {
				free ( dest );
				return strdup ( buffer );
			}
			* ptr ++ = esc_char [ j ];
		} else {
			* ptr ++ = buffer [ i ];
		}
	}
	* ptr = '\0';

	return dest;
}

char * ReadlinePromptHistory::escape ( const char * buffer){
	size_t l = strlen ( buffer );

	char * dest = ( char * ) malloc ( ( l * 2 + 1 ) * sizeof ( char ) );
	char * ptr = dest;
	for ( size_t i = 0; i < l; ++ i ) {
		size_t j = std::find ( esc_char, esc_char + esc_char_size, buffer [ i ] ) - esc_char;
		if ( j == esc_char_size ) {
			* ptr ++ = buffer [ i ];
		} else {
			* ptr ++ = '\\';
			* ptr ++ = essc_str [ j ];
		}
	}
	* ptr = '\0';

	return dest;
}

template < class Callable >
void ReadlinePromptHistory::history_transform ( Callable callable ) {
	HIST_ENTRY ** history = history_list ( );
	if ( history ) {
		int i = 0;
		while ( * history ) {
			char * tmp = callable ( ( * history )->line );
			replace_history_entry ( i, tmp, ( * history )->data );
			free ( tmp );
			++ history;
			++ i;
		}
	}
}

ReadlinePromptHistory::ReadlinePromptHistory ( std::string history_file ) : m_history_file ( std::move ( history_file ) ) {
	ReadlinePromptHistory::readHistory ( m_history_file );
}

ReadlinePromptHistory::~ ReadlinePromptHistory ( ) {
	ReadlinePromptHistory::writeHistory ( m_history_file );
	clear_history ( );
}

void ReadlinePromptHistory::readHistory ( const std::string & history_file ) {
	read_history ( history_file.c_str ( ) );
	ReadlinePromptHistory::history_transform ( ReadlinePromptHistory::descape );
}

void ReadlinePromptHistory::writeHistory ( const std::string & history_file ) {
	ReadlinePromptHistory::history_transform ( ReadlinePromptHistory::escape );
	write_history ( history_file.c_str ( ) );
}

void ReadlinePromptHistory::addHistory ( const std::string & line ) {
	add_history ( line.c_str ( ) );
}
