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

// genode's base.a
#include <os/reporter.h>  // member

// libc.lib.so
#include <stdio.h>

// haiku.lib.so
#include <app/Application.h>
#include <app/Roster.h>
#include <interface/Button.h>
#include <interface/StringView.h>
#include <interface/Window.h>
#include <support/StringList.h>
#include <storage/Directory.h>
#include <storage/File.h>



class Win : public BWindow
{
	typedef BWindow inherited;

public:
		Win();
		
		void MessageReceived( BMessage * msg ) override;

private:  // Code
		void lineFeed( BRect & r );
		BButton & add( BRect & r, const char * label, const char * pkg, const char * path_pkg_override = NULL, const char * config_override = NULL );
		void downloadInstallPkg( const char * pkg );
		void deployRunPkg( const char * pkg, const char * config_override );

private:  // Data
		Genode::Reporter installerTx;

private:  // Constants
		enum
		{
			msgbase = 'msg0',
			
			DOWNLOAD,    // AddString( "pkg".. )
			RUN  // AddString( "pkg".. )
		};
};



//#pragma mark -


Win::Win()
:	inherited( BRect(50., 50., 330., 470.), "GeDepot", B_DOCUMENT_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_ASYNCHRONOUS_CONTROLS )
	,installerTx( be_app->Env(), "installation", "installation_for_manager" )
{
	BRect r( 0., 0., 65., 30. );
	lineFeed( r );
	
	// Create install/run buttons for some selected packages, from these depot/users:
	// - https://depot.genode.org/genodelabs
	// - https://depot.genode.org/cnuke
	// - https://depot.genode.org/cproc
	// - https://depot.genode.org/jschlatow
	// - https://depot.genode.org/nfeske
	// - https://depot.genode.org/skalk
	//
	// Demonstrate "overriden-config" launch ; a bit clunky as one has to specify the path to the binary, since we don't use the provided runtime:
	add( r, "nano3d:", "genodelabs/pkg/nano3d/2024-11-19"  // download id
		,"/boot/depot/genodelabs/bin/x86_64/nano3d/2024-11-19/nano3d"  // provide run path since we won't use the runtime
		,"<config painter=\"textured\" shape=\"cube\" />" );  // override runtime (config)
	add( r, "bubble universe", "nfeske/pkg/bubble_universe/2024-11-05" );
	add( r, "wifi:", "genodelabs/pkg/pc_wifi/2024-11-19" )  // includes extra firmwares for T-series laptops (as per #5282)
		.Hide();
	add( r, "MESA-cpu:", "genodelabs/pkg/mesa_gpu-cpu/2024-11-19" )
		.Hide();
	add( r, "gears:", "genodelabs/pkg/mesa_gears/2024-11-19" );
	add( r, "textedit:", "genodelabs/pkg/qt5_textedit/2024-11-19" );
///later: find an up-to-date build of Doom or retire this :-)
//	add( r, "doom (23.05)", "cnuke/pkg/chocolate-doom/2023-04-27" );  // no longer works in 23.08+, symbol not found: genode_envp
//	add( r, "chocolate-doom-skalk:", "skalk/pkg/chocolate-doom/2023-10-23" );   NO GO, blocks after opening Audio session to mixer...
	add( r, "falkon:", "cproc/pkg/falkon-jemalloc/2024-10-28" );  // this package sets a ceiling on Falkon RAM usage to 2 GB -- this can be edited in depot/cproc/pkg/falkon-jemalloc/date../runtime (change 2GB to 16G) and in raw/falkon-jemalloc/date../init.config (change 1990 M to 15990 M) -- UPD (dec24): only one place to edit now, not two ? Or even provide a RAM override sans launcher ?
	//add( r, "morph:", "cproc/pkg/morph_browser/2023-04-26" );  // _d_ this ?
	add( r, "VNC server:", "jschlatow/pkg/vnc_server/2024-10-09" );
	
///later: system_info LVGL creates a *new* window, instead of targetting the background.... Need to: 1) create a background ?  2) make it target that background ? See the launcher/Routing here:  http://genodians.org/jschlatow/2024-02-07-system-info
//	add( r, "LVGL SysInfo", "jschlatow/pkg/system_info/2024-02-06" );
	
	//add BStringView( "Depot packages" );
	// addPkg()
	//
	//add BStringView( "Built-ins" );
	//	///addBuiltin( "ftpd", "hog_ftp", 6 );
	//	///addBuiltin( "SSH ftpd", "ssh_server", 32 );
}


BButton & Win::add( BRect & r, const char * label, const char * pkg, const char * path_pkg_override, const char * config_override )
{
	BMessage dl( DOWNLOAD );
	{
		dl.SetString( "pkg", pkg );  // e.g. "genodelabs/pkg/nano3d/2023-04-25"
	}
	BMessage run( RUN );
	{
		if( path_pkg_override && config_override )
		{
			run.SetString( "pkg", path_pkg_override );
			run.SetString( "config-override", config_override );
		}
		else
			run.SetString( "pkg", pkg );
	}
	
	BButton * b = nullptr;
	
	AddChild( new BStringView(r, "stringview", label) ); r.OffsetBy( 90., 0. );
	b = new BButton( r, "dload", "Download", new BMessage(dl) );
	AddChild( b ); r.OffsetBy( 80., 0. );
	b = new BButton( r, "run", "Run", new BMessage(run) );
	AddChild( b ); r.OffsetBy( 80., 0. );
	
	lineFeed( r );
	
	return *b;  // so that caller may Hide() it in some (special) cases
}

void Win::lineFeed( BRect & r )
{
	r.OffsetTo( 10., r.bottom +10. );
}

void Win::MessageReceived( BMessage * msg )
{
	switch( msg->what )
	{
		case DOWNLOAD:
			msg->PrintToStream();
			downloadInstallPkg( msg->FindString("pkg") );
		break;
		
		case RUN:
			msg->PrintToStream();
			deployRunPkg(
				msg->FindString( "pkg" ),
				msg->FindString( "config-override" )
				);
		break;
		
		default:
			inherited::MessageReceived( msg );
	}
}

void Win::downloadInstallPkg( const char * pkg )
{
	installerTx.enabled( true );
	installerTx.clear();
	
	///ToDo-2: also query index?: <index   path="genodelabs/index/23.04" verify="no" />
	/*
		Or:
			<config arch=\"x86_64\">
				<index user=\"genodelabs\" version=\"19.02\" content=\"yes\"/>
	*/
	///later: set "verify=yes" to check integrity of package (must provide pub key)
	BString s;
	s
		<< "<installation arch=\"x86_64\">\n"
		<< "	<archive path=\"" << pkg << "\"  verify=\"no\"    source=\"no\" />\n"
		<< "</installation>\n"
		;
	
	installerTx.report( s, s.Length() );
}

void Win::deployRunPkg( const char * ge_pkg, const char * config_override )
{
	//const char * argv[2] = { pkg, NULL };
	//load_image( 1, argv, NULL );
	//resume_thread..
	
	// BRoster.Launch() only works with actual files, with /boot prefix, so map e.g.
	// - genodelabs/pkg/nano3d/2023-04-25
	// to
	// - /boot/depot/genodelabs/pkg/nano3d/2023-04-25/runtime
	//
	// Downstream, DeployPilot will simply strip those off.
	//
	BString roster_style_pkg;
	if( BString(ge_pkg).StartsWith("/boot/") )
	{
		// absolute path, use as-is
		roster_style_pkg = ge_pkg;
	}
	else  // package (relative) path:
	{
		roster_style_pkg << "/boot/depot" << "/" << ge_pkg << "/" << "runtime" ;
	}
	
	entry_ref ref;
	
	auto res = get_ref_for_path( roster_style_pkg, &ref );
	printf("get_ref_for_path <%s> -> %d\n", ref.name, res);
	
	printf("entry.exists: %d\n", BEntry(&ref).Exists());
	
	if( NULL == config_override )
		res = be_roster->Launch( &ref );
	else
	{
		BMessage ge_msg;
		{
			ge_msg.AddInt32( "Genode:InitXml:ram_MB", 50 /**/ );
			ge_msg.AddString( "Genode:InitXml:Config", config_override );
		}
		res = be_roster->Launch( &ref, &ge_msg );
	}
	printf("launch <%s> -> %d\n", ref.name, res);
}



//#pragma mark -


class App : public BApplication
{
	typedef BApplication inherited;
public:
		App()
		:	inherited( "application/x-vnd.HoG-GeDepot" )
		{
		}
		
		void ReadyToRun() override
		{
			(new Win)->Show();
		}
};


static void create_depot_folders()
{
	status_t res = 0;
	
	res = create_directory( "/boot/public", 0777 );
	printf( "  ..createdir 'public' -> 0x%x\n", res );
	
	res = create_directory( "/boot/depot/genodelabs", 0777 );
	printf( "  ..createdir -> 0x%x\n", res );
	res = create_directory( "/boot/depot/cnuke", 0777 );
	printf( "  ..createdir -> 0x%x\n", res );
	res = create_directory( "/boot/depot/cproc", 0777 );
	printf( "  ..createdir -> 0x%x\n", res );
	res = create_directory( "/boot/depot/jschlatow", 0777 );
	printf( "  ..createdir -> 0x%x\n", res );
	res = create_directory( "/boot/depot/nfeske", 0777 );
	printf( "  ..createdir -> 0x%x\n", res );
	res = create_directory( "/boot/depot/skalk", 0777 );
	printf( "  ..createdir -> 0x%x\n", res );
	
	const char url[] = "https://depot.genode.org";
	size_t written = 0;
	
	written = BFile( "/boot/depot/genodelabs/download", B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE )
		.Write( url, strlen(url) );
	if( written != strlen(url) ) printf( "** failed creating depot 'download' url\n" );
	written = BFile( "/boot/depot/cnuke/download", B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE )
		.Write( url, strlen(url) );
	if( written != strlen(url) ) printf( "** failed creating depot 'download' url\n" );
	written = BFile( "/boot/depot/cproc/download", B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE )
		.Write( url, strlen(url) );
	if( written != strlen(url) ) printf( "** failed creating depot 'download' url\n" );
	written = BFile( "/boot/depot/jschlatow/download", B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE )
		.Write( url, strlen(url) );
	if( written != strlen(url) ) printf( "** failed creating depot 'download' url\n" );
	written = BFile( "/boot/depot/nfeske/download", B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE )
		.Write( url, strlen(url) );
	if( written != strlen(url) ) printf( "** failed creating depot 'download' url\n" );
	written = BFile( "/boot/depot/skalk/download", B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE )
		.Write( url, strlen(url) );
	if( written != strlen(url) ) printf( "** failed creating depot 'download' url\n" );
}


int main()
{
	// the <import> tag fails with vfs_ntfs (OPEN_ERR_UNACCESSIBLE...), so let's create the files 'by hand' here instead:
	create_depot_folders();
	
	App().Run();
	
	return 0;
}


