#include "lgi/common/Lgi.h"
#include "lgi/common/Growl.h"
#include "lgi/common/Net.h"
#include "lgi/common/Thread.h"

class LGrowlPriv : public LThread, public LMutex
{
	bool Loop;
	bool Async;
	LString AppName;
	LAutoPtr<LGrowl::LRegister> Reg;
	LArray<LGrowl::LNotify*> Notes;
	
public:
	LGrowlPriv(bool async) : LThread("LGrowl"), LMutex("GGrowlSem")
	{
	    Async = async;
		Loop = true;
		
		if (Async)
		    Run();
	}
	
	~LGrowlPriv()
	{
		Loop = false;
		
		while (Async && !IsExited())
		{
			LSleep(10);
		}
		
		Notes.DeleteObjects();
	}
	
	bool Register(LAutoPtr<LGrowl::LRegister> p)
	{
		if (!Lock(_FL))
			return false;

		bool Status = true;
		if (Async)
		{
		    Reg = p;
		}
		else
		{
		    LAutoPtr<LGrowl::LNotify> n;
		    Status = Process(p, n);
		}
		
		Unlock();
		return Status;
	}

	bool Notify(LAutoPtr<LGrowl::LNotify> n)
	{
		if (!Lock(_FL))
			return false;

	    bool Status = true;
		if (Async)
		{
		    Notes.Add(n.Release());
		}
		else
		{
		    LAutoPtr<LGrowl::LRegister> r;
		    Status = Process(r, n);
		}
		
		Unlock();
		return Status;
	}
	
	bool Process(LAutoPtr<LGrowl::LRegister> &reg,
			     LAutoPtr<LGrowl::LNotify> &note)
	{
		LAutoPtr<LSocket> Sock(new LSocket);
		Sock->SetTimeout(5000);
		if (!Sock->Open("localhost", 23053))
		    return false;
		    
		LStringPipe p(256);
		p.Print("GNTP/1.0 %s NONE\r\n", reg ? "REGISTER" : "NOTIFY");
		if (reg)
		{
			AppName = reg->App;
			p.Print("Application-Name: %s\r\n", reg->App.Get());
			if (reg->IconUrl)
				p.Print("Application-Icon: %s\r\n", reg->IconUrl.Get());
			p.Print("Notifications-Count: %i\r\n", reg->Types.Length());
			
			for (int i=0; i<reg->Types.Length(); i++)
			{
				auto &t = reg->Types[i];
				p.Print("\r\n"
						"Notification-Name: %s\r\n"
						"Notification-Enabled: %s\r\n",
						t.Name.Get(),
						t.Enabled ? "True" : "False");
				
				if (t.DisplayName)
					p.Print("Notification-Display-Name: %s\r\n", t.DisplayName.Get());
				if (t.IconUrl)
					p.Print("Notification-Icon: %s\r\n", t.IconUrl.Get());
			}
		}
		else if (AppName)
		{
			p.Print("Application-Name: %s\r\n"
					"Notification-Name: %s\r\n"
					"Notification-Title: %s\r\n"
					"Notification-Sticky: %s\r\n"
					"Notification-Priority: %i\r\n",
					AppName.Get(),
					note->Name.Get(),
					note->Title.Get(),
					note->Sticky ? "True" : "False",
					note->Priority);

			if (note->Id)
				p.Print("Notification-ID: %s\r\n", note->Id.Get());
			if (note->Text)
				p.Print("Notification-Text: %s\r\n", note->Text.Get());
			if (note->IconUrl)
				p.Print("Notification-Icon: %s\r\n", note->IconUrl.Get());
			if (note->CoalescingID)
				p.Print("Notification-Coalescing-ID: %s\r\n", note->CoalescingID.Get());
			if (note->CallbackData)
				p.Print("Notification-Callback-Context: %s\r\n"
						"Notification-Callback-Context-Type: %s\r\n",
						note->CallbackData.Get(),
						note->CallbackDataType.Get());
			if (note->CallbackTarget)
				p.Print("Notification-Callback-Target: %s\r\n", note->CallbackTarget.Get());
		}
		else LAssert(0);
		
		p.Print("\r\n");
		
		LAutoString Cmd(p.NewStr());
		int CmdLen = (int) strlen(Cmd);
		ssize_t w = Sock->Write(Cmd, CmdLen);
		if (w == CmdLen)
		{
			char In[256];
			while (Sock->IsOpen())
			{
				ssize_t r = Sock->Read(In, sizeof(In));
				if (r <= 0)
					break;
				p.Write(In, r);
			}
			LAutoString Resp(p.NewStr());
		}
		
		return true;
	}
	
	int Main()
	{
		// char Str[256];
		
		while (Loop)
		{
			LAutoPtr<LGrowl::LRegister> reg;
			LAutoPtr<LGrowl::LNotify> note;

			if (LockWithTimeout(100, _FL))
			{
				if (Reg)
					reg = Reg;
				else if (Notes.Length())
				{
					note.Reset(Notes[0]);
					Notes.DeleteAt(0, true);
				}
				Unlock();
			}

			if (reg || note)
			{
			    Process(reg, note);
			}
			else
			{
				LSleep(20);
			}
		}
		
		return 0;
	}
};
	
LGrowl::LGrowl(bool async)
{
	d = new LGrowlPriv(async);
}

LGrowl::~LGrowl()
{
	DeleteObj(d);
}

bool LGrowl::Register(LAutoPtr<LRegister> r)
{
	return d->Register(r);
}

bool LGrowl::Notify(LAutoPtr<LNotify> n)
{
	return d->Notify(n);
}


