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

// base
#include <base/log.h>

// haiku.lib.so / libroot-build (this does not incur a linking dependancy as we just use a #define)
#include "_GeShared_xattr_defines.h"

// project
#include "SlotItem.h"

// this
#include "IndexStore.h"

// - namespaces -
using Genode::log;
using Genode::warning;


/**************/
static int debug = 1;



IndexStore::IndexStore( Genode::Env & env )
:	memHeap( env.ram(), env.rm() )
	,slotIO( memHeap )
	,resultsIO( memHeap )
{
}

void IndexStore::NodeDeleted( const char * fullpath )
{
	if( debug >= 3 )
		log( " idx.NodeDeleted <", fullpath, ">" );
	
	auto found = slotIO.FindByFullpath( fullpath );
	if( false == found )
	{
		///xx do we have a policy of keeping slots for all files, even attr-less ? If so, no file should be absent from our index, and this warning is warranted ; if not, no warning should occur
		log( "idx.NodeDeleted: I know nothing about this file: ", fullpath );
		
		return;
	}
	// else, remove from index:
	
	// T0
	slotIO.RemoveCurrentSlot();
	
	if( debug >= 5 )
		slotIO.Dump();
	
	//slotIO.CheckIntegrity();
}

void IndexStore::NodeMoved( const char * from, const char * to )
{
	if( debug >= 3 )
		log( " idx.NodeMoved from <", from, "> to <", to, ">" );
	
///ToDo: in fact if ANY folder at all gets renamed or moved, ALL of its sub-folders and sub-files at ANY depth must have their entry synced in the whole database !
	auto found = slotIO.FindByFullpath( from );
	if( false == found )
	{
		///xx do we have a policy of keeping slots for all files, even attr-less ? If so, no file should be absent from our index, and this warning is warranted ; if not, no warning should occur
		log( "idx.NodeMoved: I know nothing about this file: ", from );
		
		return;
	}
	// else, remove from index:
	
	SlotItem slot( memHeap );
	slotIO.GetCurrentSlot( slot );
	slot.SetFullpath( to, Genode::strlen(to) );
	
	// T0
	slotIO.RemoveCurrentSlot();
	slotIO.AppendSlot( slot );
	
	if( debug >= 5 )
		slotIO.Dump();
	
	//slotIO.CheckIntegrity();
}


void IndexStore::AttrDeleted( const char * raw_attrname, const char * from_node )
{
	if( debug >= 3 )
		log( " idx.AttrDeleted raw-attr:<", raw_attrname, "> from <", from_node, ">" );
	
	// if ! attrname.StartsWith( GeShared_Attribute_Namespace ) ...
	if( Genode::strcmp(raw_attrname, GeShared_Attribute_Namespace, Genode::strlen(GeShared_Attribute_Namespace)) != 0 )
	{
		warning( " idx.attrdel: this is not a Haiku attribute, skipping" );
		
		return;
	}
	
	const char * const attrname =
		raw_attrname
		+ Genode::strlen( GeShared_Attribute_Namespace )  // "user.haiku."
		;
	
	auto found = slotIO.FindByFullpath( from_node );
	if( false == found )
	{
		log( "idx.AttrDeleted: I know nothing about this file: ", from_node );
		
		return;
	}
	// else, remove from index:
	
	SlotItem slot( memHeap );
	slotIO.GetCurrentSlot( slot );
	if( false == slot.HasAttr(attrname) )
	{
		if( debug >= 5 )
			Genode::log( "idx.AttrDeleted: the file exists, but not the attribute -- for this file: ", from_node );
		
		return;
	}
	// else:
	slot.DeleteAttr( attrname );
	
	// T0
	slotIO.RemoveCurrentSlot();
	slotIO.AppendSlot( slot );
	
	if( debug >= 5 )
		slotIO.Dump();
	
	//slotIO.CheckIntegrity();
}


static type_code typeForStr( Genode::String<4+1> str )
{
	type_code type = 0;
	
	if( 0 == Genode::strcmp(str.string(), "RTSC", 4) )
	{
		type = B_STRING_TYPE;
	}
	/*
		///ToDo: to support "Media:Length" etc, we'll need part of these:
	B_DOUBLE_TYPE
	B_FLOAT_TYPE
	B_INT16_TYPE
	B_INT32_TYPE
	B_INT64_TYPE
	B_INT8_TYPE
	B_MESSAGE_TYPE
	B_MIME_TYPE  // to handle things like, BEOS:TYPE=SMIMtext/plain
	B_RAW_TYPE
		B_STRING_TYPE
	B_TIME_TYPE
	B_UINT16_TYPE
	B_UINT32_TYPE
	B_UINT64_TYPE
	B_UINT8_TYPE
	B_MIME_STRING_TYPE
	*/
	
	//Genode::log( "     found type : ", Genode::Hex(type), " from string <", str, ">" );
	
	return type;
}


void IndexStore::AttrMutated(
			const char * fullpath,
			const char * raw_attrname,
			const void * buf,
			long buf_size
			)
{
	if( debug >= 3 )
		log( " idx.AttrMutated file <", fullpath, "> raw-attr <", raw_attrname, ">" );
	
	// if ! attrname.StartsWith( GeShared_Attribute_Namespace ) ...
	if( 0 != Genode::strcmp(raw_attrname, GeShared_Attribute_Namespace, Genode::strlen(GeShared_Attribute_Namespace)) )
	{
		warning( " idx.attrmutated: this is not a Haiku attribute, skipping: <", raw_attrname, ">" );
		//
		return;
		//
	}
	
	const char * const attrname =
		raw_attrname
		+ Genode::strlen( GeShared_Attribute_Namespace )  // "user.haiku."
		;
	const PickStr value( (const char*)buf, buf_size );
	
	// analyse the value's _first four_ bytes, which look something like "CSTR" ("RTSC"):
	const Genode::String<4+1> type_str = value.string();
	;
	switch( typeForStr(type_str) )
	{
		//++ case integer/float/double: snprintf( "%d or %f", val ), then fall-through
		// break;
		
		case B_STRING_TYPE:
			// supported "as-is" -> proceed
		break;
		
		default:
			if( debug )
				Genode::log( "indexer: ignoring non-indexable attribute, unsupported attribute type: ", type_str );  // Disregard non-numeric/non-str values, same as BeOS and Haiku do
			//
			return;
			//
	}
	
	if( debug >= 3 )
		log( " idx.AttrMutated -> raw-buf <", (const char*)value.string(), ">" );
	
	auto found = slotIO.FindByFullpath( fullpath );
	if( false == found )
	{
		// create new slot
		
		if( debug >= 4 )
			Genode::log( "idx: this file was never set-attr'ed ; let's create a new slot for that file: ", fullpath );
		
		SlotItem slot( memHeap );
		slot.SetFullpath( fullpath, Genode::strlen(fullpath) );
		slot.SetAttr( attrname, value.string() );
		slotIO.AppendSlot( slot );
	}
	else
	{
		// mutate existing slot
		
		SlotItem slot( memHeap );
		slotIO.GetCurrentSlot( slot );
		
		slot.SetAttr( attrname, value.string() );
		
		slotIO.RemoveCurrentSlot();
		slotIO.AppendSlot( slot );
	}
	
	if( debug >= 5 )
		slotIO.Dump();
	
	//slotIO.CheckIntegrity();
}



//#pragma mark -


const char * IndexStore::IndexBuffer() const
{
	return static_cast<const char*>( slotIO.Buffer() );
}
unsigned IndexStore::IndexBufferSize() const
{
	return slotIO.BufferLength();
}

void IndexStore::SetAs_And_Check( const void * index_contents, unsigned size )
{
	slotIO.SetAs( index_contents, size );
	slotIO.CheckIntegrity();
	
	if( debug >= 2 )
		slotIO.Dump();
}



//#pragma mark -


void IndexStore::SetQuery( const char * predicate )
{
	if( debug >= 2 )
		Genode::log( "idx.SetQuery <", predicate, ">" );
	
	// T-
	resultsIO.Clear();
	
	if( nullptr == predicate || '\0' == predicate[0] )
		//
		return;
		//
	
	int statistics = 0;
	
	// do an (expensive) walk over each Index entry
	//
	if( slotIO.Rewind() )
	{
		if( debug >= 5 )
			slotIO.Dump();
		
		do
		{
			SlotItem item( memHeap );
			slotIO.GetCurrentSlot( item );
			
			if( debug >= 5 )
				Genode::log( "  examining ", item.FullPath(), " ..." );
			
			if( item.MatchesPredicate(predicate) )
			{
				// T0
				resultsIO.AppendStr( item.FullPath() );
				resultsIO.Append( "\0", 1 );
				
				statistics += 1;
			}
		}
		while( slotIO.NextSlot() );
		
	}
	
	if( debug >= 2 )
		Genode::log( "idx: found ", statistics, " query entries" );
}

/*
static const char temp[] =
		"/Music/mpeg_file"
		"\0"
		"/Music/the-wall.mp3"
		;
*/

const void * IndexStore::ResultsBuf() const
{
	return resultsIO.Buffer();//temp;
}

int64 IndexStore::ResultsSize() const
{
	return resultsIO.BufferLength();//sizeof(temp);
}


