/**
 *   Løsningsforslag til eksamen i OOProg (i C++), mai 2026, oppgave 2.
 *
 *   Programmet holder orden på ulike filmer som går på EN kino
 *   (med mange saler og til ulike tidspunkter i løpet av en dag)
 *   og antall besøkende pr.forestilling av filmene.
 *
 *   Programmet:
 *      - skriver data om alle filmene
 *      - leser/legger inn en ny film
 *      - fjerner/sletter en film, og skriver dens data bakerst på en fil
 *      - registrerer besøkende på EN forestilling av hver av filmene
 *      - finner og skriver filmen med gjennomsnittlig flest besøkende
 *        pr.forestilling
 *      - sorterer filmene etter avtagende antall gjennomsnittlig besøkende
 *      - leser HELE datastrukturen fra fil
 *
 *   @file     EX_V26_2.CPP
 *   @author   Frode Haug, NTNU
 */


#include <iostream>           //  cout, cin
#include <fstream>            //  ifstream, (o)fstream
#include <string>
#include <vector>
#include <list>
#include <algorithm>          //  count
#include "LesData2.h"         //  Verktøykasse for lesing av diverse data
using namespace std;


const int SALER  =   6;    ///<  Antall saler på kinoen.
const int START  =  11;    ///<  Klokkeslett for dagens første forestilling.
const int SLUTT  =  22;    ///<  Klokkeslett for dagens siste forestilling.
const int BESOK  = 500;    ///<  Maksimum antall besøkende på en film EN dag.


/**
 *  Baseklassen 'Alle' med dens tittel, hvilken salnummer og klokkeslett
 *  den går, samt antall besøkende på hver ulike forestilling av filmen.
 */
class Alle  {
  private:
    string tittel;          //  Filmens tittel.
    int    salNr,           //  FAST salnummer filmen går i HVER dag.
           klokkeslett;     //  FAST klokkeslett filmen vises - på formen: TTMM
    vector <int> besokende; //  Antall besøkende på hver av forestillingene.
  public:
    Alle()  { salNr = klokkeslett = 0;  }                      //  Ferdiglaget
    Alle(ifstream & inn);                                      //  Oppgave 2G
    virtual void lesData();                                    //  Oppgave 2B
    void  registrer();                                         //  Oppgave 2C
    virtual void skrivData() const;                            //  Oppgave 2A
    void  skrivTilFil() const;                                 //  Oppgave 2D
    float snitt() const;                                   //  NY: Oppgave 2E
    int   summer() const;                                      //  Oppgave 2A
};


/**
 *  Avledet klasse 'Aldersgrense' med kun dets aldersgrense.
 */
class Grense : public Alle  {
  private:
    int grense;             //  Filmens aldersgrense.
  public:
    Grense()  {  grense = 0;  }                                //  Ferdiglaget
    Grense(ifstream & inn);                                    //  Oppgave 2G
    virtual void lesData();                                    //  Oppgave 2B
    virtual void skrivData() const;                            //  Oppgave 2A
};


void fjernFilm();                                              //  Oppgave 2D
void lesFraFil();                                              //  Oppgave 2G
void mestTilstede();                                           //  Oppgave 2E
void nyFilm();                                                 //  Oppgave 2B
void registrerBesokende();                                     //  Oppgave 2C
void skrivAlleFilmer();                                        //  Oppgave 2A
void skrivMeny();                                              //  Ferdiglaget
void sorterFilmene();                                          //  Oppgave 2F


list <Alle*> gFilmer;                   ///<  ALLE NÅVÆRENDE aktuelle filmer.


/**
 *  Hovedprogrammet.
 */
int main() {
  char valg;
  int nr;

  lesFraFil();                                                  //  Oppgave 2G

  skrivMeny();
  valg = lesChar("\nKommando");

  while (valg != 'Q') {
     switch (valg) {
       case 'S': skrivAlleFilmer();           break;           //  Oppgave 2A
       case 'N': nyFilm();                    break;           //  Oppgave 2B
       case 'R': registrerBesokende();        break;           //  Oppgave 2C
       case 'F': fjernFilm();                 break;           //  Oppgave 2D
       case 'M': mestTilstede();              break;           //  Oppgave 2E
       case 'O': sorterFilmene();             break;           //  Oppgave 2F
       default:  skrivMeny();                 break;
     }
     valg = lesChar("\nKommando");
  }

  cout << "\n\n";

  return 0;
}


// ---------------------------------------------------------------------------
//                       DEFINISJON AV KLASSE-FUNKSJONER:
// ---------------------------------------------------------------------------

/**
 *  Oppgave 2G - Leser ALLE data om ETT Arrangement inn fra fil.
 *
 *  @param   inn  -  Filen det leses inn fra
 */
Alle::Alle(ifstream & inn)  {
  int antall, tall;
  inn >> salNr >> klokkeslett;   inn.ignore();   //  Leser inn
  getline(inn, tittel);                          //    enkelt-medlemmene:

  inn >> antall;
  for (int i = 0;  i < antall;  i++)  {          //  Leser inn antall
      inn >> tall;                               //    besøkende pr.
      besokende.push_back(tall);                 //    forestilling.
  }
  inn.ignore();
}


/**
 *  Oppgave 2B - Leser inn 'Alle' sine hoveddata.
 */
void Alle::lesData()  {
  cout << "\tTittel: ";   getline(cin, tittel);
  salNr = lesInt("\tSalnr", 1, SALER);
  klokkeslett = lesInt("\tTime  ", START, SLUTT) * 100 +  //  Lagrer tidspunkt
                lesInt("\tMinutt ", 0, 59);               //    som:  TTMM
}


/**
 *  Oppgave 2C - Leser og registrerer antall besokende på filmen EN gitt dag.
 */
void Alle::registrer()  {
  besokende.push_back(lesInt("\tAntall besokende i dag", 0, BESOK));
}


/**
 *  Oppgave 2A - Skriver ut ALT om 'Alle' på skjermen.
 *
 *  @see   summer()
 */
void Alle::skrivData() const  {
  cout << "\tTittel: " << tittel << "\n\t\tSal: " << salNr
       << "   Kl. " << klokkeslett/100 << ':';       //  Timen.
  if (klokkeslett%100 < 10) cout << '0';             //  Legger evt. til '0'.
                                                     //  Minutt og totalen:
  cout << klokkeslett%100 << "   Totalt besokende: " << summer() << '\n';
}


/**
 *  Oppgave 2D - Skriver tittel og besøkende BAKERST på fil (appender).
 *
 *  @see   Alle::skrivData(),    Grense::skrivData()
 *  @see   summer()
 */
void Alle::skrivTilFil() const  {
   ofstream utfil("ex_v26_UTGAATTE.dta", ios::app);   //  Åpner for appending.
// Eller:  fstream utfil("ex_v26_UTGAATTE.dta", ios::out | ios::app);

   cout << "\n\tFolgende film fjernes:\n";
   skrivData();

   cout << "\tog dets data skrives BAKERST paa filen 'EX_V26_UTGAATTE.DTA'\n";

   utfil << tittel << "   Besokende totalt: " << summer()
         << "    Pr.forestilling: ";
   for (const auto & val : besokende)              //  Skriver ALLE besøkende:
       utfil << ' ' << val;
   utfil << '\n';
}


/**
 *  NY - Oppgave 2E - Beregner og returnerer gjennomsnittlig antall besøkende
 *                    alle forestillinger av filmen.
 *
 *  @return   Gjennomsnittlig antall besøkende pr forestilling
 *  @see      summer()
 */
float Alle::snitt() const  {
  return (summer() / static_cast <float> (besokende.size()));
}


/**
 *  Oppgave 2A - Summerer og returnerer summen av 'besokende's elementer.
 *
 *  @return   Totalsummen av alle elementene i 'besokende'
 */
int Alle::summer()  const  {
  int sum = 0;
  for (int i = 0;  i < besokende.size();  i++)
      sum += besokende[i];
  return sum;
}


// ---------------------------------------------------------------------------

/**
 *  Oppgave 2G - Leser inn ALLE data om EN Konsert fra fil.
 *
 *  @param   inn  -  Filen det leses inn fra
 */
Grense::Grense(ifstream & inn) : Alle(inn)  {
  inn >> grense;     inn.ignore();
}


/**
 *  Oppgave 2B - Leser inn 'Grense' sine hoveddata.
 *
 *  @see   Alle::lesData()
 */
void Grense::lesData()  {
  int alder;

  Alle::lesData();

  do  {
    alder = lesInt("\tMinste aldersgrense", 6, 18);
  } while (alder % 3 != 0);       // Siden lovlige er:  6, 9, 12, 15 og 18

  grense = alder;
}


/**
 *  Oppgave 2A - Skriver ut ALT om 'Grense' på skjermen.
 *
 *  @see   Alle::skrivData()
 */
void Grense::skrivData() const  {
  Alle::skrivData();

  cout << "\t\tALDERSGRENSE: " << grense << '\n';
}


// ---------------------------------------------------------------------------
//                       DEFINISJON AV ANDRE FUNKSJONER:
// ---------------------------------------------------------------------------

/**
 *  Oppgave 2A - Skriver ut ALT om ALLE filmer.
 *
 *  @see   Alle::skrivData(),  Grense::skrivData()
 */
void skrivAlleFilmer()  {
  int nr = 0;
  if (!gFilmer.empty())  {                    //  Filmer finnes:
     cout << "\n\tFolgende filmer gaar akkurat naa paa kinoen:\n\n";

     for (const auto & val : gFilmer)  {      //  Går igjennom alle filmene:
         cout << '\t' << ++nr << ':';         //  Skriver dets nummer,
         val->skrivData();                    //    og data.
         if (nr % 15 == 0)  {                 //  Venter for hver femtende:
            cout << "\t\t\t\t\tTrykk ENTER for aa fortsette utskriften .....";
            getchar();
         }
     }
  } else
    cout << "\n\tIngen filmer er pr.naa registrert/lagt inn.\n";
}


/**
 *  Oppgave 2B - Legger inn en ny film.
 *
 *  @see   Alle::Alle(),       Grense::Grense()
 *  @see   Alle::lesData()  ,  Grense::lesData()
 *  @see   Alle::skrivData(),  Grense::skrivData()
 */
void nyFilm()  {
  Alle* nyFilm;
  char type;

  do  {                      //  Leser og sikrer om har aldersgrense eller ei:
    type = lesChar("\tFilm U(ten) M(ed aldersgrense)");
  } while (type != 'U'  &&  type != 'M');

  switch (type)  {                             //  Lager nytt aktuelt objekt:
    case 'U':  nyFilm = new Alle;      break;
    case 'M':  nyFilm = new Grense;    break;
  }

  nyFilm->lesData();                           //  Dens data leses inn.
  cout << "\n\tRegistrerte nye data:\n";
  nyFilm->skrivData();                         //  Skriver dets innleste data.
  gFilmer.push_back(nyFilm);                   //  Legges inn i listen.
}


/**
 *  Oppgave 2C - Registrerer ALLE besøkende i ALLE saler
 *               på ALLE forestillinger på EN gitt dag.
 *
 *  @see   Alle::skrivData(),  Grense::skrivData()
 *  @see   Alle::registrer()
 */
void registrerBesokende()  {
  auto it = gFilmer.begin();
  while (it != gFilmer.end())  {          //  Går igjennom ALLE filmene:
    (*it)->skrivData();                   //  Deres data skrives, og
    (*it++)->registrer();                 //    dagens besøkende registreres.
  }
}


/**
 *  Oppgave 2D - Fjerner (om aktuelt) en film.
 *
 *  @see   skrivAlleFilmer()
 *  @see   Alle::skrivTilFil()
 */
void fjernFilm()  {

  skrivAlleFilmer();

  int nr = lesInt("\n\tFjern film nr (0=ingen)", 0, gFilmer.size());
  if (nr != 0)  {
     auto it = gFilmer.begin();  advance(it, nr-1);   //  Finner rett film.
     (*it)->skrivTilFil();                            //  Den skrives til fil.
     delete (*it);                                    //  OBJEKTET slettes.
     gFilmer.erase(it);                               //  PEKEREN slettes.
  } else
     cout << "\n\n\tOK - ingen filmer fjernes.\n";
}


/**
 *  Oppgave 2E - Skriver filmen det gjennomsnittlig har vært flest tilstede på.
 *
 *  @see   Alle::snitt()
 *  @see   Alle::skrivData(),    Grense::skrivData()
 */
void mestTilstede()  {
  float snitt, mest = 0.0;
  Alle* film = nullptr;
//  int nr = -1;

  auto it = gFilmer.begin();
  for (int i = 0;  i < gFilmer.size();  i++)  {  //  Går gjennom ALLE filmene:
      snitt = (*it)->snitt();                    //  Henter ENS snitt:
      if (snitt > mest)  {                       //  Størst gjennomsnitt?
         mest = snitt;                           //  Lagrer unna storst snitt.
         film = *it;                             //  Pekeer til selve filmen.
//         nr = i+1;                             //  Evt. dets nummer istedet.
      }
      it++;
  }

  cout << "\n\tFilmen med gjennomsnittlig flest besokende:\n\n";
  if (film)   {                                  //  Film funnet:
     film->skrivData();                          //  Skriver dens data.
//  Evt. bare:   cout << "\t\tEr nummer: " << nr << '\n';
     cout << "\tSnitt pr.forestilling: " << mest << '\n';
  } else
     cout << "\t\tIngen\n";
}


/**
 *  Oppgave 2F - Sorterer filmene etter totalantallet som har sett filmen.
 *
 *  @see   skrivAlleFilmer()
 *  @see   Alle::snitt()
 */
void sorterFilmene()  {
  cout << "\n\nFOR sortering etter totalt antall besokende:\n";
  skrivAlleFilmer();

  gFilmer.sort([] (Alle* a1, Alle* a2)      //  Sorterer vha. Lambda-funksjon:
                  {  return (a1->summer() > a2->summer()); });
  cout << "\n\nETTER sortering:\n";
  skrivAlleFilmer();
}


/**
 *  Oppgave 2G - Leser ALLE kinoens nåværende filmer inn fra fil.
 *
 *  @see   Alle::Alle(...),   Grense::Grense(...)
 */
void lesFraFil()  {
  ifstream innfil("EX_V26_KINO.DTA");
  int antall;
  char filmType;

  if (innfil)  {                                //  Filen finnes:
    cout << "\n\nLeser inn fra filen 'EX_V26_KINO.DTA' .....\n\n";
    innfil >> antall;   innfil.ignore();        //  Leser antall filmer.
    for (int i = 0;  i < antall;  i++)  {       //  For hver film:
        innfil >> filmType;   innfil.ignore();  //  Leser typen aldersgrense.
        switch (filmType)  {                    //  Leser og legger inn film:
          case 'A':  gFilmer.push_back(new Alle(innfil));            break;
          case 'G':  gFilmer.push_back(new Grense(innfil));          break;
          default:  cout << "\n\nUlovlig Film-type paa filen!\n\n";  break;
        }
    }
  } else                                        //  Filen finnes IKKE:
    cout << "\n\n\tFant ikke filen 'EX_V26_KINO.DTA'!\n\n";
}


/**
 *  Skriver programmets menyvalg/muligheter på skjermen.
 */
void skrivMeny()  {
  cout << "\nFolgende kommandoer er tilgjengelige:\n"
       << "   S  - Skriv alle filmer\n"
       << "   N  - Ny film\n"
       << "   R  - Registrer besokende\n"
       << "   F  - Fjern/slett en film\n"
       << "   M  - filmen det har vaert gjennomsnittlig Mest/flest paa\n"
       << "   O  - sOrter filmene ut fra gjennomsnittlig mest/flest\n"
       << "   Q  - Quit / avslutt\n";
}
