/*
* Copyright (c) 2025, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/

#if defined(__HAIKU__)

#include <memory>
#include <string>
#include <MenuItem.h>
#include <MenuBar.h>
#include <TextView.h>
#include <Font.h>
#include <MessageRunner.h>
#include <StringItem.h>
#include <ListView.h>
#include <TabView.h>
#include <ScrollView.h>
#include <Button.h>
#include <Window.h>
#include <Application.h>
#include <Alert.h>
#include<sstream>
//#include<Roster.h>

#include "version.h"
#include "Log.h"
#include "Config.h"
#include "RouterContext.h"
#include "Tunnel.h"
#include "Transports.h"
#include "Daemon.h"
#include "ClientContext.h"
enum
{
	M_GRACEFUL_SHUTDOWN = 1,
	M_RUN_PEER_TEST,
	M_DUMMY_COMMAND,
	C_GRACEFUL_SHUTDOWN_UPDATE,
	C_MAIN_VIEW_UPDATE,
	M_SHOW_TUNNEL,
	C_OPENHTTP,
	M_SHOWMAIN,
        M_CLOSETUNNEL
};	
constexpr bigtime_t GRACEFUL_SHUTDOWN_UPDATE_INTERVAL = 1000*1100; // in microseconds, ~ 1 sec
constexpr int GRACEFUL_SHUTDOWN_UPDATE_COUNT = 600; // 10 minutes
constexpr bigtime_t MAIN_VIEW_UPDATE_INTERVAL = 5000*1000; // in miscroseconds, 5 secs

class MainWindow: public BWindow
{
	public:
		MainWindow ();
	
	private:
		void MessageReceived (BMessage * msg) override;	
		bool QuitRequested () override;
		void GetInfoAboutTunnel(BMessage * msg);	
		void UpdateMainView ();
		void OpenHTTPInterface(void);
		void DrawTunnel(std::stringstream & s);
		size_t m_counter_streams = 0;
	private:
		BMessenger m_Messenger;
		BTabView * m_tab_view;
		BTextView * m_MainView;
		std::unique_ptr<BMessageRunner> m_MainViewUpdateTimer, m_GracefulShutdownTimer;	
		bool m_IsGracefulShutdownComplete = false;
		bool m_IsDrawTunnel = false;
		i2p::data::IdentHash m_IdentHash;
};	

class I2PApp: public BApplication
{
	public:
	
		I2PApp ();
		void CreateMainWindow ();
};

MainWindow::MainWindow ():
	BWindow (BRect(100, 100, 500, 400), "i2pd " VERSION, B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE),
	m_Messenger (nullptr, this)
{	
	auto r = Bounds (); r.bottom = 20;
	auto menuBar = new BMenuBar (r, "menubar");
	AddChild (menuBar);
	auto runMenu = new BMenu ("Run");
	runMenu->AddItem (new BMenuItem ("Open web interface", new BMessage(C_OPENHTTP)));
	runMenu->AddItem (new BMenuItem ("Graceful shutdown", new BMessage (M_GRACEFUL_SHUTDOWN), 'G'));
	runMenu->AddItem (new BMenuItem ("Show Main page", new BMessage (M_SHOWMAIN), 'M'));
	runMenu->AddItem (new BMenuItem ("Quit", new BMessage (B_QUIT_REQUESTED), 'Q'));
	menuBar->AddItem (runMenu);
	auto commandsMenu = new BMenu ("Commands");
	commandsMenu->AddItem (new BMenuItem ("Run peer test", new BMessage (M_RUN_PEER_TEST), 'P'));
	menuBar->AddItem (commandsMenu);
	auto tunnelsMenu = new BMenu ("Tunnels");
	for (auto& it: i2p::client::context.GetClientTunnels ())
	{
		auto msg = new BMessage{M_SHOW_TUNNEL};
		msg->AddString("tunnel_name", BString(it.second->GetName()));
		msg->AddBool("is_client_tunnel", true);
		tunnelsMenu->AddItem (new BMenuItem (it.second->GetName (), msg));
	}
	for (auto& it: i2p::client::context.GetServerTunnels ())
	{
		auto msg = new BMessage{M_SHOW_TUNNEL};
		msg->AddString("tunnel_name", BString(it.second->GetName()));
		msg->AddBool("is_client_tunnel", false);
		tunnelsMenu->AddItem (new BMenuItem (it.second->GetName (), msg));	
	}
	menuBar->AddItem (tunnelsMenu);

	m_tab_view = new BTabView(Bounds().OffsetByCopy(0, 20), "tabview");
	AddChild(m_tab_view);

	BRect tabRect = m_tab_view->ContainerView()->Bounds();
	auto bview = new BView(tabRect, "main_tab", B_FOLLOW_ALL, B_WILL_DRAW);
	bview->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));

	BRect viewRect(10, 10, tabRect.right - 10, tabRect.bottom - 20);
	m_MainView = new BTextView(viewRect,"info_view", viewRect, B_FOLLOW_ALL);
	m_MainView->MakeEditable (false);
	m_MainView->MakeSelectable (true);
	BFont font = *be_plain_font;
	font.SetSize (12);
	rgb_color color;
	color.set_to (0xD4, 0x3B, 0x69);
	m_MainView->SetFontAndColor (&font, B_FONT_ALL, &color);
	m_MainView->SetText ("Starting...");
	bview->AddChild(m_MainView);

	m_tab_view->AddTab(bview);
	m_tab_view->TabAt(m_counter_streams)->SetLabel("Info");
		
	m_MainViewUpdateTimer = std::make_unique<BMessageRunner>(m_Messenger, 
		BMessage (C_MAIN_VIEW_UPDATE), MAIN_VIEW_UPDATE_INTERVAL);
}

void MainWindow::UpdateMainView ()
{
	std::stringstream s;
	if(!m_IsDrawTunnel)
	{
	 i2p::util::PrintMainWindowText (s);
	}
	else 
	{
	  DrawTunnel(s);
        }
	////std::cout << s.str() << std::endl;
	m_MainView->SetText (s.str ().c_str ());
}	
/*
 * Логика - создать вектор с streamID + vector dest.
 * не перерисовывать каждый раз а лишь добавлять которых нет 
 * и удалять те которые есть
 * кнопка close работает
 * */
void MainWindow :: DrawTunnel(std::stringstream & s)
{
       
	s << "\n";
	s << "I2P Address: " << i2p::client::context.GetAddressBook().ToAddress(m_IdentHash) << "\n";
	auto dest = i2p::client::context.FindLocalDestination (m_IdentHash);
	while(m_tab_view->CountTabs() > 1)
	{
		m_tab_view->RemoveTab(m_tab_view->CountTabs() - 1);
	}
	m_counter_streams = 0;
	//std::cout << "Draw List View" << std::endl;;
	if(!dest)
	{
		s << "Can't found local dest\r\n";
	}
	else
	{
		if (dest->IsEncryptedLeaseSet ())
		{
			i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ());
			s << "B33 address: " << blinded.ToB33 () << ".b32.i2p\n";
		}	
		s << "Streams: \n";
		for (auto stream : dest->GetAllStreams())
		{
			std::stringstream s1;
			auto streamDest = i2p::client::context.GetAddressBook().ToAddress(stream->GetRemoteIdentity());
			std::string streamDestShort = streamDest.substr(0,12) + ".b32.i2p";
			s1 << streamDestShort << "|out|" << stream->GetNumSentBytes() << "|in:" << stream->GetNumReceivedBytes() <<"|";

			BRect tabRect = m_tab_view->ContainerView()->Bounds();
			auto bview = new BView(tabRect, streamDestShort.c_str(), B_FOLLOW_ALL, B_WILL_DRAW);
			bview->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));

			BRect viewRect(0, 0, tabRect.right - 100, tabRect.bottom - 100);
			auto tunnel_view = new BTextView(viewRect,"tunnel_view", viewRect, B_FOLLOW_ALL);
			tunnel_view->MakeEditable (false);
			tunnel_view->MakeSelectable (true);
			tunnel_view->SetText (s1.str ().c_str ());
			
			BRect btnRect{10,10,90,35};//10+32,10+12);
			auto msg = new BMessage(M_CLOSETUNNEL);
			msg->AddUInt32("tunnel_val", stream->GetRecvStreamID());
			BButton * closeButton = new BButton(
			btnRect,"close leaseset", "close", msg
					);
			bview->AddChild(closeButton);
			bview->AddChild(tunnel_view);

			
			m_tab_view->AddTab(bview);
			m_tab_view->TabAt(++m_counter_streams)->SetLabel(streamDestShort.c_str());


			/*
			 * void ShowLocalDestination in daemon/HTTPServer.cpp
			 */
			s << s1.str() << "\n";
		}
		//s << "\r\n";
	}
}

void MainWindow :: GetInfoAboutTunnel(BMessage * msg)
{
	if(!msg) return;
	bool isClient = false;
	BString name{};
	msg->FindString("tunnel_name", &name);
	msg->FindBool("is_client_tunnel", &isClient);
	if (isClient)
	{
		 for(auto it :i2p::client::context.GetClientTunnels () )
		 {
			if( BString(it.second->GetName()) == name )
			{
	     		  auto & ident = it.second->GetLocalDestination()->GetIdentHash();
			  m_IdentHash = ident;
			  m_IsDrawTunnel = true;
			  return UpdateMainView(); 
					   //TODO:
			}
		 }
	}else {
		for(auto it: i2p::client::context.GetServerTunnels())
		{
			if( BString(it.second->GetName()) == name)
			{
	     		  auto & ident = it.second->GetLocalDestination()->GetIdentHash();
			  m_IdentHash = ident;
			  m_IsDrawTunnel = true;
			  return UpdateMainView();
						//TODO:
			}
		}	
	}// if not client tunnel
}

void MainWindow :: OpenHTTPInterface()
{
	bool enabled; i2p::config::GetOption("http.enabled", enabled);
	uint16_t port; i2p::config::GetOption("http.port", port);
	std::string address; i2p::config::GetOption("http.address", address);
	if(!enabled)
	{
		return m_MainView->SetText("Not enabled http server");
	}
	std::ostringstream com;
	com << "/bin/open 'http://" << address << ":" << port<<"'";
	//char * argv[] = {(char*)url.str().c_str() , NULL, NULL};
	//BRoster{}.Launch( "application/x-vnd.Haiku-WebPositive", 2, argv);
	std::string com_s{com.str()};
	std::thread ([com_s]() {
		system(com_s.c_str());
	}).detach();	
}

void MainWindow::MessageReceived (BMessage * msg)
{
	if (!msg) return;
	uint32_t tunnel_val;// streamID (to method)
	switch (msg->what)
	{
		// to method
                   case M_CLOSETUNNEL:
                       //std::cout << "Close Tunnel (DEBUG NOT WORKS)" << std::endl;
		       msg->FindUInt32("tunnel_val", &tunnel_val);
		       if(tunnel_val)
		       {
				auto dest = 
				i2p::client::context.FindLocalDestination
				(m_IdentHash);
				if(dest)
				{
					dest->DeleteStream(tunnel_val);
				}
		       }

                  break;
		case M_SHOWMAIN:
			m_IsDrawTunnel = false;
			m_IdentHash = {};
			UpdateMainView();
		break;
		case C_MAIN_VIEW_UPDATE:
			UpdateMainView ();
		break;
		case C_OPENHTTP:
			OpenHTTPInterface();

		break;
		case M_SHOW_TUNNEL:
				GetInfoAboutTunnel(msg);
				break;
		case M_GRACEFUL_SHUTDOWN:
			if (!m_GracefulShutdownTimer)
			{
				i2p::context.SetAcceptsTunnels (false);
				Daemon.gracefulShutdownInterval = GRACEFUL_SHUTDOWN_UPDATE_COUNT;
				m_MainViewUpdateTimer = nullptr;
				m_GracefulShutdownTimer = std::make_unique<BMessageRunner>(m_Messenger, 
					BMessage (C_GRACEFUL_SHUTDOWN_UPDATE), GRACEFUL_SHUTDOWN_UPDATE_INTERVAL);
			}	
		break;
		case C_GRACEFUL_SHUTDOWN_UPDATE:
			if (Daemon.gracefulShutdownInterval > 0)
			{
				UpdateMainView ();
				Daemon.gracefulShutdownInterval--;
			}	
			if (!Daemon.gracefulShutdownInterval || i2p::tunnel::tunnels.CountTransitTunnels () <= 0)
			{
				m_GracefulShutdownTimer = nullptr;
				Daemon.gracefulShutdownInterval = 0;
				m_IsGracefulShutdownComplete = true;
				m_Messenger.SendMessage (B_QUIT_REQUESTED);
			}		
		break;
		case M_RUN_PEER_TEST:
			i2p::transport::transports.PeerTest ();
		break;	
		default:
			BWindow::MessageReceived (msg);
	}	
}	

bool MainWindow::QuitRequested ()
{
	bool isQuit = true;
	if (!m_IsGracefulShutdownComplete)
	{
		auto alert = new BAlert (nullptr, "This will stop i2pd. Are you sure?", "Cancel", "Yes", "No",
			B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
		alert->SetShortcut (0, B_ESCAPE);
		isQuit = alert->Go () == 1; // Yes
	}
	if (isQuit)
	{
		m_MainViewUpdateTimer = nullptr;
		m_GracefulShutdownTimer = nullptr;
	}
	return isQuit;
}	

I2PApp::I2PApp (): BApplication("application/x-vnd.purplei2p-i2pd")
{
}	

void I2PApp::CreateMainWindow ()
{
	auto mainWindow = new MainWindow ();
	mainWindow->Show ();
}	

namespace i2p
{
namespace util
{
	bool DaemonHaiku::start ()
	{	
		I2PApp * app = nullptr;
		if (!isDaemon)
		{
			app = new I2PApp(); // set be_app
			i2p::log::SetThrowFunction ([](const std::string& s)
				{
					auto alert = new BAlert (nullptr, s.c_str (), "Quit", nullptr, nullptr,
						B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_STOP_ALERT);
					alert->Go ();
				});
		}	
		bool ret = false;
		if (app)
		{	
			ret = Daemon_Singleton::start ();
			if (ret)
				app->CreateMainWindow ();
		}
		else
			ret = DaemonUnix::start ();
		return ret;
	}	
	
	void DaemonHaiku::run ()
	{
		if (be_app)
		{
			be_app->Run ();
			delete be_app;
		}		
		else
			DaemonUnix::run ();
	}	
}	
}	

#endif



