; ============================================================================
; MainWindow Module - UI and Event Handling
; ============================================================================

Module MainWindow
	EnableExplicit
	
	; ========================================================================
	; UI Structures
	; ========================================================================
	
	Structure ArticleLanguageGadgets
		Name.i
		Image.i
		Blurb.i
		Content.i
	EndStructure
	
	Structure TagLanguageGadgets
		Name.i
	EndStructure
	
	Structure UIState
		Window.i
		CurrentSelection.i
		CurrentArticleID.i
		CurrentTagID.i
		UploadBase64.s
	EndStructure
	
	Structure MenuBarGadgets
		Container.i
		Font.i
		Articles.i
		Files.i
		Tags.i
		Separator0.i
		Separator1.i
	EndStructure
	
	Structure ArticleListGadgets
		Container.i
		ListIcon.i
		ButtonNew.i
		ButtonEdit.i
		ButtonDelete.i
	EndStructure
	
	Structure ArticleEditorGadgets
		Container.i
		StringTitle.i
		CheckboxDraft.i
		Date.i
		Panel.i
		ButtonSave.i
		ButtonPreview.i
		ListviewTags.i
		List LanguageTabs.ArticleLanguageGadgets()
	EndStructure
	
	Structure FilesGadgets
		Container.i
		ListIcon.i
		ButtonNew.i
		ButtonDelete.i
	EndStructure
	
	Structure TagsListGadgets
		Container.i
		ListIcon.i
		ButtonNew.i
		ButtonEdit.i
	EndStructure
	
	Structure TagEditorGadgets
		Container.i
		StringName.i
		StringColor.i
		Panel.i
		ButtonSave.i
		List LanguageTabs.TagLanguageGadgets()
	EndStructure
	
	; -- Constants --
	#MenuBar_Height = 60
	#MenuBar_Height_Mobile = 80
	#MobileBreakpoint = 750
	#MaxContentWidth = 950
	
	Enumeration Selection
		#Sel_ArticleList
		#Sel_Article
		#Sel_Files
		#Sel_TagList
		#Sel_Tag
	EndEnumeration
	
	; -- Module Variables --
	Global State.UIState
	Global MenuBar.MenuBarGadgets
	Global ArticleList.ArticleListGadgets
	Global ArticleEditor.ArticleEditorGadgets
	Global FilesView.FilesGadgets
	Global TagsList.TagsListGadgets
	Global TagEditor.TagEditorGadgets
	
	; -- Forward Declarations --
	Declare CreateMenuBar()
	Declare CreateArticleListView()
	Declare CreateArticleEditorView()
	Declare CreateFilesView()
	Declare CreateTagsListView()
	Declare CreateTagEditorView()
	Declare.s SanitizeURL(InputString.s)
	Declare.s StringToBase64(String.s)
	Declare ChangeSelection()
	Declare SetMenuHighlight(ActiveGadget)
	Declare OpenURLInNewTab(URL.s)
	Declare Handler_Resize()
	Declare Handler_Articles()
	Declare Handler_Files()
	Declare Handler_Tags()
	Declare Handler_NewArticle()
	Declare Handler_EditArticle()
	Declare Handler_DeleteArticle()
	Declare Handler_UpdateArticle()
	Declare Handler_PreviewArticle()
	Declare Handler_NewFile()
	Declare Handler_DeleteFile()
	Declare Handler_FileListIcon()
	Declare Handler_NewTag()
	Declare Handler_EditTag()
	Declare Handler_UpdateTag()
	Declare UpdateArticle_Callback(Success, Result.s, UserData)
	Declare EditArticle_Callback(Success, Result.s, UserData)
	Declare NewArticle_Callback(Success, Result.s, UserData)
	Declare DeleteArticle_Callback(Success, Result.s, UserData)
	Declare NewFile_Callback()
	Declare OpenFile_Callback(Status, Filename$, File, SizeRead)
	Declare UploadFile_Callback(Success, Result.s, UserData)
	Declare DeleteFile_Callback(Success, Result.s, UserData)
	Declare PreviewArticle_Callback(Success, Result.s, UserData)
	Declare NewTag_Callback(Success, Result.s, UserData)
	Declare EditTag_Callback(Success, Result.s, UserData)
	Declare UpdateTag_Callback(Success, Result.s, UserData)
	
	; ========================================================================
	; Public Entry Point
	; ========================================================================
	
	Procedure Open()
		State\Window = OpenWindow(#PB_Any, 0, 0, 10, 10, General::#AppName, #PB_Window_Background)
		State\CurrentSelection = #Sel_ArticleList
		
		CreateMenuBar()
		CreateArticleListView()
		CreateArticleEditorView()
		CreateFilesView()
		CreateTagsListView()
		CreateTagEditorView()
		
		BindEvent(#PB_Event_SizeWindow, @Handler_Resize())
		Handler_Resize()
	EndProcedure
	
	; ========================================================================
	; UI Creation
	; ========================================================================
	
	Procedure CreateMenuBar()
		Protected TitleGadget
		
		MenuBar\Font = LoadFont(#PB_Any, "Tahoma", 20)
		MenuBar\Container = ContainerGadget(#PB_Any, 0, 0, 10, #MenuBar_Height)
		SetGadgetColor(MenuBar\Container, #PB_Gadget_BackColor, $2A2822)
		
		TitleGadget = TextGadget(#PB_Any, 10, 0, 170, 60, General::#AppName, #PB_Text_Center | #PB_Text_VerticalCenter)
		SetGadgetFont(TitleGadget, FontID(MenuBar\Font))
		SetGadgetColor(TitleGadget, #PB_Gadget_FrontColor, $FFFFFF)
		
		MenuBar\Articles = HyperLinkGadget(#PB_Any, 0, 0, 50, 20, "Articles", $8A8578)
		SetGadgetColor(MenuBar\Articles, #PB_Gadget_FrontColor, $999999)
		BindGadgetEvent(MenuBar\Articles, @Handler_Articles())
		
		MenuBar\Separator0 = TextGadget(#PB_Any, 0, 0, 10, 20, Chr(9679), #PB_Text_VerticalCenter)
		SetGadgetColor(MenuBar\Separator0, #PB_Gadget_FrontColor, $FFFFFF)
		
		MenuBar\Files = HyperLinkGadget(#PB_Any, 0, 0, 60, 20, "Files", $8A8578)
		SetGadgetColor(MenuBar\Files, #PB_Gadget_FrontColor, $FFFFFF)
		BindGadgetEvent(MenuBar\Files, @Handler_Files())
		
		MenuBar\Separator1 = TextGadget(#PB_Any, 0, 0, 10, 20, Chr(9679), #PB_Text_VerticalCenter)
		SetGadgetColor(MenuBar\Separator1, #PB_Gadget_FrontColor, $FFFFFF)
		
		MenuBar\Tags = HyperLinkGadget(#PB_Any, 0, 0, 60, 20, "Tags", $8A8578)
		SetGadgetColor(MenuBar\Tags, #PB_Gadget_FrontColor, $FFFFFF)
		BindGadgetEvent(MenuBar\Tags, @Handler_Tags())
		
		CloseGadgetList()
	EndProcedure
	
	Procedure CreateArticleListView()
		ArticleList\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
		
		ArticleList\ListIcon = ListIconGadget(#PB_Any, 0, 0, 10, 10, "Title", 200)
		BindGadgetEvent(ArticleList\ListIcon, @Handler_EditArticle(), #PB_EventType_LeftDoubleClick)
		AddGadgetColumn(ArticleList\ListIcon, 1, "Status", 50)
		AddGadgetColumn(ArticleList\ListIcon, 2, "Date", 100)
		
		ForEach DataModel::Articles()
			AddGadgetItem(ArticleList\ListIcon, 0, DataModel::FormatArticleListItem(DataModel::@Articles()))
			SetGadgetItemData(ArticleList\ListIcon, 0, DataModel::Articles()\ID)
		Next
		
		ArticleList\ButtonNew = ButtonGadget(#PB_Any, 10, 10, 120, 20, "New Article")
		ArticleList\ButtonEdit = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Edit")
		ArticleList\ButtonDelete = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Delete")
		BindGadgetEvent(ArticleList\ButtonNew, @Handler_NewArticle())
		BindGadgetEvent(ArticleList\ButtonEdit, @Handler_EditArticle())
		BindGadgetEvent(ArticleList\ButtonDelete, @Handler_DeleteArticle())
		
		CloseGadgetList()
	EndProcedure
	
	Procedure CreateArticleEditorView()
		ArticleEditor\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
		
		TextGadget(#PB_Any, 20, 20, 50, 20, "Title:", #PB_Text_VerticalCenter)
		ArticleEditor\StringTitle = StringGadget(#PB_Any, 80, 20, 510, 20, "")
		
		TextGadget(#PB_Any, 20, 60, 50, 20, "Date:", #PB_Text_VerticalCenter)
		ArticleEditor\Date = DateGadget(#PB_Any, 80, 60, 200, 20)
		
		ArticleEditor\CheckboxDraft = CheckBoxGadget(#PB_Any, 570, 60, 60, 20, "Draft", #PB_CheckBox_Center)
		
		ArticleEditor\ListviewTags = ListViewGadget(#PB_Any, 20, 100, 100, 100, #PB_ListView_ClickSelect)
		ForEach DataModel::Tags()
			AddGadgetItem(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\DefaultName)
			SetGadgetItemData(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\BinaryID)
		Next
		
		ArticleEditor\Panel = PanelGadget(#PB_Any, 0, 0, 10, 10)
		ForEach DataModel::Language()
			AddElement(ArticleEditor\LanguageTabs())
			AddGadgetItem(ArticleEditor\Panel, -1, DataModel::Language())
			
			TextGadget(#PB_Any, 20, 20, 90, 20, "Localized title:", #PB_Text_VerticalCenter)
			ArticleEditor\LanguageTabs()\Name = StringGadget(#PB_Any, 130, 20, 80, 20, "")
			
			TextGadget(#PB_Any, 20, 60, 60, 20, "Image:", #PB_Text_VerticalCenter)
			ArticleEditor\LanguageTabs()\Image = StringGadget(#PB_Any, 130, 60, 80, 20, "")
			
			TextGadget(#PB_Any, 20, 100, 50, 20, "Blurb:", #PB_Text_VerticalCenter)
			ArticleEditor\LanguageTabs()\Blurb = EditorGadget(#PB_Any, 20, 120, 80, 50, #PB_Editor_WordWrap)
			
			TextGadget(#PB_Any, 20, 180, 50, 20, "Content:", #PB_Text_VerticalCenter)
			ArticleEditor\LanguageTabs()\Content = EditorGadget(#PB_Any, 20, 200, 80, 20)
		Next
		CloseGadgetList()
		
		ArticleEditor\ButtonPreview = ButtonGadget(#PB_Any, 0, 0, 180, 25, "Preview")
		ArticleEditor\ButtonSave = ButtonGadget(#PB_Any, 0, 0, 180, 25, "Save")
		BindGadgetEvent(ArticleEditor\ButtonSave, @Handler_UpdateArticle())
		BindGadgetEvent(ArticleEditor\ButtonPreview, @Handler_PreviewArticle())
		
		CloseGadgetList()
		HideGadget(ArticleEditor\Container, #True)
	EndProcedure
	
	Procedure CreateFilesView()
		FilesView\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
		
		FilesView\ListIcon = ListIconGadget(#PB_Any, 0, 0, 10, 10, "Name", 200)
		AddGadgetColumn(FilesView\ListIcon, 1, "Type", 50)
		
		ForEach DataModel::Files()
			AddGadgetItem(FilesView\ListIcon, 0, DataModel::Files()\Name + Chr(10) + DataModel::Files()\Type)
			SetGadgetItemData(FilesView\ListIcon, 0, DataModel::Files()\ID)
		Next
		
		FilesView\ButtonNew = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Upload File")
		FilesView\ButtonDelete = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Delete File")
		BindGadgetEvent(FilesView\ButtonNew, @Handler_NewFile())
		BindGadgetEvent(FilesView\ButtonDelete, @Handler_DeleteFile())
		BindGadgetEvent(FilesView\ListIcon, @Handler_FileListIcon())
		
		CloseGadgetList()
		HideGadget(FilesView\Container, #True)
	EndProcedure
	
	Procedure CreateTagsListView()
		TagsList\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
		
		TagsList\ListIcon = ListIconGadget(#PB_Any, 0, 0, 10, 10, "Name", 200)
		BindGadgetEvent(TagsList\ListIcon, @Handler_EditTag(), #PB_EventType_LeftDoubleClick)
		AddGadgetColumn(TagsList\ListIcon, 1, "Color", 100)
		
		ForEach DataModel::Tags()
			AddGadgetItem(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::FormatTagListItem(DataModel::@Tags()))
			SetGadgetItemData(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::Tags()\ID)
		Next
		
		TagsList\ButtonNew = ButtonGadget(#PB_Any, 10, 10, 120, 20, "New Tag")
		TagsList\ButtonEdit = ButtonGadget(#PB_Any, 10, 10, 120, 20, "Edit Tag")
		BindGadgetEvent(TagsList\ButtonNew, @Handler_NewTag())
		BindGadgetEvent(TagsList\ButtonEdit, @Handler_EditTag())
		
		CloseGadgetList()
		HideGadget(TagsList\Container, #True)
	EndProcedure
	
	Procedure CreateTagEditorView()
		TagEditor\Container = ContainerGadget(#PB_Any, 0, 0, 10, 10)
		
		TextGadget(#PB_Any, 20, 20, 80, 20, "Default Name:", #PB_Text_VerticalCenter)
		TagEditor\StringName = StringGadget(#PB_Any, 110, 20, 200, 20, "")
		
		TextGadget(#PB_Any, 20, 60, 50, 20, "Color:", #PB_Text_VerticalCenter)
		TagEditor\StringColor = StringGadget(#PB_Any, 110, 60, 100, 20, "")
		
		TagEditor\Panel = PanelGadget(#PB_Any, 0, 0, 10, 10)
		ForEach DataModel::Language()
			AddElement(TagEditor\LanguageTabs())
			AddGadgetItem(TagEditor\Panel, -1, DataModel::Language())
			
			TextGadget(#PB_Any, 20, 20, 100, 20, "Localized name:", #PB_Text_VerticalCenter)
			TagEditor\LanguageTabs()\Name = StringGadget(#PB_Any, 130, 20, 200, 20, "")
		Next
		CloseGadgetList()
		
		TagEditor\ButtonSave = ButtonGadget(#PB_Any, 0, 0, 180, 25, "Save")
		BindGadgetEvent(TagEditor\ButtonSave, @Handler_UpdateTag())
		
		CloseGadgetList()
		HideGadget(TagEditor\Container, #True)
	EndProcedure
	
	; ========================================================================
	; Utilities
	; ========================================================================
	
	Procedure.s SanitizeURL(InputString.s)
		Protected Result.s, Char.s, i, CharCode, Len = Len(InputString)
		
		For i = 1 To Len
			Char = Mid(InputString, i, 1)
			CharCode = Asc(Char)
			If (CharCode >= 65 And CharCode <= 90) Or (CharCode >= 97 And CharCode <= 122) Or (CharCode >= 48 And CharCode <= 57) Or Char = "-" Or Char = "." Or Char = "_" Or Char = "/"
				Result + Char
			EndIf
		Next
		ProcedureReturn Result
	EndProcedure
	
	Procedure.s StringToBase64(String.s)
		Protected Size, *Memory, Result.s
		Size = StringByteLength(String)
		If Size > 0
			*Memory = AllocateMemory(Size, #PB_Memory_NoClear)
			PokeS(*Memory, 0, String)
			Result = Base64Encoder(*Memory, 0, Size)
			FreeMemory(*Memory)
		EndIf
		ProcedureReturn Result
	EndProcedure
	
	Procedure ChangeSelection()
		Select State\CurrentSelection
			Case #Sel_ArticleList
				HideGadget(ArticleList\Container, #True)
				SetGadgetColor(MenuBar\Articles, #PB_Gadget_FrontColor, $FFFFFF)
			Case #Sel_Article
				HideGadget(ArticleEditor\Container, #True)
			Case #Sel_Files
				HideGadget(FilesView\Container, #True)
				SetGadgetColor(MenuBar\Files, #PB_Gadget_FrontColor, $FFFFFF)
			Case #Sel_TagList
				HideGadget(TagsList\Container, #True)
				SetGadgetColor(MenuBar\Tags, #PB_Gadget_FrontColor, $FFFFFF)
			Case #Sel_Tag
				HideGadget(TagEditor\Container, #True)
		EndSelect
	EndProcedure
	
	Procedure SetMenuHighlight(ActiveGadget)
		SetGadgetColor(MenuBar\Articles, #PB_Gadget_FrontColor, $FFFFFF)
		SetGadgetColor(MenuBar\Files, #PB_Gadget_FrontColor, $FFFFFF)
		SetGadgetColor(MenuBar\Tags, #PB_Gadget_FrontColor, $FFFFFF)
		If ActiveGadget
			SetGadgetColor(ActiveGadget, #PB_Gadget_FrontColor, $999999)
		EndIf
	EndProcedure
	
	Procedure OpenURLInNewTab(URL.s)
		!window.open(v_url, '_blank').focus();
	EndProcedure
	
	; ========================================================================
	; Resize Handler
	; ========================================================================
	
	Procedure Handler_Resize()
		Protected WinWidth = WindowWidth(State\Window)
		Protected WinHeight = WindowHeight(State\Window)
		Protected ContentWidth = WinWidth
		Protected ContentHeight, ContentX = 0, ContentY = #MenuBar_Height_Mobile
		Protected MenuX, PanelWidth, PanelHeight
		
		If WinWidth < #MobileBreakpoint
			ResizeGadget(MenuBar\Container, 0, 0, WinWidth, #MenuBar_Height_Mobile)
			ResizeGadget(MenuBar\Articles, 15, 55, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Separator0, 66, 55, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Files, 85, 55, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Separator1, 123, 55, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Tags, 140, 55, #PB_Ignore, #PB_Ignore)
			ContentHeight = WinHeight - #MenuBar_Height_Mobile
		Else
			ResizeGadget(MenuBar\Container, 0, 0, WinWidth, #MenuBar_Height)
			MenuX = WinWidth - 185
			ResizeGadget(MenuBar\Articles, MenuX + 15, 22, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Separator0, MenuX + 66, 22, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Files, MenuX + 85, 22, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Separator1, MenuX + 123, 22, #PB_Ignore, #PB_Ignore)
			ResizeGadget(MenuBar\Tags, MenuX + 140, 22, #PB_Ignore, #PB_Ignore)
			ContentHeight = WinHeight - (2 * #MenuBar_Height_Mobile)
			ContentWidth = WinWidth * 0.8
			If ContentWidth > #MaxContentWidth : ContentWidth = #MaxContentWidth : EndIf
			ContentY + 30
			ContentX = (WinWidth - ContentWidth) * 0.5
		EndIf
		
		Select State\CurrentSelection
			Case #Sel_ArticleList
				ResizeGadget(ArticleList\Container, ContentX, ContentY, ContentWidth, ContentHeight)
				ResizeGadget(ArticleList\ListIcon, 0, 0, ContentWidth, ContentHeight - 40)
				ResizeGadget(ArticleList\ButtonNew, ContentWidth - 130, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				ResizeGadget(ArticleList\ButtonEdit, ContentWidth - 260, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				ResizeGadget(ArticleList\ButtonDelete, ContentWidth - 390, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				
			Case #Sel_Article
				ResizeGadget(ArticleEditor\Container, ContentX, ContentY, ContentWidth, ContentHeight)
				ResizeGadget(ArticleEditor\StringTitle, #PB_Ignore, #PB_Ignore, ContentWidth - 100, #PB_Ignore)
				ResizeGadget(ArticleEditor\Date, #PB_Ignore, #PB_Ignore, ContentWidth - 170, #PB_Ignore)
				ResizeGadget(ArticleEditor\CheckboxDraft, ContentWidth - 80, 60, 60, #PB_Ignore)
				ResizeGadget(ArticleEditor\ListviewTags, #PB_Ignore, #PB_Ignore, ContentWidth - 40, 100)
				If ContentWidth > 550
					ResizeGadget(ArticleEditor\Panel, 20, 210, ContentWidth - 40, ContentHeight - 265)
				Else
					ResizeGadget(ArticleEditor\Panel, 0, 210, ContentWidth, ContentHeight - 265)
				EndIf
				ResizeGadget(ArticleEditor\ButtonPreview, 20, ContentHeight - 40, (ContentWidth - 60) * 0.5, 25)
				ResizeGadget(ArticleEditor\ButtonSave, 40 + (ContentWidth - 60) * 0.5, ContentHeight - 40, (ContentWidth - 60) * 0.5, 25)
				PanelWidth = GadgetWidth(ArticleEditor\Panel)
				PanelHeight = GadgetHeight(ArticleEditor\Panel)
				ForEach ArticleEditor\LanguageTabs()
					ResizeGadget(ArticleEditor\LanguageTabs()\Name, #PB_Ignore, #PB_Ignore, PanelWidth - 150, #PB_Ignore)
					ResizeGadget(ArticleEditor\LanguageTabs()\Image, #PB_Ignore, #PB_Ignore, PanelWidth - 150, #PB_Ignore)
					ResizeGadget(ArticleEditor\LanguageTabs()\Blurb, #PB_Ignore, #PB_Ignore, PanelWidth - 40, #PB_Ignore)
					ResizeGadget(ArticleEditor\LanguageTabs()\Content, #PB_Ignore, #PB_Ignore, PanelWidth - 40, PanelHeight - 250)
				Next
				
			Case #Sel_Files
				ResizeGadget(FilesView\Container, ContentX, ContentY, ContentWidth, ContentHeight)
				ResizeGadget(FilesView\ListIcon, 0, 0, ContentWidth, ContentHeight - 40)
				ResizeGadget(FilesView\ButtonNew, ContentWidth - 130, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				ResizeGadget(FilesView\ButtonDelete, ContentWidth - 260, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				
			Case #Sel_TagList
				ResizeGadget(TagsList\Container, ContentX, ContentY, ContentWidth, ContentHeight)
				ResizeGadget(TagsList\ListIcon, 0, 0, ContentWidth, ContentHeight - 40)
				ResizeGadget(TagsList\ButtonNew, ContentWidth - 130, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				ResizeGadget(TagsList\ButtonEdit, ContentWidth - 260, ContentHeight - 30, #PB_Ignore, #PB_Ignore)
				
			Case #Sel_Tag
				ResizeGadget(TagEditor\Container, ContentX, ContentY, ContentWidth, ContentHeight)
				ResizeGadget(TagEditor\StringName, #PB_Ignore, #PB_Ignore, ContentWidth - 130, #PB_Ignore)
				ResizeGadget(TagEditor\StringColor, #PB_Ignore, #PB_Ignore, ContentWidth - 130, #PB_Ignore)
				If ContentWidth > 550
					ResizeGadget(TagEditor\Panel, 20, 100, ContentWidth - 40, ContentHeight - 155)
				Else
					ResizeGadget(TagEditor\Panel, 0, 100, ContentWidth, ContentHeight - 155)
				EndIf
				ResizeGadget(TagEditor\ButtonSave, 20, ContentHeight - 40, ContentWidth - 40, 25)
				PanelWidth = GadgetWidth(TagEditor\Panel)
				ForEach TagEditor\LanguageTabs()
					ResizeGadget(TagEditor\LanguageTabs()\Name, #PB_Ignore, #PB_Ignore, PanelWidth - 150, #PB_Ignore)
				Next
		EndSelect
	EndProcedure
	
	; ========================================================================
	; Navigation Handlers
	; ========================================================================
	
	Procedure Handler_Articles()
		If State\CurrentSelection <> #Sel_ArticleList
			ChangeSelection()
			State\CurrentSelection = #Sel_ArticleList
			SetMenuHighlight(MenuBar\Articles)
			HideGadget(ArticleList\Container, #False)
			Handler_Resize()
		EndIf
	EndProcedure
	
	Procedure Handler_Files()
		If State\CurrentSelection <> #Sel_Files
			ChangeSelection()
			State\CurrentSelection = #Sel_Files
			SetMenuHighlight(MenuBar\Files)
			HideGadget(FilesView\Container, #False)
			Handler_Resize()
		EndIf
	EndProcedure
	
	Procedure Handler_Tags()
		If State\CurrentSelection <> #Sel_TagList
			ChangeSelection()
			State\CurrentSelection = #Sel_TagList
			SetMenuHighlight(MenuBar\Tags)
			HideGadget(TagsList\Container, #False)
			Handler_Resize()
		EndIf
	EndProcedure
	
	; ========================================================================
	; Article Handlers
	; ========================================================================
	
	Procedure Handler_NewArticle()
		HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "newarticle", @NewArticle_Callback())
	EndProcedure
	
	Procedure Handler_EditArticle()
		Protected Item = GetGadgetState(ArticleList\ListIcon)
		If Item >= 0
			State\CurrentArticleID = GetGadgetItemData(ArticleList\ListIcon, Item)
			HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "getarticle=" + State\CurrentArticleID, @EditArticle_Callback())
		EndIf
	EndProcedure
	
	Procedure Handler_DeleteArticle()
		Protected SelectedItem = GetGadgetState(ArticleList\ListIcon)
		If SelectedItem > -1
			Protected ArticleID = GetGadgetItemData(ArticleList\ListIcon, SelectedItem)
			HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "deletearticle=" + ArticleID, @DeleteArticle_Callback())
		EndIf
	EndProcedure
	
	Procedure Handler_UpdateArticle()
		Protected TextBundle.s, tags.i, Title.s
		
		;Sanitize the title and make sure it's unique
		Title = SanitizeURL(GetGadgetText(ArticleEditor\StringTitle))
		SetGadgetText(ArticleEditor\StringTitle, Title)
		
		ForEach DataModel::Articles()
			If DataModel::Articles()\Title = Title
				If DataModel::Articles()\ID <> State\CurrentArticleID
					Debug "[Error] An article with the same name already exists."
					ProcedureReturn #False
				EndIf
			EndIf
		Next
		
		
		ForEach DataModel::Tags()
			If GetGadgetItemState(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()))
				tags = tags | DataModel::Tags()\BinaryID
			EndIf
		Next
		
		TextBundle = Str(State\CurrentArticleID) + General::#FieldSeparator
		TextBundle + Title + General::#FieldSeparator
		TextBundle + Str(GetGadgetState(ArticleEditor\CheckboxDraft)) + General::#FieldSeparator
		TextBundle + Tags + General::#FieldSeparator
		TextBundle + Str(GetGadgetState(ArticleEditor\Date)) + General::#RecordSeparator
		
		ForEach ArticleEditor\LanguageTabs()
			TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Name)) + General::#FieldSeparator
			TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Image)) + General::#FieldSeparator
			TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Blurb)) + General::#FieldSeparator
			TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Content)) + General::#RecordSeparator
		Next
		
		HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "updatearticle=" + TextBundle, @UpdateArticle_Callback(), tags)
	EndProcedure
	
	Procedure Handler_PreviewArticle()
		Protected TextBundle.s
		SelectElement(ArticleEditor\LanguageTabs(), GetGadgetState(ArticleEditor\Panel))
		TextBundle = StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Name)) + General::#FieldSeparator
		TextBundle + StringToBase64(GetGadgetText(ArticleEditor\LanguageTabs()\Content))
		HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "previewarticle=" + TextBundle, @PreviewArticle_Callback())
	EndProcedure
	
	; ========================================================================
	; File Handlers
	; ========================================================================
	
	Procedure Handler_NewFile()
		OpenFileRequester("*.*", @NewFile_Callback())
	EndProcedure
	
	Procedure Handler_DeleteFile()
		Protected SelectedItem = GetGadgetState(FilesView\ListIcon)
		If SelectedItem > -1
			Protected FileID = GetGadgetItemData(FilesView\ListIcon, SelectedItem)
			HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "deletefile=" + FileID, @DeleteFile_Callback())
		EndIf
	EndProcedure
	
	Procedure Handler_FileListIcon()
		Protected URL.s, EventType = EventType()
		Protected SelectedItem = GetGadgetState(FilesView\ListIcon)
		
		If SelectedItem > -1
			URL = General::#Address + RemoveString(GetGadgetItemText(FilesView\ListIcon, SelectedItem, 0), "/", #PB_String_CaseSensitive, 0, 1)
			If EventType = #PB_EventType_LeftDoubleClick
				OpenURLInNewTab(URL)
			ElseIf EventType = #PB_EventType_RightClick
				SetClipboardText(URL)
			EndIf
		EndIf
	EndProcedure
	
	; ========================================================================
	; Tag Handlers
	; ========================================================================
	
	Procedure Handler_NewTag()
		HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "newtag", @NewTag_Callback())
	EndProcedure
	
	Procedure Handler_EditTag()
		Protected Item = GetGadgetState(TagsList\ListIcon)
		If Item >= 0
			State\CurrentTagID = GetGadgetItemData(TagsList\ListIcon, Item)
			HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "gettag=" + State\CurrentTagID, @EditTag_Callback())
		EndIf
	EndProcedure
	
	Procedure Handler_UpdateTag()
		Protected TextBundle.s, Name.s
		
		;Sanitize the title
		Name = SanitizeURL(GetGadgetText(TagEditor\StringName))
		SetGadgetText(TagEditor\StringName, Name)
		
		ForEach DataModel::Tags()
			If DataModel::Tags()\DefaultName = Name
				If DataModel::Tags()\ID <> State\CurrentTagID
					Debug "[Error] A tag with the same name already exists."
					ProcedureReturn #False
				EndIf
			EndIf
		Next
		
		TextBundle = Str(State\CurrentTagID) + General::#FieldSeparator
		TextBundle + GetGadgetText(TagEditor\StringName) + General::#FieldSeparator
		TextBundle + GetGadgetText(TagEditor\StringColor) + General::#RecordSeparator
		
		ForEach TagEditor\LanguageTabs()
			TextBundle + StringToBase64(GetGadgetText(TagEditor\LanguageTabs()\Name)) + General::#RecordSeparator
		Next
		
		HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "updatetag=" + TextBundle, @UpdateTag_Callback())
	EndProcedure
	
	; ========================================================================
	; HTTP Callbacks
	; ========================================================================
	
	Procedure UpdateArticle_Callback(Success, Result.s, Tags.i)
		If Not Success
			MessageRequester("Error - Failed to update article.")
			ProcedureReturn
		EndIf
		
		If DataModel::FindArticleByID(State\CurrentArticleID)
			DataModel::Articles()\Date = GetGadgetState(ArticleEditor\Date)
			DataModel::Articles()\Draft = GetGadgetState(ArticleEditor\CheckboxDraft)
			DataModel::Articles()\Title = GetGadgetText(ArticleEditor\StringTitle)
			DataModel::Articles()\Tags = Tags
			
			Protected Item = GetGadgetState(ArticleList\ListIcon)
			SetGadgetItemText(ArticleList\ListIcon, Item, DataModel::FormatArticleListItem(DataModel::@Articles()))
			Handler_Articles()
		EndIf
	EndProcedure
	
	Procedure EditArticle_Callback(Success, Result.s, UserData)
		Protected Entry.s
		
		If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_ArticleContent)
			MessageRequester("Error - Failed to load article.")
			ProcedureReturn
		EndIf
		
		Result = DataModel::StripPrefix(Result, General::#Prefix_ArticleContent)
		
		ResetList(ArticleEditor\LanguageTabs())
		While NextElement(ArticleEditor\LanguageTabs())
			Entry = DataModel::GetRecord(Result, 1 + ListIndex(ArticleEditor\LanguageTabs()))
			SetGadgetText(ArticleEditor\LanguageTabs()\Name, DataModel::GetField(Entry, 1))
			SetGadgetText(ArticleEditor\LanguageTabs()\Image, DataModel::GetField(Entry, 2))
			SetGadgetText(ArticleEditor\LanguageTabs()\Blurb, DataModel::GetField(Entry, 3))
			SetGadgetText(ArticleEditor\LanguageTabs()\Content, DataModel::GetField(Entry, 4))
		Wend
		
		If DataModel::FindArticleByID(State\CurrentArticleID)
			SetGadgetText(ArticleEditor\StringTitle, DataModel::Articles()\Title)
			SetGadgetState(ArticleEditor\CheckboxDraft, DataModel::Articles()\Draft)
			SetGadgetState(ArticleEditor\Date, DataModel::Articles()\Date)
			ForEach DataModel::Tags()
				SetGadgetItemState(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), Bool(DataModel::Tags()\BinaryID & DataModel::Articles()\Tags))
			Next
		EndIf
		
		ChangeSelection()
		State\CurrentSelection = #Sel_Article
		HideGadget(ArticleEditor\Container, #False)
		Handler_Resize()
	EndProcedure
	
	Procedure NewArticle_Callback(Success, Result.s, UserData)
		If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_NewArticle)
			MessageRequester("Error - Failed to create new article.")
			ProcedureReturn
		EndIf
		
		Result = DataModel::StripPrefix(Result, General::#Prefix_NewArticle)
		
		LastElement(DataModel::Articles())
		AddElement(DataModel::Articles())
		DataModel::Articles()\ID = Val(DataModel::GetField(Result, 1))
		DataModel::Articles()\Title = DataModel::GetField(Result, 2)
		DataModel::Articles()\Draft = Val(DataModel::GetField(Result, 3))
		DataModel::Articles()\Tags = Val(DataModel::GetField(Result, 4))
		DataModel::Articles()\Date = Val(DataModel::GetField(Result, 5))
		
		AddGadgetItem(ArticleList\ListIcon, 0, DataModel::FormatArticleListItem(DataModel::@Articles()))
		SetGadgetItemData(ArticleList\ListIcon, 0, DataModel::Articles()\ID)
		
		State\CurrentArticleID = DataModel::Articles()\ID
		HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "getarticle=" + State\CurrentArticleID, @EditArticle_Callback())
	EndProcedure
	
	Procedure DeleteArticle_Callback(Success, Result.s, UserData)
		If Not Success
			MessageRequester("Error - Failed to delete article.")
			ProcedureReturn
		EndIf
		
		Protected SelectedItem = GetGadgetState(ArticleList\ListIcon)
		Protected ArticleID = GetGadgetItemData(ArticleList\ListIcon, SelectedItem)
		
		; Remove from list
		RemoveGadgetItem(ArticleList\ListIcon, SelectedItem)
		
		; Remove from local data
		If DataModel::FindArticleByID(ArticleID)
			DeleteElement(DataModel::Articles())
		EndIf
	EndProcedure
	
	Procedure NewFile_Callback()
		If NextSelectedFile()
			OpenFile(#PB_Any, SelectedFileName(), @OpenFile_Callback(), #PB_LocalFile)
		EndIf
	EndProcedure
	
	Procedure OpenFile_Callback(Status, Filename$, File, SizeRead)
		Protected *Buffer
		
		If Status <> #PB_Status_Loaded
			ProcedureReturn
		EndIf
		
		*Buffer = AllocateMemory(SizeRead)
		ReadData(File, *Buffer, 0, SizeRead)
		State\UploadBase64 = Base64Encoder(*Buffer, 0, SizeRead)
		FreeMemory(*Buffer)
		CloseFile(File)
		
		Filename$ = ReplaceString(Filename$, "\", "/")
		Filename$ = StringField(Filename$, CountString(Filename$, "/") + 1, "/")
		Filename$ = InputRequester("FilePath", General::#Address + Filename$)
		Filename$ = RemoveString(Filename$, General::#Address, #PB_String_CaseSensitive, 0, 1)
		Filename$ = SanitizeURL(Filename$)
		
		If Filename$ = ""
			ProcedureReturn
		EndIf
		
		ForEach DataModel::Files()
			If DataModel::Files()\Name = Filename$
				MessageRequester("Error - A file with this name already exists.")
				ProcedureReturn
			EndIf
		Next
		
		If Left(Filename$, 1) <> "/"
			Filename$ = "/" + Filename$
		EndIf
		
		HTTPRequest(#PB_HTTP_Post, General::#Address + "admin/", "newfile=" + Filename$ + "&filedata=" + State\UploadBase64, @UploadFile_Callback())
	EndProcedure
	
	Procedure UploadFile_Callback(Success, Result.s, UserData)
		If Not Success
			MessageRequester("Error - Failed to upload file.")
			ProcedureReturn
		EndIf
		
		Result = DataModel::StripPrefix(Result, General::#Prefix_NewFile)
		
		LastElement(DataModel::Files())
		AddElement(DataModel::Files())
		DataModel::Files()\ID = Val(DataModel::GetField(Result, 1))
		DataModel::Files()\Name = DataModel::GetField(Result, 2)
		DataModel::Files()\Type = DataModel::MIMETypes(GetExtensionPart(DataModel::Files()\Name))
		
		AddGadgetItem(FilesView\ListIcon, 0, DataModel::Files()\Name + Chr(10) + DataModel::Files()\Type)
		SetGadgetItemData(FilesView\ListIcon, 0, DataModel::Files()\ID)
	EndProcedure
	
	Procedure DeleteFile_Callback(Success, Result.s, UserData)
		If Not Success
			MessageRequester("Error - Failed to delete file.")
			ProcedureReturn
		EndIf
		
		Protected SelectedItem = GetGadgetState(FilesView\ListIcon)
		Protected FileID = GetGadgetItemData(FilesView\ListIcon, SelectedItem)
		
		RemoveGadgetItem(FilesView\ListIcon, SelectedItem)
		If DataModel::FindFileByID(FileID)
			DeleteElement(DataModel::Files())
		EndIf
	EndProcedure
	
	Procedure PreviewArticle_Callback(Success, Result.s, UserData)
		If Not Success
			MessageRequester("Error - Failed to generate preview.")
			ProcedureReturn
		EndIf
		Protected URL.s = General::#Address + "preview?UUID=" + DataModel::StripPrefix(Result, General::#Prefix_PreviewArticle)
		OpenURLInNewTab(URL)
	EndProcedure
	
	Procedure NewTag_Callback(Success, Result.s, UserData)
		If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_NewTag)
			MessageRequester("Error - Failed to create new tag.")
			ProcedureReturn
		EndIf
		
		Result = DataModel::StripPrefix(Result, General::#Prefix_NewTag)
		
		LastElement(DataModel::Tags())
		AddElement(DataModel::Tags())
		DataModel::Tags()\ID = Val(DataModel::GetField(Result, 1))
		DataModel::Tags()\DefaultName = DataModel::GetField(Result, 2)
		DataModel::Tags()\Color = DataModel::GetField(Result, 3)
		DataModel::Tags()\BinaryID = Pow(2, ListIndex(DataModel::Tags()))
		
		AddGadgetItem(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::FormatTagListItem(DataModel::@Tags()))
		SetGadgetItemData(TagsList\ListIcon, ListIndex(DataModel::Tags()), DataModel::Tags()\ID)
		SetGadgetState(TagsList\ListIcon, ListIndex(DataModel::Tags()))
		AddGadgetItem(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\DefaultName)
		SetGadgetItemData(ArticleEditor\ListviewTags, ListIndex(DataModel::Tags()), DataModel::Tags()\BinaryID)
		
		State\CurrentTagID = DataModel::Tags()\ID
		HTTPRequest(#PB_HTTP_Get, General::#Address + "admin/", "gettag=" + State\CurrentTagID, @EditTag_Callback())
	EndProcedure
	
	Procedure EditTag_Callback(Success, Result.s, UserData)
		Protected Entry.s
		
		If Not Success Or Not DataModel::ValidateResponse(Result, General::#Prefix_TagContent)
			MessageRequester("Error - Failed to load tag.")
			ProcedureReturn
		EndIf
		
		Result = DataModel::StripPrefix(Result, General::#Prefix_TagContent)
		
		Entry = DataModel::GetRecord(Result, 1)
		SetGadgetText(TagEditor\StringName, DataModel::GetField(Entry, 1))
		SetGadgetText(TagEditor\StringColor, DataModel::GetField(Entry, 2))
		
		ResetList(TagEditor\LanguageTabs())
		While NextElement(TagEditor\LanguageTabs())
			Entry = DataModel::GetRecord(Result, 2 + ListIndex(TagEditor\LanguageTabs()))
			SetGadgetText(TagEditor\LanguageTabs()\Name, Entry)
		Wend
		
		ChangeSelection()
		State\CurrentSelection = #Sel_Tag
		HideGadget(TagEditor\Container, #False)
		Handler_Resize()
	EndProcedure
	
	Procedure UpdateTag_Callback(Success, Result.s, UserData)
		If Not Success
			MessageRequester("Error - Failed to update tag.")
			ProcedureReturn
		EndIf
		
		If DataModel::FindTagByID(State\CurrentTagID)
			DataModel::Tags()\DefaultName = GetGadgetText(TagEditor\StringName)
			DataModel::Tags()\Color = GetGadgetText(TagEditor\StringColor)
			
			Protected Item = GetGadgetState(TagsList\ListIcon)
			SetGadgetItemText(TagsList\ListIcon, Item, DataModel::FormatTagListItem(DataModel::@Tags()))
			SetGadgetItemText(ArticleEditor\ListviewTags, Item, DataModel::Tags()\DefaultName)
			Handler_Tags()
		EndIf
	EndProcedure
	
EndModule
; IDE Options = SpiderBasic 3.10 (Windows - x86)
; CursorPosition = 671
; FirstLine = 184
; Folding = BAAAAAw
; iOSAppOrientation = 0
; AndroidAppCode = 0
; AndroidAppOrientation = 0
; EnableXP
; DPIAware
; CompileSourceDirectory