/**
 *   Løsningsforslag til eksamen i GrProg (i C), desember 2025, oppgave 2.
 *
 *   Program som holder orden på personene i en sosial gruppe,
 *   og på hvilke dager disse evt. kan møtes/treffes i EN gitt måned.
 *
 *   @file     EX_H25_2.C
 *   @author   Frode Haug, NTNU
 */


#include <stdio.h>               //  printf, FILE
#include <stdbool.h>             //  bool
#include <stdlib.h>              //  sizeof, malloc
#include <string.h>              //  strcpy, strlen
#include "LesData.h"             //  Verktøykasse for lesing av diverse data


#define   MAXDELTAGERE  20       ///<  Max. antall deltagere i gruppen.
#define   ANTDAG        31       ///<  Max. antall dager i en måned.
const int STRLEN   =    80;      ///<  Max. tekstlengde.


/**
 *  Deltager (med navnet, mailadresse, telefon og hvilke dager kan delta).
 */
struct Deltager {
  char* navn;                    //  Deltagerens navn.
  char* mail;                    //  Mailadresse.
  int   tlf;                     //  Telefon (fast eller mobil).
  bool  dag[ANTDAG];             //  Kan (ikke) på dag nr.'i'
};                               //       der indeksene 0 til ANTDAG-1 brukes.


void skrivMeny();                                              //  Ferdiglaget
void skrivAlleDeltagere();                                     //  Oppgave 2A
void deltagerSkrivData(const struct Deltager* deltager);       //  Oppgave 2A
void nyDeltager();                                             //  Oppgave 2B
void deltagerLesData(struct Deltager* deltager);               //  Oppgave 2B
void fjernDeltager();                                          //  Oppgave 2C
void deltagerFrigi(struct Deltager* deltager);                 //  Oppgave 2C
void nullstillAlle();                                          //  Oppgave 2D
void deltagerNullstill(struct Deltager* deltager);             //  Oppgave 2D
void lesDager();                                               //  Oppgave 2E
void deltagerLesDager(struct Deltager* deltager);              //  Oppgave 2E
void finnMoteDager();                                          //  Oppgave 2F
void lesFraFil();                                              //  Oppgave 2G
void deltagerLesFraFil(FILE* inn, struct Deltager* deltager);  //  Oppgave 2G


struct Deltager* gDeltagere[MAXDELTAGERE];    ///<  Array med Deltagere.
int gAntallDeltagere = 0;                     ///<  Antall deltagere hittil.


/**
 *   Hovedprogram.
 */
int main()  {
   char kommando;

   lesFraFil();                                         //  Oppgave 2G

   skrivMeny();
   kommando = lesChar("\nValg");

   while (kommando != 'Q')  {
     switch (kommando)  {
       case 'S':  skrivAlleDeltagere();    break;       //  Oppgave 2A
       case 'N':  nyDeltager();            break;       //  Oppgave 2B
       case 'F':  fjernDeltager();         break;       //  Oppgave 2C
       case 'U':  nullstillAlle();         break;       //  Oppgave 2D
       case 'L':  lesDager();              break;       //  Oppgave 2E
       case 'D':  finnMoteDager();         break;       //  Oppgave 2F
       default:   skrivMeny();             break;
     }
     kommando = lesChar("\nValg");
   }

  return 0;
}


/**
 *  Skriver/presenterer programmets muligheter/valg for brukeren.
 */
void skrivMeny()  {
  printf("\n\nFOLGENDE KOMMANDOER ER LOVLIG:\n");
  printf("\tS   = Skriv alle deltageres hoveddata\n");
  printf("\tN   = Ny deltager\n");
  printf("\tF   = Fjern deltager\n");
  printf("\tU   = nUllstill dagene for ALLE deltagerne\n");
  printf("\tL   = Les dager for EN deltager\n");
  printf("\tD   = finn felles Dager for aktuelle moter/treff\n");
  printf("\tQ   = Quit/avslutt\n");
}


/**
 *  Oppgave 2A - Skriver ALLE deltagernes hoveddata.
 *
 *  @see   deltagerSkrivData(...)
 */
void skrivAlleDeltagere()  {
  printf("\n\tAlle deltagernes hoveddata:\n");
  for (int i = 0;  i < gAntallDeltagere;  i++)  {
      printf("\t%5i:   ", i+1);
      deltagerSkrivData(gDeltagere[i]);        //  Hver skriver seg selv.
  }
}


/**
 *  Oppgave 2A - Skriver ALLE data (unntatt 'dag') for EN deltager ut på skjerm.
 *
 *  @param   deltager  -  Deltageren det skrives ut alle hoveddata for
 */
void deltagerSkrivData(const struct Deltager* deltager)  {
  printf("%s,  mail: %s,  tlf: %i\n",
         deltager->navn, deltager->mail, deltager->tlf);
}


/**
 *  Oppgave 2B - Legger (om mulig) inn EN ny deltager.
 *
 *  @see   deltagerLesData(...)
 */
void nyDeltager()  {
  if (gAntallDeltagere < MAXDELTAGERE)  {      //  Plass til flere deltagere:
                                      //  Ny opprettes, leser selv sine data:
     gDeltagere[gAntallDeltagere] =
                           (struct Deltager*) malloc(sizeof(struct Deltager));
     deltagerLesData(gDeltagere[gAntallDeltagere]);
     printf("\n\tNy deltager registrert som nr.%i\n", ++gAntallDeltagere);
  } else                             //  Fullt:
     printf("\n\tFullt med %i deltagere allerede!\n\n", MAXDELTAGERE);
}


/**
 *  Oppgave 2B - Leser inn alle data om EN deltager (unntatt 'dag').
 *
 *  @param   deltager  -  Deltageren som får sine data innlest
 *  @see     deltagerNullstill(...)
 */
void deltagerLesData(struct Deltager* deltager)  {
  char buffer[STRLEN];

  deltager->navn = lagOgLesText("\tNavn");
  deltager->mail = lagOgLesText("\tMail");
  deltager->tlf  = lesInt("\tTelefon", 10000000, 99999999);

  deltagerNullstill(deltager);
}


/**
 *  Oppgave 2C - Fjerner EN deltager.
 *
 *  @see   deltagerFrigi(...)
 */
void fjernDeltager()  {                   //  Ber om deltagernummer:
  int nr = lesInt("\tFjern deltager (0 = ingen/angre)", 0, gAntallDeltagere);

  if (nr != 0)  {                         //  Aktuell valgt:
     deltagerFrigi(gDeltagere[nr-1]);     //  Structens memory frigis.
     free(gDeltagere[nr-1]);              //  Selve structen frigis/slettes.
                                          //  Flytter bakerste til "hullet":
     gDeltagere[nr-1] = gDeltagere[--gAntallDeltagere];
     printf("\n\tDeltageren er fjernet/slettet!\n\n");
  } else                                  //  Ingen (0) valgt:
     printf("\n\tOK - ingen deltager ble fjernet/slettet.\n");
}


/**
 *  Oppgave 2C - Frigir all allokert memory inni EN deltager-struct.
 *
 *  @param   deltager  -  Deltageren som får sin memory frigitt
 */
void deltagerFrigi(struct Deltager* deltager)  {
  free(deltager->navn);
  free(deltager->mail);
}


/**
 *  Oppgave 2D - Nullstiller (til 'false') ALLE deltagernes 'dag'-array.
 *
 *  @see   deltagerNullstill(...)
 */
void nullstillAlle()  {
  for (int i = 0;  i < gAntallDeltagere;  i++)
      deltagerNullstill(gDeltagere[i]);
  printf("\n\tALLE deltagernes muligheter for aa delta er nullstilt.\n\n");
}


/**
 *  Oppgave 2D - Nullstiller (til 'false') HELE en deltagers 'dag'-array.
 *
 *  @param   deltager  -  Deltageren som får sin array 'dag' satt til 'false'
 */
void deltagerNullstill(struct Deltager* deltager)  {
  for (int i = 0;  i < ANTDAG;  i++)
      deltager->dag[i] = false;
}


/**
 *  Oppgave 2E - Leser EN deltagers mulige møtedager.
 *
 *  @see   deltagerLesDager(...)
 */
void lesDager()  {
  int nr = lesInt("\tLes dager for deltager", 1, gAntallDeltagere);
  deltagerLesDager(gDeltagere[nr-1]);
}


/**
 *  Oppgave 2E - Leser aktuelle møtedager for EN deltager.
 *
 *  @param    deltager  -  Deltageren som får akyuelle møtedager lest inn
 *  @see      deltagerNullstill(...)
 */
void deltagerLesDager(struct Deltager* deltager)  {
  int dag;                               //  Aktuell dag.

  deltagerNullstill(deltager);           //  Nullstiller alle dager.
                                         //  Leser (om mulig) 1.møtedag:
  dag = lesInt("\tSkriv dagnumre (avslutt med 0)", 0, ANTDAG);
  while (dag != 0)  {                    //  Så lenge flere dager:
    deltager->dag[dag-1] = true;         //  Setter at kan møte.
                                         //  Leser (om mulig) neste møtedag:
    dag = lesInt("\tSkriv dagnumre (avslutt med 0)", 0, ANTDAG);
  }
  printf("\n\tRegistrert at KAN folgende dager:\n\t");
  for (int i = 0;  i < ANTDAG;  i++)     //  Skriver hva registrert:
      if (deltager->dag[i]) printf("  %i", i+1);
  printf("\n");
}



/**
 *  Oppgave 2F - Finner dager der flest kan møtes/samles.
 */
void finnMoteDager()  {
  int antallKan[ANTDAG];             //  Antall som kan møte hver månedsdag.
  int i, j;                          //  Løkkevariable.

  for (j = 0;  j < ANTDAG;  j++)  antallKan[j] = 0;   // Nullstiller array.

  for (i = 0;  i < gAntallDeltagere;  i++)            //  For hver deltager,
      for (j = 0;  j < ANTDAG;  j++)                  //    og hver månedsdag:
         if (gDeltagere[i]->dag[j]) antallKan[j]++;   //  Tell opp antall som
                                                      //    kan møte.
  printf("\n\tFolgende dager kan ALLE mote:\n\t");    //  ALLE kan møte
  for (j = 0;  j < ANTDAG;  j++)                      //    følgende dager:
      if (antallKan[j] == gAntallDeltagere)  printf("  %i", j+1);

  printf("\n\n\tFolgende dager kan MINST halvparten mote:\n\t");
  for (j = 0;  j < ANTDAG;  j++)         //  Minst halvparten, men IKKE alle:
      if (antallKan[j] >= (gAntallDeltagere/2)  &&  antallKan[j] < gAntallDeltagere)
         printf("  %i (%i)", j+1, antallKan[j]);
  printf("\n\n");
}


/**
 *  Oppgave 2G - Leser ALLE deltagerne fra fil.
 *
 *  @see   deltagerLesFraFil(...)
 */
void lesFraFil()  {
  FILE* innfil  = fopen("EX_H25_SAMLING.DTA", "r");  //  Åpner aktuell fil:

  if (innfil) {                                   //  Filen finnes:
     fscanf(innfil, "%i", &gAntallDeltagere);     //  Leser antall deltagere.
     getc(innfil);                                //  Leser avsluttende '\n'.
                                            //  Alle deltagere lages og leses:
     for (int i = 0;  i < gAntallDeltagere;  i++) {
       gDeltagere[i] = (struct Deltager*) malloc(sizeof(struct Deltager));
       deltagerLesFraFil(innfil, gDeltagere[i]);  //  Leser resten selv.
     }
     printf("\n\n\tDeltagere er lest inn fra 'EX_H25_SAMLING.DTA'!\n\n");
  } else                                          //  Filen ikke funnet:
     printf("\n\tFant ikke filen 'EX_H25_SAMLING.DTA'!\n\n");

  fclose(innfil);                                 //  Lukker åpen fil.
}


/**
 *  Oppgave 2G - Leser ALT om EN deltager inn fra fil.
 *
 *  @param  inn       -  Filen det skal leses inn fra
 *  @param  deltager  -  Deltageren som får innlest sine data
 */
void deltagerLesFraFil(FILE* inn, struct Deltager* deltager)  {
  char buffer[STRLEN];
  int kan;                                 //  Kan (ikke) møte (0 eller 1).

  fscanf(inn, "%i", &(deltager->tlf));     //  Leser telefonnummer.
  getc(inn);                               //  Forkaster ' ' før navn.

  fgets(buffer, STRLEN, inn);              //  Leser navnet.
  buffer[strlen(buffer)-1] = '\0';         //  Tar vekk '\n' bakerst.
  deltager->navn = (char*) malloc((strlen(buffer) + 1) * sizeof(char));
  strcpy(deltager->navn, buffer);

  fgets(buffer, STRLEN, inn);              //  Leser mailadressen.
  buffer[strlen(buffer)-1] = '\0';         //  Tar vekk '\n' bakerst.
  deltager->mail = (char*) malloc((strlen(buffer) + 1) * sizeof(char));
  strcpy(deltager->mail, buffer);

  for (int i = 0;  i < ANTDAG;  i++)  {    //  Leser HELE måneden:
     fscanf(inn, "%i", &kan);              //  Leser '0' eller '1'.
     deltager->dag[i] = (kan == 1);        //  Omgjør 0/1 til true/false.
  }
  getc(inn);                               //  Forkaster avsluttende '\n'.
}
