#include <iostream>
#include <string>
#include <windows.h>

using namespace std;
struct Student
{
    int mark;
    string name;
};
struct School
{
    int number;
};
struct Score
{
    double score;
};
class Journal
{
private:
    Student* students;
    School* schools;
    Score* scores;
    int NumberOfStudents = 0;
    int NOS = 0;
    int KolS = 0;
    int StudentData = 0;
    int AverageScore = 0;

    static int static_NOS; // статическая переменная, задающая номер школы согласно 3 заданию

public:

    static int get_static_NOS() { return static_NOS; } // функция, возвращающая значение статической переменной
    static void set_static_NOS(int _new_static_NOS) { static_NOS = _new_static_NOS; } // установка нового значения статической переменной

    Journal(){ // конструктор без параметров
        cout << "\n\t\t\t\t\t\t\t\tИнициализация конструктора без параметров для объекта " << this << "\n"; // вывод информации о инициализации конструктора
    }
    // конструктор с параметрами полностью повторяет ф-цию NumOfSchool
    Journal(int nos){ // конструктор с параметром
        cout << "\n\t\t\t\t\t\t\t\tИнициализация конструктора с параметром для объекта " << this << "\n"; // вывод информации о инициализации конструктора
        NOS = nos;
        schools = new School[NOS];
        schools[KolS].number = static_NOS; // передача значения статической переменной в качестве номера школы
        KolS++;
    }

    ~Journal(){ // деструктор
        cout << "\n\t\t\t\t\t\t\t\tИнициализация деструктора для объекта " << this << "\n"; // вывод информации о инициализации деструктора
    }

    void NumOfSchool(int nos)
    {
        NOS = nos;
        schools = new School[NOS];
        schools[KolS].number = static_NOS; // в качестве номера школы передаётся значение статической переменной
        KolS++;
    }
    void NumberOfStds(int kol)
    {
        NumberOfStudents = kol;
        students = new Student[NumberOfStudents];
    }
    void SetStds(string name, int mark)
    {
        students[StudentData].name = name;
        students[StudentData].mark = mark;
        StudentData++;
    }
    void SaR(double as) 
    {
        scores = new Score[NOS];
        scores[AverageScore].score = as;
    }
    string Spisok() 
    {
        string b;
        for (int i = 0; i < KolS; i++) {
            b = b + "Школа №: " + to_string(schools[i].number) + "\n";
        }
        return b;
    }
    string SpisokSt()
    {
        string sp;
        for (int i = 0; i < NumberOfStudents; i++) {
            sp = sp + " Фамилия: " + students[i].name + " Оценка: " + to_string(students[i].mark)+"\n";
        }
        return sp;
    }
    string AverScore()
    {
        string h;
        for (int i = 0; i < KolS; i++) {
            h = h + " Средний балл: " + to_string(scores[i].score)+"\n";
        }
        return h;
    }
    void Idol()
    {
        cout << "Фамилия ученика с баллом выше среднего: "<<"\n";
        string u;
        for (int i = 0; i < KolS; i++) {
            for (int j = 0; j < NumberOfStudents; j++) {
                if (scores[i].score < students[j].mark) {
                    u = u +" | "+students[j].name+" | "+"\n";
                }
            }
            cout << u << "\n";
        }
    }
    // перегрузка оператора "Присваивание сложения" для добавления текущего значения оценки ученика в переменную averagescore
    double operator += (double _average_score){ // перегрузка принимает в качестве аргумента переменную типа double
        return (_average_score + this->students[StudentData-1].mark); // возвращения полученного значения после сложения значения переданного аргумента и значения оценки текущего ученика
    }
    // объявление перегруженного оператора ввода дружественным для доступа приватным полям класса и их вводу
    friend istream& operator>> (istream &in, Journal& journal);
};

istream& operator>> (istream &in, Journal& journal){ // перегрузка оператора ввода для прямого ввода координат точек
    cout << "\tВведите фамилию для ученика : "; in >> journal.students[journal.StudentData].name;
    cout << "\tВведите оценку для ученика : "; in >> journal.students[journal.StudentData].mark;
    journal.StudentData++;
    return in;
}

ostream& operator<< (ostream& out, Journal& journal){ //перегрузка оператора вывода
    return out << "\n" << journal.Spisok() << "\n" << journal.SpisokSt() << "\n" << journal.AverScore(); // вывод значений школы через перегрузку оператора вывода
}

int Journal::static_NOS = 1; // установка значения статической переменной по умолчанию во избежание ошибок "Неопределённая ссылка на статическую ф-цию"

int main()
{
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    setlocale(LC_ALL, "rus");
    int kolschool; // количество школ
    int kolstudents; // количество студентов
    int numofschool; // номер школы
    string familia; // фамилия студента
    int mark; // оценка студента
    double averagescore = 0; // средняя оценка
    char change_static_value; // переменная проверки на изменение статического значения класса
    // ввод количества школ
    cout << "Введите количество школ: "; 
    cin >> kolschool;
    // указатель на массив с объектами класса Journal и размером kolschool
    Journal* journals[kolschool];
    for (int i = 0; i < kolschool; i++) {
        // ввод номера школы через статическую переменную согласно 3 заднию
        cout << "\nТекущее значение номера школы: " << Journal::get_static_NOS(); // вывод текущего значения статической переменной (номер школы)
        cout << "\nЖелаете его изменить? y/n "; cin >> change_static_value; // уточнение у пользователя желание изменить значение статической переменной (номер школы)
        // если пользователь изъявляет такое желание, то он вводит новое значение, которое после передаётся в статическую переменную
        if (change_static_value == 'y') {cout << "\nВведите новое значение: "; cin >> numofschool; Journal::set_static_NOS(numofschool); }
        // таким образом, были реализованы 2 типа конструкторов из 3. Конструктор с параметром по умолчанию смотрите во 2 файле
        if (i % 2 == 1){ // если текущий элемент не делится на 2
            journals[i] = new Journal(); // используется конструктор без параметра
            (*journals[i]).NumOfSchool(kolschool); // передача введённых значений
        }
        else { // если текущий элемент делиться на 2
            journals[i] = new Journal(kolschool); // используется конструктор с параметрами, в который передаются введённые значения
        }
            cout << "Введите количество учеников: ";
            cin >> kolstudents;
            // обращение к объекту через указатель "(*journals[i])" необходимо, так как в массиве лежат указатели, а не сами объекты
            (*journals[i]).NumberOfStds(kolstudents);
            for (int j = 0; j < kolstudents; j++) { // был слегка изменён ввод данных ученика, так как теперь они реализованы через перегрузку оператора
                cout << "\nВведите данные по " << j+1 << " ученику.\n\n";
                cin >> (*journals[i]); // ввод фамилии и оценки ученика через перегрузку оператора ввода
                // добавление значения оценки ученика в переменную averagescore через перегрузку оператора "Присваивание сложения"
                averagescore = ((*journals[i]) += averagescore);
            }
            averagescore = averagescore / kolstudents;
            (*journals[i]).SaR(averagescore);
            averagescore = 0;
    }
    for (int i = 0; i < kolschool; i++) {
        cout << (*journals[i]); // вывод значений объекта через перегрузку оператора вывода
        (*journals[i]).Idol();
    }

    for (int i = 0; i < kolschool; i++) delete journals[i]; // удаление элементов динамического массива для очистки ОЗУ

    return 0; // конец программы
}