/*
 * Copyright 2022-2023, ttcoder
 * All rights reserved. Distributed under the terms of the MIT license.
 */


// project
#include "SlotItem.h"

// this
#include "SlotIO.h"


/********/
static int debug = 0;


//done ///ToDo: instead of "." prefix, use eg "+" or "<tab>" prefix for attr lines
#define attr_KVP_LineHeading '\t'
#define attr_KVP_StrLineHeading "\t"



// This impl. is not needed when running in a "C" host (e.g. basic-layers test binary), but is needed when running in a pure-Genode host (e.g. vfs):
const void * memchr( const char * s, int ch, size_t span )
{
	const char * end = s + span;
	
	for( const char * i = s; i < end; i++ )
		if( ch == *i )
			return i;
	
	return nullptr;
}



//#pragma mark -


SlotIO::SlotIO( Genode::Allocator & alloc )
:	inherited( alloc )
	,curByte( 0 )
{
}


void SlotIO::SetAs( const void * index_contents, unsigned size )
{
	// augment
	curByte = 0;
	
	// inherited
	Clear();
	Append( index_contents, size );
}

void SlotIO::CheckIntegrity()
{
	//if( debug )
		Genode::log( "CheckIntegrity of Attr-Index (", BufferLength(), " bytes)" );
	
	if( false == Rewind() )
		//
		return;
	
	unsigned count_nodes = 0;
	unsigned count_attributes = 0;
	
	do
	{
		if( debug >= 4 )
		{
			PickStr line( curLine(), curLineSize() -sizeof('\n') );
			Genode::log( "<", line.string(), ">" );
		}
		SlotItem item( heapAlloc );
		GetCurrentSlot( item );
		
		// T0
		count_nodes += 1;
		count_attributes += item.CountAttrs();
		
		if( debug >= 3 )
			Genode::log( "+ node <", item.FullPath(), "> with ", item.CountAttrs(), " attributes" );
	} while( NextSlot() );
	
	//if( debug )
		Genode::log( "Found ", count_attributes, " attribute(s) in ", count_nodes, " node(s)." );
}



//#pragma mark -


void SlotIO::AppendSlot( const SlotItem & slot )
{
	if( debug )
		Genode::log( " AppendSlot" );
	
	BufIO buf( heapAlloc );
	{
		if( slot.FullPath()[0] != '/' )
		{
			Genode::error( "FAILED ASSERT, corrupt index: index entries must start with a slash!" );
			
			return;
		}
		///assert no \n inside val...
		buf.AppendStr( slot.FullPath() );//, Genode::strlen(slot.FullPath()) );
		buf.AppendStr( "\n" );
		
		for( const KeyValPair * pair = slot.first(); pair; pair = pair->next() )
		{
			///assert no '=' inside *key*...
			///assert no \n inside val...
			///assert no "/" inside val, so that it can be queried via "dot/slash path"...
///why do we need -1 on these: ?
			buf.AppendStr( attr_KVP_StrLineHeading );
			buf.Append( pair->key.string(), pair->key.length() -1 );
			buf.AppendStr( "=" );
			buf.Append( pair->value.string(), pair->value.length() -1 );
			buf.AppendStr( "\n" );
			
			//if( debug >= 5 )
			//	buf.Dump();
		}
	}
	
	Append(
		buf.Buffer(),
		buf.BufferLength()
		);
	
	if( debug >= 4 )
		Dump();
}


///+ "Decode"CurrentSlot()
void SlotIO::GetCurrentSlot( SlotItem & into )
{
	if( debug )
		Genode::log( " GetCurSlot" );
	
	const unsigned restore = curByte;
	
	if( curLine()[0] != '/' )
	{
		Genode::error( "idx: SlotIO.GetCurrentSlot(): we're supposed to be positioned on a slot, but slash is missing !" );
		return;
	}
	
	into.Clear();
	
	into.SetFullpath(
		curLine(),
		curLineSize() -strlen("\n")
		);
	
	curByte += curLineSize();
	
	while( curLine()[0] == attr_KVP_LineHeading && curByte < BufferLength() )
	{
		if( debug >= 4 )
			Genode::log( "  getcurslot: processing line at ", curByte, " with size ", curLineSize() );
		
		PickStr line(
				curLine() + strlen( attr_KVP_StrLineHeading ),
				curLineSize() -strlen( "\n" ) -strlen( attr_KVP_StrLineHeading )
				);
		//const char * sign = (const char*)memchr( curLine(), '=', BufferLength() -curByte );
		const char * sign = (const char*)memchr( line.string(), '=', line.length() );
		
		if( debug >= 4 )
			Genode::log( "  getcurslot: picked-sub-str <", line.string(), ">" );// from curLine <", curLine(), ">" );
		
		if( sign )
		{
			PickStr key(
				line.string(),//curLine() + strlen(attr_KVP_StrLineHeading),
				sign - line.string()//curLine()
				);
			PickStr value(
				sign + strlen("="),
				(line.string() + line.length()) - (sign +strlen("="))//curLineSize() - (sign -curLine()) -strlen("\n")
				);
			
			///ToDo: is it here we should handle B_INT32_TYPE etc? :
			into.SetAttr( key.string(), value.string() );
		}
		else
			Genode::error( "idx: corrupt index: cannot find an 'equal' sign in attribute definition <", line.string(), ">" );
		
		if( curLineSize() < 1 )
		{
			Genode::error( "empty line ?!?" );
			break;
		}
		
		curByte += curLineSize();
	}
	
	curByte = restore;
}

bool SlotIO::FindByFullpath( const char * fullpath )
{
	if( debug )
		Genode::log( " FindByFullpath <", fullpath, ">:" );
	
	for( curByte = 0; curByte < BufferLength(); curByte += curLineSize() )
	{
		if( curLine()[0] != '/' )
			continue;
		
		PickStr line( curLine(), curLineSize()-strlen("\n") );
		
		if( 0==Genode::strcmp(line.string(), fullpath) )
			// leave <curByte> unchanged and return success:
			return true;
	}
	
	return false;
}


void SlotIO::RemoveCurrentSlot()
{
	if( debug )
		Genode::log( " RemoveCurSlot" );
	
	const unsigned beginning = curByte;
	
	do
	{
		curByte += curLineSize();
		
		if( debug >= 4 )
			Genode::log( "     curByte advanced to ", curByte );
	} while( curByte < BufferLength() && curLine()[0] == attr_KVP_LineHeading );
	
	const int cut_size = curByte -beginning;
	
	if( debug >= 3 )
	{
		Genode::log( " .Erase(", beginning, ", ", beginning, "+", cut_size, ") from ", BufferLength(), " bytes big index" );
		
		PickStr line( static_cast<const char*>( Buffer() ) + beginning, cut_size );
		Genode::log( "  -> i.e. erase <", line.string(), ">" );
	}
	
	// T0
	//
	Erase(
		beginning,
		cut_size
		);
	
	if( debug >= 4 )
		Dump();
	
	// we've been iterating <curByte> (in the do..while), into a (now) invalidated pos, set it back to beginning
	curByte = 0;
}


bool SlotIO::Rewind()
{
	curByte = 0;
	
	return curByte < BufferLength();
}

bool SlotIO::NextSlot()
{
	do
	{
		curByte += curLineSize();
	}
	while( curByte < BufferLength() && curLine()[0] != '/' );
	
	return curByte < BufferLength();
}


const char * SlotIO::curLine() const
{
	return static_cast<const char*>( Buffer() ) + curByte;
}

unsigned SlotIO::curLineSize() const
{
	//Genode::log( "         cursize: BufferLength ", BufferLength(), " curByte ", curByte, " curLine <", curLine(), ">" );
	
	const char * past_feed = (const char*)memchr(
		curLine() +1,  // +1: we MUST make caller advance by at least one byte, so make sure to NOT match a potential "in place" linefeed
		'\n',
		BufferLength() -curByte /// -1 ?
		);
	
	if( past_feed )
		past_feed += strlen( "\n" );
	else
		past_feed = static_cast<const char*>(Buffer()) + BufferLength();
	
	return past_feed - static_cast<const char*>(Buffer()) - curByte;
}

