#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXCDS 25
#define NAMELENGTH 16 
 
typedef enum { MC_Alternative = 0, MC_ClassicRock, MC_HeavyMetal, MC_Pop } Music_Category;
#define MAXCATEGORY 4
 
typedef struct {
  int minutes;
  int seconds;
} Length_Struct;

typedef struct {
  char group_name[NAMELENGTH];
  char CD_name[NAMELENGTH];
  float cost;
  int num_tracks;
  float rating;
  Length_Struct length;
  int in_category[MAXCATEGORY];
} CD_Struct;

void Read_To_Newline(FILE *stream) {
  while (fgetc(stream) != '\n');
}

void Read_NameString_With_Spaces (char *string) {
  scanf(" %15[^\n]",string);
  Read_To_Newline(stdin);
}

void Menu() {
  printf("Options: I)nsert D)elete printC)ats N)amesort R)atesort P)rintCDs Q)uit\n");
}

void Insert_CD (CD_Struct CDs[], int *num_CDs) {
  int cat;
  char option;
  float f;
  int i1;
  int i2;
  char tempname[NAMELENGTH];

  if (*num_CDs == MAXCDS)
    printf("Unable to insert -- no more space!\n");
  else {
    printf("Group name: ");
    Read_NameString_With_Spaces(tempname);
    strcpy(CDs[*num_CDs].group_name,tempname);
    printf("CD Title: ");
    Read_NameString_With_Spaces(tempname);
    strcpy(CDs[*num_CDs].CD_name,tempname);
    printf("Cost: ");
    scanf("%f",&f);
    CDs[*num_CDs].cost = f;
    printf("#Tracks: ");
    scanf("%d",&i1);
    CDs[*num_CDs].num_tracks = i1;
    printf("Rating: ");
    scanf("%f",&f);
    CDs[*num_CDs].rating = f;
    printf("Length - Minutes:Seconds: ");
    scanf("%d %*c%d",&i1,&i2);
    CDs[*num_CDs].length.minutes = i1;
    CDs[*num_CDs].length.seconds = i2;
    for (cat = 0; cat < MAXCATEGORY; cat++)
      CDs[*num_CDs].in_category[cat] = 0;
    do {
      printf("Category to Add: A)lternative, C)lassicRock, H)eavyMetal, P)op, D)one: ");
      scanf(" %c",&option);
      Read_To_Newline(stdin);
      switch (option) {
        case 'A': case 'a':
          CDs[*num_CDs].in_category[MC_Alternative] = 1;
          break;
        case 'C': case 'c':
          CDs[*num_CDs].in_category[MC_ClassicRock] = 1;
          break;
        case 'H': case 'h':
          CDs[*num_CDs].in_category[MC_HeavyMetal] = 1;
          break;
        case 'P': case 'p':
          CDs[*num_CDs].in_category[MC_Pop] = 1;
          break;
        case 'D': case 'd':
          break;
        default:
          printf("Unknown category starting with %c\n",option);
      }
    } while ((option != 'D') && (option != 'd'));
    printf("Is this information correct (Y or y for yes)? ");
    scanf(" %c",&option);
    Read_To_Newline(stdin);
    if ((option == 'Y') || (option == 'y'))
      *num_CDs = *num_CDs + 1;
  }
}

void Delete_CD (CD_Struct CDs[], int *num_CDs) {
  char dgroup[NAMELENGTH];
  char dtitle[NAMELENGTH];
  int D;
  char option;

  printf("Group Name: ");
  Read_NameString_With_Spaces(dgroup);
  printf("CD Title: ");
  Read_NameString_With_Spaces(dtitle);
  for (D = 0; (D < *num_CDs) && (strcmp(dgroup,CDs[D].group_name) || strcmp(dtitle,CDs[D].CD_name)); D++);
  if (D >= *num_CDs)
    printf("Unable to delete, CD not found!\n");
  else {
    printf("Are you sure you want to delete this CD? ");
    scanf(" %c",&option);
    if ((option == 'Y') || (option == 'y')) {
      if (D < (*num_CDs - 1))
        CDs[D] = CDs[*num_CDs - 1];
      *num_CDs = *num_CDs - 1;
    }
  }
}

void Print_Categories (CD_Struct CDs[], int num_CDs) {
  Music_Category cat;
  int I;
  int count;
  float rate;
  float cost;

  printf("Type         # AvgCost AvgRate\n");
  printf("------------------------------\n");
  for (cat = MC_Alternative; cat <= MC_Pop; cat++) {
    switch (cat) {
      case MC_Alternative: printf("Alternative"); break;
      case MC_ClassicRock: printf("ClassicRock"); break;
      case MC_HeavyMetal : printf("HeavyMetal "); break;
      case MC_Pop        : printf("Pop        "); break;
    }
    count = 0;
    rate = 0.0;
    cost = 0.0;
    for (I = 0; I < num_CDs; I++)
      if (CDs[I].in_category[cat]) {
        count++;
        rate += CDs[I].rating;
        cost += CDs[I].cost;
      }
    if (count == 0)
      printf(" 0    -      -\n");
    else
      printf("%2d%7.2f%7.2f\n",count,(cost / count),(rate / count));
  }
}

void Sort_CDs_By_Name (CD_Struct CDs[], int num_CDs) {
  int minCD, I, J, compareval;
  CD_Struct temp;

  for (I = 0; I < (num_CDs - 1); I++) {
    minCD = I;
    for (J = I+1; J < num_CDs; J++) {
      compareval = strcmp(CDs[J].group_name,CDs[minCD].group_name);
      if ((compareval < 0) || ((compareval == 0) && (strcmp(CDs[J].CD_name,CDs[minCD].CD_name) < 0)))
        minCD = J;
    }
    temp = CDs[I];
    CDs[I] = CDs[minCD];
    CDs[minCD] = temp;
  }
}

void Sort_CDs_By_Rating (CD_Struct CDs[], int num_CDs) {
  int minCD, I, J;
  CD_Struct temp;

  for (I = 0; I < (num_CDs - 1); I++) {
    minCD = I;
    for (J = I+1; J < num_CDs; J++)
      if (CDs[J].rating > CDs[minCD].rating)
        minCD = J;
    temp = CDs[I];
    CDs[I] = CDs[minCD];
    CDs[minCD] = temp;
  }
}

void Print_CD (CD_Struct CD) {
  Music_Category cat;
  int print_comma = 0;

  printf("%-16s%-16s%5.2f%4d%3d:%02d%5.1f ",CD.group_name,CD.CD_name,CD.cost,CD.num_tracks,CD.length.minutes,CD.length.seconds,CD.rating);
  for (cat = MC_Alternative; cat <= MC_Pop; cat++)
    if (CD.in_category[cat]) {
      if (print_comma) printf(", ");
      switch (cat) {
        case MC_Alternative: printf("Alternative"); break;
        case MC_ClassicRock: printf("ClassicRock"); break;
        case MC_HeavyMetal: printf("HeavyMetal"); break;
        case MC_Pop: printf("Pop"); break;
      }
      print_comma = 1;
    }
  printf("\n");
}

void Print_CDs (CD_Struct CDs[], int num_CDs) {
  int I;

  printf("Group           Title           Cost   Tr Lngth Rate Categories\n");
  printf("-------------------------------------------------------------------------------\n");
  for (I = 0; I < num_CDs; I++)
    Print_CD(CDs[I]);
}

void main() {
  int num_CDs = 0;
  CD_Struct CDs[MAXCDS];
  char option;

  do {
    Menu();
    printf("Option: ");
    scanf(" %c",&option);
    Read_To_Newline(stdin);
    switch (option) {
      case 'I': case 'i': Insert_CD(CDs,&num_CDs); break;
      case 'D': case 'd': Delete_CD(CDs,&num_CDs); break;
      case 'C': case 'c': Print_Categories(CDs,num_CDs); break;
      case 'N': case 'n': Sort_CDs_By_Name(CDs,num_CDs); break;
      case 'R': case 'r': Sort_CDs_By_Rating(CDs,num_CDs); break;
      case 'P': case 'p': Print_CDs(CDs,num_CDs); break;
      case 'Q': case 'q': break;
      default:
        printf("Unknown menu option %c!\n",option);
    }
    printf("\n");
  } while ((option != 'Q') && (option != 'q'));
}