// swad_announcement.c: Global announcement

/*
    SWAD (Shared Workspace At a Distance),
    is a web platform developed at the University of Granada (Spain),
    and used to support university teaching.

    This file is part of SWAD core.
    Copyright (C) 1999-2025 Antonio Caas Vargas

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General 3 License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/*********************************** Headers *********************************/
/*****************************************************************************/

#include "swad_action_list.h"
#include "swad_alert.h"
#include "swad_announcement.h"
#include "swad_announcement_database.h"
#include "swad_autolink.h"
#include "swad_box.h"
#include "swad_database.h"
#include "swad_error.h"
#include "swad_form.h"
#include "swad_global.h"
#include "swad_hidden_visible.h"
#include "swad_HTML.h"
#include "swad_parameter.h"
#include "swad_parameter_code.h"
#include "swad_role.h"

/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/

extern struct Globals Gbl;

/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/

static void Ann_PutIconToAddNewAnnouncement (__attribute__((unused)) void *Args);

static void Ann_GetAnnouncementDataFromRow (MYSQL_RES *mysql_res,
                                            struct Ann_Announcement *Announcement);

static void Ann_DrawAnAnnouncement (struct Ann_Announcement *Announcement,
                                    Lay_Show_t ShowAll,
                                    Usr_Can_t ICanEdit);
static void Ann_PutParAnnCod (void *AnnCod);
static void Ann_PutSubjectMessage (const char *Field,const char *Label,
                                   unsigned Rows);

static void Ann_HideUnhideAnnouncement (HidVis_HiddenOrVisible_t HiddenOrVisible);

/*****************************************************************************/
/************************** Show global announcements ************************/
/*****************************************************************************/

void Ann_ShowAllAnnouncements (void)
  {
   extern const char *Hlp_COMMUNICATION_Announcements;
   extern const char *Txt_Announcements;
   extern const char *Txt_No_announcements;
   MYSQL_RES *mysql_res;
   struct Ann_Announcement Announcement;
   unsigned NumAnnouncements;
   unsigned NumAnn;
   Usr_Can_t ICanEdit = Gbl.Usrs.Me.Role.Logged == Rol_SYS_ADM ? Usr_CAN :
								 Usr_CAN_NOT;

   /***** Get announcements from database *****/
   if (ICanEdit)
      /* Select all announcements */
      NumAnnouncements = Ann_DB_GetAllAnnouncements (&mysql_res);
   else if (Gbl.Usrs.Me.Logged)
      /* Select only announcements I can see */
      NumAnnouncements = Ann_DB_GetAnnouncementsICanSee (&mysql_res);
   else // No user logged
      /* Select only active announcements for unknown users */
      NumAnnouncements = Ann_DB_GetAnnouncementsForUnknownUsers (&mysql_res);

   /***** Begin box *****/
   Box_BoxBegin (Txt_Announcements,
                 ICanEdit ? Ann_PutIconToAddNewAnnouncement :
			    NULL,NULL,
		 Hlp_COMMUNICATION_Announcements,Box_NOT_CLOSABLE);

      if (!NumAnnouncements)
	 Ale_ShowAlert (Ale_INFO,Txt_No_announcements);

      /***** Show the announcements *****/
      for (NumAnn = 0;
	   NumAnn < NumAnnouncements;
	   NumAnn++)
	{
	 /* Get announcement data */
	 Ann_GetAnnouncementDataFromRow (mysql_res,&Announcement);

	 /* Show the announcement */
	 Ann_DrawAnAnnouncement (&Announcement,
	                         Lay_SHOW,	// Show all announcements
	                         ICanEdit);
	}

   /***** End box *****/
   Box_BoxEnd ();

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);
  }

/*****************************************************************************/
/******************** Put icon to add a new announcement *********************/
/*****************************************************************************/

static void Ann_PutIconToAddNewAnnouncement (__attribute__((unused)) void *Args)
  {
   Ico_PutContextualIconToAdd (ActWriAnn,NULL,NULL,NULL);
  }

/*****************************************************************************/
/************************** Show global announcements ************************/
/*****************************************************************************/

void Ann_ShowMyAnnouncementsNotMarkedAsSeen (void)
  {
   MYSQL_RES *mysql_res;
   unsigned NumAnnouncements;
   unsigned NumAnn;
   struct Ann_Announcement Announcement;

   /***** Select announcements not seen *****/
   Rol_GetRolesInAllCrss (&Gbl.Usrs.Me.UsrDat);
   NumAnnouncements = Ann_DB_GetAnnouncementsNotSeen (&mysql_res);

   /***** Show the announcements *****/
   if (NumAnnouncements)
     {
      HTM_DIV_Begin ("class=\"CM\"");

	 for (NumAnn = 0;
	      NumAnn < NumAnnouncements;
	      NumAnn++)
	   {
	    /* Get announcement data */
	    Ann_GetAnnouncementDataFromRow (mysql_res,&Announcement);

	    /* Show the announcement */
	    Ann_DrawAnAnnouncement (&Announcement,
	                            Lay_DONT_SHOW,	// Don't show all announcements
	                            Usr_CAN_NOT);	// I can not edit
	   }

      HTM_DIV_End ();
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);
  }

/*****************************************************************************/
/********************** Get announcement data from row ***********************/
/*****************************************************************************/

static void Ann_GetAnnouncementDataFromRow (MYSQL_RES *mysql_res,
                                            struct Ann_Announcement *Announcement)
  {
   MYSQL_ROW row;

   /***** Get next row from result *****/
   row = mysql_fetch_row (mysql_res);

   /***** Get announcement code (row[0]) *****/
   if (sscanf (row[0],"%ld",&Announcement->AnnCod) != 1)
      Err_WrongAnnouncementExit ();

   /***** Get status of the announcement (row[1]) *****/
   Announcement->HiddenOrVisible = HidVis_GetHiddenFrom01 (row[1][0]);	// 1 ==> hidden

   /***** Get roles (row[2]) *****/
   if (sscanf (row[2],"%u",&Announcement->Roles) != 1)
      Err_ShowErrorAndExit ("Error when reading roles of announcement.");

   /***** Get the subject (row[3]), the content (row[4]), and insert links *****/
   Str_Copy (Announcement->Subject,row[3],sizeof (Announcement->Subject) - 1);
   Str_Copy (Announcement->Content,row[4],sizeof (Announcement->Content) - 1);
   ALn_InsertLinks (Announcement->Content,Cns_MAX_BYTES_TEXT,50);
  }

/*****************************************************************************/
/****************** Draw an announcement as a yellow note ********************/
/*****************************************************************************/

static void Ann_DrawAnAnnouncement (struct Ann_Announcement *Announcement,
                                    Lay_Show_t ShowAll,
                                    Usr_Can_t ICanEdit)
  {
   extern const char *Txt_Users;
   extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   extern const char *Txt_Do_not_show_again;
   static const char *ContainerClass[HidVis_NUM_HIDDEN_VISIBLE] =
     {
      [HidVis_HIDDEN ] = "NOTICE_BOX NOTICE_BOX_WIDE LIGHT",
      [HidVis_VISIBLE] = "NOTICE_BOX NOTICE_BOX_WIDE",
     };
   static Act_Action_t ActionHideUnhide[HidVis_NUM_HIDDEN_VISIBLE] =
     {
      [HidVis_HIDDEN ] = ActUnhAnn,	// Hidden ==> action to unhide
      [HidVis_VISIBLE] = ActHidAnn,	// Visible ==> action to hide
     };
   Rol_Role_t Role;
   bool SomeRolesAreSelected;

   /***** Begin yellow note *****/
   HTM_DIV_Begin ("class=\"NOTICE_CONT\"");
      HTM_DIV_Begin ("class=\"%s\"",ContainerClass[Announcement->HiddenOrVisible]);

	 if (ICanEdit == Usr_CAN)
	   {
	    /***** Icon to remove announcement *****/
	    Ico_PutContextualIconToRemove (ActRemAnn,NULL,
					   Ann_PutParAnnCod,&Announcement->AnnCod);

	    /***** Icon to hide/unhide the announcement *****/
	    Ico_PutContextualIconToHideUnhide (ActionHideUnhide,NULL,	// TODO: Put anchor
					       Ann_PutParAnnCod,&Announcement->AnnCod,
					       Announcement->HiddenOrVisible);
	   }

	 /***** Write the subject of the announcement *****/
	 HTM_DIV_Begin ("class=\"NOTICE_SUBJECT NOTICE_SUBJECT_%s\"",
			The_GetSuffix ());
	    HTM_Txt (Announcement->Subject);
	 HTM_DIV_End ();

	 /***** Write the content of the announcement *****/
	 HTM_DIV_Begin ("class=\"NOTICE_TEXT NOTICE_TEXT_%s\"",
			The_GetSuffix ());
	    HTM_Txt (Announcement->Content);
	 HTM_DIV_End ();

	 /***** Write announcement foot *****/
	 /* Begin container for foot */
	 HTM_DIV_Begin ("class=\"NOTICE_USERS\"");

	    switch (ShowAll)
	      {
	       case Lay_SHOW:
		  /* Users' roles who can view this announcement */
		  HTM_Txt (Txt_Users); HTM_Colon ();
		  for (Role  = Rol_UNK, SomeRolesAreSelected = false;
		       Role <= Rol_TCH;
		       Role++)
		     if (Announcement->Roles & (1 << Role))
		       {
			if (SomeRolesAreSelected)
			   HTM_Comma ();
			SomeRolesAreSelected = true;
			HTM_SP ();
			HTM_Txt (Txt_ROLES_PLURAL_abc[Role][Usr_SEX_UNKNOWN]);
		       }
		  break;
	       case Lay_DONT_SHOW:
	       default:
		  /***** Put form to mark announcement as seen *****/
		  Lay_PutContextualLinkIconText (ActAnnSee,NULL,
						 Ann_PutParAnnCod,&Announcement->AnnCod,
						 "times.svg",Ico_BLACK,
						 Txt_Do_not_show_again,NULL);
		  break;
	      }

	 /* End container for foot */
	 HTM_DIV_End ();

      /***** End yellow note *****/
      HTM_DIV_End ();
   HTM_DIV_End ();
  }

/*****************************************************************************/
/******************** Params used to edit an assignment **********************/
/*****************************************************************************/

static void Ann_PutParAnnCod (void *AnnCod)
  {
   if (AnnCod)
      ParCod_PutPar (ParCod_Ann,*((long *) AnnCod));
  }

/*****************************************************************************/
/***************** Show form to create a new announcement ********************/
/*****************************************************************************/

void Ann_ShowFormAnnouncement (void)
  {
   extern const char *Hlp_COMMUNICATION_Announcements;
   extern const char *Txt_Announcement;
   extern const char *Txt_MSG_Subject;
   extern const char *Txt_MSG_Content;
   extern const char *Txt_Users;

   /***** Begin form *****/
   Frm_BeginForm (ActNewAnn);

      /***** Begin box and table *****/
      Box_BoxTableBegin (Txt_Announcement,NULL,NULL,
			 Hlp_COMMUNICATION_Announcements,Box_NOT_CLOSABLE,2);

	 /***** Announcement subject and body *****/
	 Ann_PutSubjectMessage ("Subject",Txt_MSG_Subject, 2);
	 Ann_PutSubjectMessage ("Content",Txt_MSG_Content,20);

	 /***** Users' roles who can view the announcement *****/
	 HTM_TR_Begin (NULL);
	    HTM_TD_TxtColon (Txt_Users);
	    HTM_TD_Begin ("class=\"LT DAT_%s\"",The_GetSuffix ());
	       Rol_WriteSelectorRoles (1 << Rol_UNK |
				       1 << Rol_GST |
				       1 << Rol_STD |
				       1 << Rol_NET |
				       1 << Rol_TCH,
				       1 << Rol_UNK |
				       1 << Rol_GST |
				       1 << Rol_STD |
				       1 << Rol_NET |
				       1 << Rol_TCH,
				       HTM_NO_ATTR);
	    HTM_TD_End ();
	 HTM_TR_End ();

      /***** End table, send button and end box *****/
      Box_BoxTableWithButtonEnd (Btn_CREATE);

   /***** End form *****/
   Frm_EndForm ();
  }

/*****************************************************************************/
/*********** Put form field for message subject or message content ***********/
/*****************************************************************************/

static void Ann_PutSubjectMessage (const char *Field,const char *Label,
                                   unsigned Rows)
  {
   /***** Subject or content *****/
   HTM_TR_Begin (NULL);

      /* Label */
      Frm_LabelColumn ("RT",Field,Label);

      /* Data */
      HTM_TD_Begin ("class=\"LT\"");
	 HTM_TEXTAREA_Begin (HTM_NO_ATTR,
			     "id=\"%s\" name=\"%s\" cols=\"75\" rows=\"%u\""
		             " class=\"INPUT_%s\"",
			     Field,Field,Rows,The_GetSuffix ());
	 HTM_TEXTAREA_End ();
      HTM_TD_End ();

   HTM_TR_End ();
  }

/*****************************************************************************/
/****** Receive a new announcement from a form and store it in database ******/
/*****************************************************************************/

void Ann_ReceiveAnnouncement (void)
  {
   extern const char *Txt_Announcement_created;
   unsigned Roles;
   char Subject[Cns_MAX_BYTES_SUBJECT + 1];
   char Content[Cns_MAX_BYTES_TEXT + 1];

   /***** Get data from form *****/
   /* Get the subject of the announcement */
   Par_GetParHTML ("Subject",Subject,Cns_MAX_BYTES_SUBJECT);

   /* Get the content of the announcement */
   Par_GetParAndChangeFormat ("Content",Content,Cns_MAX_BYTES_TEXT,
                              Str_TO_RIGOROUS_HTML,Str_REMOVE_SPACES);

   /* Get users who can view this announcement */
   Roles = Rol_GetSelectedRoles ();

   /***** Create a new announcement in database *****/
   Ann_DB_CreateAnnouncement (Roles,Subject,Content);

   /***** Write message of success *****/
   Ale_ShowAlert (Ale_SUCCESS,Txt_Announcement_created);

   /***** Refresh list of announcements *****/
   Ann_ShowAllAnnouncements ();
  }

/*****************************************************************************/
/**************************** Hide/unhide a notice ***************************/
/*****************************************************************************/

void Ann_HideAnnouncement (void)
  {
   Ann_HideUnhideAnnouncement (HidVis_HIDDEN);
  }

void Ann_UnhideAnnouncement (void)
  {
   Ann_HideUnhideAnnouncement (HidVis_VISIBLE);
  }

static void Ann_HideUnhideAnnouncement (HidVis_HiddenOrVisible_t HiddenOrVisible)
  {
   long AnnCod;

   /***** Get the code of the global announcement to hide/unhide *****/
   AnnCod = ParCod_GetAndCheckPar (ParCod_Ann);

   /***** Set global announcement as hidden *****/
   Ann_DB_ChangeAnnouncementStatus (AnnCod,HiddenOrVisible);
  }

/*****************************************************************************/
/********************** Remove a global announcement *************************/
/*****************************************************************************/

void Ann_RemoveAnnouncement (void)
  {
   extern const char *Txt_Announcement_removed;
   long AnnCod;

   /***** Get the code of the global announcement *****/
   AnnCod = ParCod_GetAndCheckPar (ParCod_Ann);

   /***** Remove users who have seen the announcement *****/
   Ann_DB_RemoveUsrsWhoSawAnnouncement (AnnCod);

   /***** Remove the announcement *****/
   Ann_DB_RemoveAnnouncement (AnnCod);

   /***** Write message of success *****/
   Ale_ShowAlert (Ale_SUCCESS,Txt_Announcement_removed);

   /***** Refresh list of announcements *****/
   Ann_ShowAllAnnouncements ();
  }

/*****************************************************************************/
/************************ Create a new announcement **************************/
/*****************************************************************************/

void Ann_MarkAnnouncementAsSeen (void)
  {
   long AnnCod;

   /***** Get the code of the global announcement *****/
   AnnCod = ParCod_GetAndCheckPar (ParCod_Ann);

   /***** Mark announcement as seen *****/
   Ann_DB_MarkAnnouncementAsSeenByMe (AnnCod);

   /***** Show other announcements again *****/
   Ann_ShowMyAnnouncementsNotMarkedAsSeen ();
  }
