/*

4. zadatak Ispit jul 2017 v2

Ova verzija radi sa razlicitim brojem polozenih ispita po studentu.

U svakoj liniji tekstualne ulazne datoteke studenti.txt.
nalaze se podaci o studentima i podaci o polozenim ispitima.
Svaka linija u ovoj ulaznoj datoteci se sastoji od vise kolona
koje su medjusobno odvojene znakom tackazarez (;).

U prvoj koloni se nalazi ime i prezime studenta (maksimalan broj karaktera je 50).
U drugoj koloni se nalazi broj indeksa studenta (maksimalan broj karaktera je 9).
Potom slede podaci o polozenim ispitima.

Za svaki ispit cuvaju se podaci o sifri ispita (maksimalan broj karaktera je 6)
i oceni sa tog ispita (ocene su u intervalu od 6 do 10).

Prebaciti podatke iz tekstualne datoteke studenti.txt u jednostruko spregnutu listu.
                                                                    (20 poena)

Sortirati ovu listu (u opadajucem redosledu prema prosecnoj oceni)
i prikazati njen sadrzaj.                                           (15 poena)


Prikaz jedne linije ulazne datoteke:

Marko Markovic;0001/2016;P1;8;OIKT;9;UIS;6;

*/

    // Moramo prvo formirati cvor liste i onda njega ubaciti na kraj liste

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>      // zbog atoi()

#define SIZE_IME 51         // za jedan vise zbog '\0' na kraju stringa
#define SIZE_BRINDEKSA 10   // za jedan vise zbog '\0' na kraju stringa
#define SIZE_SIFRAISPITA 7  // za jedan vise zbog '\0' na kraju stringa
#define MAX_BRISPITA 50     // 50 ispita tokom studiranja
#define MAX_SIZETEKST 600   // SifraIspita + ; + ocena = 8 + ; + 2 = 11+1=12 . 12 * 50 = 600

typedef struct student STUDENT;

struct student{
    char ImeIPrezime[SIZE_IME];
    char BrojIndeksa[SIZE_BRINDEKSA];
    char SifraIspita[MAX_BRISPITA][SIZE_SIFRAISPITA];
    int ocena[MAX_BRISPITA];
    float ProsecnaOcena;        // ovo je dodato u listu zbog sortiranja po prosecnoj oceni
    int BrojPolozenihIspita;    // takodje
};


typedef struct cvor CVOR;
typedef CVOR* PCVOR;
struct cvor{
    STUDENT podatak;
    PCVOR sledeci;
};



// Prikazuje sadrzaj cvorova liste glava
// Argument glava nije pointer jer ova funkcija ne menja glavu.
// Funkcija zavisi od sadrzaja liste glava.
void prikazi_listu(char *tekst, PCVOR glava)
{
    int i;

    printf("\n%s\n",tekst);
// Ne mora se pitati if(glava == NULL) vec se odmah moze krenuti sa while(glava).
// Tada prazna lista jednostavno nece imati prikazane elemente.
    if(glava == NULL) {
        printf(" prazna. \n");
    } else {
         while(glava) {

            printf("\n | %-14s %10s ",
                glava->podatak.ImeIPrezime,
                glava->podatak.BrojIndeksa );

            for( i=0; i < glava->podatak.BrojPolozenihIspita; i++ ){

                printf(" %4s %2d ",
                glava->podatak.SifraIspita[i],
                glava->podatak.ocena[i]);
            }

            printf(" %5.2f %2d | \n",
                glava->podatak.ProsecnaOcena,
                glava->podatak.BrojPolozenihIspita );

            glava = glava->sledeci;
        }
    }

    printf("\n");
}


// Na kraj liste glava dodaje vec napravljeni novi cvor
// Funkcija ne zavisi od sadrzaja liste glava.
void dodaj_na_kraj (PCVOR *glava, PCVOR *rep, PCVOR novi)
{                               // dodaje se na kraj liste, pa
    novi->sledeci == NULL;      // za svaki slucaj, ako ranije nije napisano

    if(*glava == NULL) {     // ako je lista prazna
        *rep = novi;            // i rep i glava su taj novi cvor
        *glava = novi;
    } else {                // ako lista nije prazna
        (*rep)->sledeci = novi;  // sledeci od starog repa je taj novi
        *rep = novi;            // novi rep je taj novi
    }
}


// Sortira listu glava u opadajucem redosledu po float prosecna_ocena
// Funkcija zavisi od sadrzaja liste glava.
void sortiraj_listu_po_float_prosecna_ocena_opadajuce( PCVOR *glava )
{
    PCVOR i, j;                     // pomocne liste i i j
    int mem_int;                    // memorija za pamcenje integer-a
    float mem_float;                // memorija za pamcenje float-a
    char mem_string[SIZE_IME];      // memorija za pamcenje string-a
    int k;                          // brojac for petlje

    for ( i=*glava; i; i=i->sledeci )           // i ide od glave do repa
        for ( j=*glava; j; j=j->sledeci )       // j ide od glave do repa

            if ( i->podatak.ProsecnaOcena > j->podatak.ProsecnaOcena ) {  // KRITERIJUM SORTIRANJA

                // medjusobno zamenjujemo mesta svim podacima (sadrzajima cvorova i i j)
                strcpy(mem_string, i->podatak.ImeIPrezime); // sa stringovima mora strcpy()
                strcpy(i->podatak.ImeIPrezime, j->podatak.ImeIPrezime);
                strcpy(j->podatak.ImeIPrezime, mem_string);

                strcpy(mem_string, i->podatak.BrojIndeksa); // sa stringovima mora strcpy()
                strcpy(i->podatak.BrojIndeksa, j->podatak.BrojIndeksa);
                strcpy(j->podatak.BrojIndeksa, mem_string);

                for( k=0; k < (*glava)->podatak.BrojPolozenihIspita; k++ ){

                    strcpy(mem_string, i->podatak.SifraIspita[k]); // sa stringovima mora strcpy()
                    strcpy(i->podatak.SifraIspita[k], j->podatak.SifraIspita[k]);
                    strcpy(j->podatak.SifraIspita[k], mem_string);

                    mem_int = i->podatak.ocena[k];
                    i->podatak.ocena[k] = j->podatak.ocena[k];
                    j->podatak.ocena[k] = mem_int;
                }

                mem_float = i->podatak.ProsecnaOcena;
                i->podatak.ProsecnaOcena = j->podatak.ProsecnaOcena;
                j->podatak.ProsecnaOcena = mem_float;

                mem_int = i->podatak.BrojPolozenihIspita;
                i->podatak.BrojPolozenihIspita = j->podatak.BrojPolozenihIspita;
                j->podatak.BrojPolozenihIspita = mem_int;

            }
}



/*
Sortiranje liste u opadajuci redosled nezavisno od sadrzaja cvorova:
Da bismo sortirali listu p, prvo prolazimo kroz nju kako bismo pronasli
element sa najvecom vrednoscu podatka.
Nakon toga uklanjamo taj element iz liste p
i dodajemo ga na pocetak druge liste nova_glava, koja je inicijalno bila prazna.
Ovaj proces ponavljamo sve dok pocetna lista p ne postane prazna.
Na kraju p postaje pokazivac na listu nova_glava u koju su prebaceni svi elementi.
*/
// Sortira listu u opadajucem redosledu nezavisno od sadrzaja cvorova
// Funkcija ne zavisi od sadrzaja liste glava, osim u bloku KRITERIJUM SORTIRANJA !!!
void SortirajUOpadajuciRedosled( PCVOR *glava )
{
    PCVOR tekuci, tek_sledeci, max, prethodni, nova_glava;

    nova_glava = NULL;

    while( *glava ){      // do kraja liste
        prethodni = NULL;
        max  =  *glava;     // pretpostavljamo da je najveci bas prvi element (glava)
        tekuci =  *glava;
        tek_sledeci = (*glava)->sledeci;

        while ( tek_sledeci ){          // KRITERIJUM SORTIRANJA    // novi->podatak.ProsecnaOcena
            if( max->podatak.ProsecnaOcena < tek_sledeci->podatak.ProsecnaOcena ){
                max = tek_sledeci;  // max postaje tek_sledeci
                prethodni = tekuci;
            }

            tekuci = tek_sledeci;
            tek_sledeci = tek_sledeci->sledeci;
        }

        if( prethodni == NULL )
            *glava = max->sledeci;
        else
            prethodni->sledeci = max->sledeci;

        max->sledeci = NULL;

        // dodaj max na kraj nova_glava (bez repa)
        if( nova_glava == NULL )
            nova_glava = max; // premesta element sa najvecom vrednoscu podatka iz liste p na pocetak liste nova_glava
        else {
            tekuci = nova_glava;
                    // prolazi kroz listu nova_glava da bi pronasao njen poslednji element
            while( tekuci->sledeci )
                tekuci = tekuci->sledeci;
                    // premesta element sa najvecom vrednoscu podatka iz liste p na kraj liste nova_glava
            tekuci->sledeci = max;
        }
    }
    // glava postaje nova_glava
    *glava = nova_glava; // Na kraju p postaje pokazivac na listu nova_glava u koju su prebaceni svi elementi.
}




int main(void)
{
    FILE *fp;
    PCVOR novi=NULL;
    PCVOR glava=NULL;
    PCVOR rep=NULL;
    int i, j, duzina;                   // j je indeks za tekst2
    int ZbirOcenaJednogStudenta;
    char tekst[MAX_SIZETEKST];
    char tekst2[SIZE_SIFRAISPITA];
    char ch;

    fp=fopen("studenti.txt","r");

    if(fp == NULL){
        printf("\n Greska pri otvaranju ulaznog fajla. \n");
        exit(1);
    }

    // Marko Markovic;0001/2016;P1;8;OIKT;9;UIS;6;
    // Od pocetka do kraja fajla ucitavamo jedan po jedan podatak i smestamo ga u listu
    while( 1 ) {    // beskonacna petlja koja se prekida sa break;

        novi = (PCVOR)malloc(sizeof(CVOR)); // rezervisemo memoriju za cvor novi
        if(novi == NULL) {
            printf("\n Greska pri rezervisanju memorije! \n");
            exit(1);
        }

        ZbirOcenaJednogStudenta=0;               // resetujemo ZbirOcenaJednogStudenta

        // Iz svakog reda ucitavamo redom podatke i smestamo ih u cvor novi
        // format za ucitavanje "%[^;]" znaci da ucitavamo dok ne naidjemo na tackazarez  !!!
        //                      ZAPAMTI !!!
    // Ucitavamo imeiprezime uz proveru da li smo stigli do kraja fajla
        if( fscanf(fp,"%[^;]", novi->podatak.ImeIPrezime) == EOF )
            break;
        //printf("\n|%s|\n",novi->podatak.ImeIPrezime);

        // Stao je na tackazarez i sada preskacemo tackazarez
        ch = fgetc(fp);

    // Ucitavamo broj indeksa uz proveru da li smo stigli do kraja fajla
        if( fscanf(fp,"%[^;]", novi->podatak.BrojIndeksa) == EOF )
            break;
        //printf("\n|%s|\n",novi->podatak.BrojIndeksa);

        // Stao je na tackazarez i sada preskacemo tackazarez
        ch = fgetc(fp);

        // Mi jos ne znamo broj polozenih predmeta (promenljiv je) i moramo da ga odredimo.
        // Do sada smo ucitali ImeIPrezime i BrojIndeksa.
        // Sada cemo sa fgets() ucitati ostatak reda do kraja reda u tekst i analizirati ga.

        fgets(tekst,MAX_SIZETEKST,fp);

        duzina = strlen(tekst);

        //printf("\n duzina = %d \n",duzina);

        // eliminisemo '\n' na kraju tekst jer je fgets() ucitao i '\n'
        if( tekst[duzina-1] == '\n' )
            tekst[duzina-1]='\0';

        //printf("\n tekst = |%s|\n",tekst);

        // Sada analiziramo tekst, tj. vadimo parove SifraIspita i ocena, do kraja stringa tekst.
        // Na kraju stringa tekst zbog fgets() je '\n' i '\0'.
        i=0;        // resetujemo indeks stringa tekst[i]
        //j=0;        // resetujemo indeks stringa novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]
        novi->podatak.BrojPolozenihIspita=0;  // resetujemo brojac polozenih ispita
        while( tekst[i] != '\n' && tekst[i] != '\0' ){

            // Stao je na tackazarez i sada preskacemo tackazarez
            if( tekst[i] == ';' ){   // preskacemo tackazarez
                i++;
//                printf("\n Preskocena tackazarez ! \n ");
            }

        // sada ucitavamo sifru ispita do sledece tackazarez ili do kraja stringa tekst
            j=0;        // resetujemo indeks stringa novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]
            while( tekst[i] != ';' && tekst[i] != '\n' && tekst[i] != '\0' ){
                novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][j++] = tekst[i++];
            }

            // zavrsavamo string novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]
            novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][j]='\0';

//printf("\n A novi->podatak.SifraIspita = |%s|\n",novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]);
//system("PAUSE");

            // eliminisemo '\n' na kraju tekst jer je fgets() ucitao i '\n'
            if( novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][j] == '\n' )
                novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita][--j]='\0';

            //printf("\n novi->podatak.SifraIspita = |%s|\n",novi->podatak.SifraIspita[novi->podatak.BrojPolozenihIspita]);

            // Stao je na tackazarez i sada preskacemo tackazarez
            if( tekst[i] == ';' ){   // preskacemo tackazarez
//                printf("\n pre tacka zarez, tekst[] = |%s|\n",(tekst + i));
                i++;
//                printf("\n Preskocena tackazarez ! \n ");
//                printf("\n posle tacka zarez, tekst[] = |%s|\n",(tekst + i));
            }

        // Sada ucitavamo ocena. Prvo u tekst2 ucitamo string, a zatim tekst2 pretvorimo u integer
            j=0;        // resetujemo indeks stringa tekst2
            // sada ucitavamo tekst2 do sledece tackazarez ili do kraja stringa tekst
            while( tekst[i] != ';' && tekst[i] != '\n' && tekst[i] != '\0' ){
                tekst2[j++] = tekst[i++];
            }

            tekst2[j]='\0';     // zavrsavamo string tekst2

            // eliminisemo '\n' na kraju tekst2 jer je fgets() ucitao i '\n'
            if( tekst2[j] == '\n' )
                tekst2[--j]='\0';

            //printf("\n tekst2 = |%s|\n",tekst2);
            // punimo ocena[] koja je integer
            novi->podatak.ocena[novi->podatak.BrojPolozenihIspita] = atoi(tekst2);

            ZbirOcenaJednogStudenta += novi->podatak.ocena[novi->podatak.BrojPolozenihIspita];

            //printf("\n novi->podatak.ocena = %d \n",novi->podatak.ocena[novi->podatak.BrojPolozenihIspita]);

            novi->podatak.BrojPolozenihIspita++;  // brojimo polozene predmete

            //printf("\n novi->podatak.BrojPolozenihIspita = %d \n",novi->podatak.BrojPolozenihIspita );
        }

        novi->podatak.BrojPolozenihIspita--;  // smanjujemo ga jer je poslednji put nepotrebno uvecan

//        printf("\n AAAA novi->podatak.BrojPolozenihIspita = %d \n",novi->podatak.BrojPolozenihIspita );

        // Sada konacno imamo broj polozenih ispita za tog jednog studenta.
        // Racunamo prosecnu ocenu tog jednog studenta:
        novi->podatak.ProsecnaOcena = (float)ZbirOcenaJednogStudenta / (float)novi->podatak.BrojPolozenihIspita;

        novi->sledeci=NULL;     // Obavezno jer je to novi cvor.

        dodaj_na_kraj(&glava,&rep,novi);
    }

    prikazi_listu(" Lista je: ",glava);

    //SortirajUOpadajuciRedosled(&glava);
    sortiraj_listu_po_float_prosecna_ocena_opadajuce(&glava);

    prikazi_listu(" Sortirana lista je: ",glava);


    fclose(fp);

    return 0;
}