#ifndef xattr_mapping_H
#define xattr_mapping_H
/*
 * Copyright 2021-2023, ttcoder of TTS-LLC
 */


/* Genode includes */
//#include <os/path.h>
//#include <os/vfs.h>  // Directory::Path
#include <vfs/directory_service.h>

//xx namespace {

	using namespace Vfs;

//};


#ifndef DISABLE_XATTR

/***************/
#define TRACE_Xa(x...) ;
//#define TRACE_Xa  Genode::log

// In Genode, extended-attribute (xattr) access à la setxattr() is initiated
// by ioctl() calls, which get translated into special file paths.
// For the background discussion on this see: https://github.com/genodelabs/genode/issues/4346
// Hence these classes that help parse/map file path ops into xattr ops.


class VirtualizedListingXa
{
	private:  // Types

		enum { maxlen = 512 };///
		
//		typedef Genode::Directory::Path Path;
		typedef Genode::Path<maxlen> Path;
		

	private:  // Data

		Vfs::Directory_service::Stat_result
			_initcheck;
		Path//<maxlen>
			_realfile_path;

	public:

		VirtualizedListingXa(char const * path)
		:	_initcheck(Vfs::Directory_service::STAT_ERR_NO_ENTRY)
			,_realfile_path()
		{
			using Genode::log;
			using namespace Vfs;
			
			TRACE_Xa("     VirtualizedListingXa path=", path );
			
			// split path into values that are "candidate" for a match:
			Path/*<maxlen>*/ parent_dotted;  // dot-prefixed variant of a real file name
			Path/*<maxlen>*/ litteral;  // virtual (not an actually separate file) file's name
			{
				parent_dotted.import(path);
				parent_dotted.strip_last_element();  // go to parent node, whose name ought to start with a dot, e.g. ".testfile"
				
				litteral.import(path);
				litteral.keep_only_last_element();  // leaf name ought to be an "attr_list" litteral
			}
			
			if (parent_dotted.last_element()[0] == '.'
				&& litteral.equals("/attr_list"))
			{
				_initcheck = Directory_service::STAT_OK;
				
				Path/*<maxlen>*/ p;
				{
					p.import(parent_dotted.string());
					// go to parent
					p.strip_last_element();
					// go back down to node, but without its "." dot prefix
					p.append_element(parent_dotted.last_element() + sizeof('.'));  // e.g. append "test.tst" (instead of ".test.tst")
				}
				_realfile_path = p;
				
				TRACE_Xa("  _!_ VirtualizedListingXa match, this is a request for Attribute Listing of file: ", p.string() );
			}
		}

		void debug_dump()
		{
			TRACE_Xa( "  VirtualizedXattrLISTING  init <", int(_initcheck), "> real-path <", _realfile_path, ">" );
		}
		
		Vfs::Directory_service::Stat_result initcheck() const
		{
			return _initcheck;
		}
		
		char const * real_path() const
		{
			return _realfile_path.string();
		}
};


class VirtualizedXattr
{
	private:  // Types
		enum { maxlen = 2 * MAX_PATH_LEN };//
		typedef Genode::Path<maxlen> Path;

	private:  // Data

		Vfs::Directory_service::Stat_result
			_initcheck;
#if 0
		Directory_service::Stat
			_out; ///xx rename to _stat ..
#endif
		Path//<maxlen>
			_physical_path;  // actual file that holds xattr data
		Genode::String<128>  // no need for 512 len here
			_xattr_name;

	public:
		
		VirtualizedXattr( const char * const path )
		:	_initcheck( Vfs::Directory_service::STAT_ERR_NO_ENTRY )
		{
			using namespace Vfs;
			
			// Attempt to open the "virtual extended attributes" counterpart of 'path'
			// Tentatively split 'path' into these trailing components:
			// 1) a dot-prefixed leafname
			// 2) the string litteral "attr"
			// 3) the attribute's name
			
			// path is e.g. "/.Music"
			TRACE_Xa("     VirtualizedXattr path=", path );

			Path/*<maxlen>*/ dot_leafname;
			Path litteral;
			Path attr_name;
			{
				attr_name.import(path);
				attr_name.keep_only_last_element(); //xx this preserves the heading slash!! so take that into account, test for "/foo" and not "foo" (down there)
#if 1
// now that we no longer use ioctl(), but access directly the "dot path" from LibC, we end up here through stat() :-(
if( attr_name.last_element()[0] == '.' )
{
	TRACE_Xa("     xa tokens: case 'early ok', this (might be) the direct-libc-dot-access case");
	
	_physical_path.import( path );  // make sure real_path() returns the actual (unchanged) path, in case we are called for non-xattr related business
	_xattr_name = "///early-parsing-case///";
	
	//xxxxx do not touch _out, but set _initcheck to OK ?
	//xxxxx or find another status, like "partial match" ?
	_initcheck = Directory_service::STAT_OK;
	;
	return;
}
#endif
				
				litteral.import(path);
				litteral.strip_last_element();
				litteral.keep_only_last_element();

				dot_leafname.import(path);
				dot_leafname.strip_last_element();
				dot_leafname.strip_last_element();
				dot_leafname.keep_only_last_element();
#if 1
// now that we no longer use ioctl(), but access directly the "dot path" from LibC, we end up here through stat() :-/
if( litteral.last_element()[0] == '.' && attr_name.equals("/attr") )
{
	TRACE_Xa("  Second-EARLY Ok, this is the direct-libc-dot-access case");
	
	_physical_path.import( path );  // make sure real_path() returns the actual (unchanged) path, in case we are called for non-xattr related business
	_xattr_name = "///second-early-parsing-case///";
	
	//xxxx see above
	_initcheck = Directory_service::STAT_OK;
	;
	return;
}
#endif
				// this is a _key_ decision point here:
				TRACE_Xa( "      litteral=", litteral.last_element(), " dot_leafname=", dot_leafname.last_element(), " attr_name=", attr_name.last_element());
			}

			if(litteral.equals("/attr")
				&& dot_leafname.last_element()
				&& dot_leafname.last_element()[0] == '.')
			{
				TRACE_Xa(" == Valid XAttr magic found: rebuild path to actual/physical file from parent dir ==");
				
//				if(parent.last_element() && parent.last_element()[0] == '.')
				{
					Path/*<maxlen>*/ parent_parent;
					parent_parent.import(path);
					parent_parent.strip_last_element();
					parent_parent.strip_last_element();
					parent_parent.strip_last_element();
					
					// rebuild a file path, but this time without a heading "dot":
					//
					Path/*<maxlen>*/ counterpart;
					counterpart.import(parent_parent.string());  // e.g. "/testdir"
					counterpart.append_element(dot_leafname.last_element() + sizeof('.'));  // e.g. append "test.tst" (instead of ".test.tst")
					TRACE_Xa(" deal with: ", counterpart);
					
/////// Do Not call FUSE here, this header is also used by the indexer !
					// fuse.getattr()
#if 0
					struct stat s;
					int res = Fuse::fuse()->op.getattr(counterpart.string(), &s);
					if(res == 0)
#endif
					{
						//log(" Yes, the physical counterpart does exist, so perform ioctl!");
						
						_physical_path = counterpart;
						
						_xattr_name = attr_name.last_element();
						
#if 0
						_out.device = (Genode::addr_t)this;
						_out.inode = s.st_ino ? s.st_ino : 1;  // dodge "0" inode as that would break LibC
						_out.size  = 99;//s.st_size; ///XXXXX
						_out.rwx   = Node_rwx::rw();//?
						//out.type  = Node_type::DIRECTORY; <- use this for the "XATTR_LIST" ioctl ? or rather a pseudo-text file that contains more information
						_out.type  = Node_type::CONTINUOUS_FILE;  // <- use this for the "XATTR_GET/SET" ioctl
#endif
						
//what if create==true?
///ToDo: if( fuse.getXattr() success ) _initcheck = OPEN_ERR_EXISTS;
						_initcheck = Directory_service::STAT_OK;
						;
	//					TRACE_Xa("  .._!_ VirtualizedXattr match, We'll handle this as an xattr" );
					}
				}
			}
	/*
					// e.g. we're being called on a dot-file such as: stat path=/testdir/.test.tst/info
					// and that dot-file does not exist physically
					// (meaning this originates from the libc: ioctl(fd, XATTR_SET, buf_xa)...)
			*/
		}

		void debug_dump()
		{
			TRACE_Xa( "  VirtualizedXattr  init=", int(_initcheck), " phys-path=", _physical_path, " xattrname=", _xattr_name );
		}
		
		Vfs::Directory_service::Stat_result initcheck() const
		{
			return _initcheck;
		}
		
#if 0
		Directory_service::Stat attr_status() const
		{
			return _out;
		}
#endif
		
		char const * real_path() const
		{
			return _physical_path.string();
		}
		
		char const * xattr_name() const
		{
			return _xattr_name.string();
		}
};

#undef TRACE_Xa
#endif  // DISABLE_XATTR

#endif // xattr_mapping_H

