#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

enum {
    MAXNUM = 400,
    MIDDLE = 20, EXECISE = 30, FINAL = 50, /* 成绩比例 */
    HISTLEN = 60,         /* 最长行的长度（字符数）*/
    SEGLEN = 5,           /* 分段长度 */
    SEGNUM = 100/SEGLEN+1 /* 分段数，根据分段长度自动算出 */
};

/* 公用类型的定义 */
typedef struct {
    unsigned long num;
    char name[20];
    double mid, exe, final, score;
} StuRec;

/* 全局性数据对象的定义 */
StuRec students[MAXNUM];

int getcmd (char *prompt, int c1, int cn);
int next(char s[]);
void getnstr (char prompt[], int lim, char bf[]);

void statistics(int n, StuRec tb[]) {
    int i;
    double s, sum, avr;

    if (n <= 1) {
        printf("Data too few. Statistics stop.\n");
        return;
    }

    for (sum = 0.0, i = 0; i < n; ++i) sum += tb[i].score;
    avr = sum/n;
    for (sum = 0.0, i = 0; i < n; ++i)
        sum += (tb[i].score - avr)*(tb[i].score - avr);
    s = sqrt(sum/(n-1));

    printf("Total students: %d\n", n);
    printf("Average score: %lf\n", avr);
    printf("Standard deviation: %lf\n\n", s);
}

void prtHH(int n) {
    int i;
    for (i = 0; i < n; ++i) putchar('H');
}

void histogram(int n, StuRec tb[], int high) {
    int i, mx;
    int segs[SEGNUM];

    for (i = 0; i < SEGNUM; ++i) segs[i] = 0; /* 初始化 */
    for (i = 0; i < n; ++i) /* 统计各分段人数 */
        segs[(int)tb[i].score / SEGLEN]++;

    for (mx = 0, i = 0; i < SEGNUM; ++i) /* 为规范化找最大个数 */
        if (segs[i] > mx) mx = segs[i];
    for (i = 0; i < SEGNUM; ++i) { /* 输出 */
        printf("<%3d: %4d|", (i+1)*SEGLEN, segs[i]);
        prtHH(segs[i]*high/mx);
        putchar('\n');
    }
    putchar('\n');
}

int scrcmp(const void *vp1, const void *vp2) {
    StuRec *p1 = (StuRec*)vp1, *p2 = (StuRec*)vp2;
    return p1->score > p2->score ? 1 :
            p1->score == p2->score ? 0 : -1;
}

int printSRec(FILE *fp, int limit, StuRec tb[]) {
    int i;

    for (i = 0; i < limit; ++i) 
        fprintf(fp, "%-10lu%20s%6.1f%6.1f%6.1f%6.1f\n", tb[i].num,  
            tb[i].name, tb[i].mid, tb[i].exe, tb[i].final, tb[i].score);

    return 0;
}

void sortoutput(int n, StuRec tb[]) {
    char fn[128];
    FILE *fp;

    do { /* 交互式获取文件名 */
        getnstr("File name for saving: ", 128, fn);
        if ((fp = fopen(fn, "w")) == NULL)
            printf("Can't open file: %s\n", fn);
        else {
            qsort(tb, n, sizeof(StuRec), scrcmp);
            printSRec(fp, n, tb);
            fclose(fp);
            break;
        }
    } while (next("Really want to save"));
}

static int check(double x) {
    return x >= 0.0 && x <= 100.0;
}

static int readrec(FILE* fp, StuRec *stp) {
    char s[256];

    if (fgets(s, 256, fp) == NULL) return EOF;
    if (sscanf(s, "%lu %s%lf%lf%lf", &stp->num, stp->name,
            &stp->mid, &stp->exe, &stp->final) == 5)
        if (check(stp->mid) && check(stp->exe) && check(stp->final)) {
            stp->score = (stp->mid*MIDDLE + stp->exe*EXECISE +
                          stp->final*FINAL) / 100;
            return 1;
        }

    return 0;
}

int readSRecs(FILE *fp, int limit, StuRec tb[]) {
    int i = 0, line = 1, n;
    double x;

    while (i < limit && (n = readrec(fp, &tb[i])) != EOF) {
        if (n == 0)
            printf("Data error, line %d\n", line);
        else ++i;

        ++line;
    }

    if (i == limit && !feof(fp)) { /* 还有数据 */
        printf("Too many data. Output is not correct.\n");
        return 0;
    }

    return i;
}

void commander (FILE* fp, char *fn) {
    int n, cmd;

    n = readSRecs(fp, MAXNUM, students);
    if (n <= 1) {
        printf("File %s: too few data items.\n", fn);
        return;
    }
    printf("File %s: %d student records read.\n", fn, n);

    do {
        cmd = getcmd("Cmds: 1,Statistics; 2,Histogram; "
                        "3,Sort and store to file\n", 1, 3);
        switch (cmd) {
        case 1: statistics(n, students);
                break;
        case 2: 	histogram(n, students, SEGLEN);
                break;
        case 3:	sortoutput(n, students);
                break;
        }
    } while (next("command"));
}

int main(void) {
    FILE *fp;
    char fn[128];

    do { /* 交互式获取文件名 */
        getnstr("Student record file name: ", 128, fn);
        if ((fp = fopen(fn, "r")) == NULL)
            printf("Can't open file: %s\n", fn);
        else {
            commander(fp, fn);
            fclose(fp);
        }
    } while (next("file"));

    return 0;
}

void getnstr (char prompt[], int lim, char bf[]) {
    int c, i = 0;

    printf("%s", prompt);
    while (i < lim-1 && (c = getchar()) != EOF && !isspace(c))
        bf[i++] = c;
    if (c != '\n')
        while (getchar() != '\n') ; /* 吃掉本行剩余字符 */

    bf[i] = '\0';
}

int next(char s[]) {
    int c;

    printf("Next %s? (y/n): ", s);
    while (isspace(c = getchar())) /* 读下一个非空白字符 */
        ;
    while (getchar() != '\n') ; /* 吃掉本行剩余字符 */

    return (c == 'y' || c == 'Y');
}

int getcmd (char *prompt, int c1, int cn) {
    return 0; /* 需要定义 */ 
} 


