/******************************************
 FILTER FUNCTIONS
 ******************************************/

int Check1(struct CassetteStruct *Cassette,struct FilterStruct *Filter) {
     int Found,W;

     Found=0;
     if (Filter->FirstCas[0]) {
        for (W=0; W<10; ++W) {
          if (Filter->FirstCas[W] &&
              Cassette->Number>=Filter->FirstCas[W] &&
              Cassette->Number<=Filter->LastCas[W]) Found=1;
        }
        return Found;
     }
     return 1;
}

int Check2(struct SongStruct *Song,struct SideStruct *Side,struct FilterStruct *Filter) {
     int Found,W;

     Found=0;
     if (Filter->Album[0]) {
        for (W=0; W<10; ++W) {
          if (Filter->Album[W] &&
              wildcmp(Filter->Album[W],Side->Album)) Found=1;
        }
        if (!Found) return 0;
     }
     Found=0;
     if (Filter->Artist[0]) {
        for (W=0; W<10; ++W) {
          if (Filter->Artist[W] &&
             ((Song && Song->Artist && wildcmp(Filter->Artist[W],Song->Artist)) ||
              (Side->Artist && wildcmp(Filter->Artist[W],Side->Artist)))) Found=1;
        }
        if (!Found) return 0;
     }
     Found=0;
     if (Filter->Title[0] && Song) {
        for (W=0; W<10; ++W) {
          if (Filter->Title[W] &&
              wildcmp(Filter->Title[W],Song->Title)) Found=1;
        }
        if (!Found) return 0;
     }
     Found=0;
     if (Filter->MusicType[0]) {
        for (W=0; W<10; ++W) {
          if (Filter->MusicType[W] &&
	     ((Song && (Song->Music & Filter->MusicType[W]) == Filter->MusicType[W]) ||
	      (!Song && (Side->Music & Filter->MusicType[W]) == Filter->MusicType[W]))) Found=1;
        }
        if (!Found) return 0;
     }
     Found=0;
     switch (Filter->Volumes) {
        case 1:if (Side->Volume) Found=1; break;
        case 2:if (!Side->Volume) Found=1; break;
        default:Found=1; break;
     }

     return Found;
}

int CheckSong(struct SongStruct *Song,struct FilterStruct *Filter) {
     if (!Filter) return 1;
     switch (Filter->Marked) {
       case 1:if (!Song->Mark) return 0; break;
       case 2:if (Song->Mark) return 0; break;
     }
     return Check1(Song->Side->Cassette,Filter) && Check2(Song,Song->Side,Filter);
}

int CheckAlbum(struct SideStruct *Album,struct FilterStruct *Filter) {
     struct SongStruct *Song;

     if (!Filter) return 1;
     switch (Filter->Marked) {
       case 1:if (!Album->Cassette->Mark) return 0; break;
       case 2:if (Album->Cassette->Mark) return 0; break;
     }
     if (Config.SongSearch) {
	Song=Album->FirstSong;
	while (Song && Check2(Song,Album,Filter)) Song=Song->Next;
	return Song ? 1 : 0;
     }
     else
        return Check1(Album->Cassette,Filter) && Check2(NULL,Album,Filter);
}

int CheckCassette(struct CassetteStruct *Cassette,struct FilterStruct *Filter) {
     if (Cassette->SideA && !CheckAlbum(Cassette->SideA,Filter)) return 0;
     if (Cassette->SideB && !CheckAlbum(Cassette->SideB,Filter)) return 0;
     return 1;
}

/******************************************************************/

int ShowCassetteLine(struct CassetteStruct *Cassette) {
	static int Count=0;
	
	if (!Cassette) return Count=0;
	
	printf("%-6s: ",ShowCasNr(Cassette,'\0'));
	if (Cassette->SideA) {
		printf("%s - %s",Cassette->SideA->Collection ? Cassette->SideA->Album : Cassette->SideA->Artist,
		                 Cassette->SideA->Collection ? Cassette->SideA->Volume : Cassette->SideA->Album);
	}
	if (Cassette->SideB) {
		printf("; %s - %s",Cassette->SideB->Collection ? Cassette->SideB->Album : Cassette->SideB->Artist,
		                 Cassette->SideB->Collection ? Cassette->SideB->Volume : Cassette->SideB->Album);
	}
	printf("\n");
	
	if (++Count>20) {
		Count=0;
		printf("\nC - Continue  A - Abort: ");
		rewind(stdin);
		if (toupper(getchar())=='A') {
			Count=0;
			return 1;
		}
		clrscr();
	}
	return 0;
}

int ShowAlbumLine(struct SideStruct *Album) {
	static int Count=0;
	
	if (!Album) return Count=0;
	
	printf("%-6s: ",ShowCasNr(Album->Cassette,Album->Side));
	printf("%s - %s",Album->Collection ? Album->Album : Album->Artist,
	                 Album->Collection ? Album->Volume : Album->Album);
	printf("\n");
	
	if (++Count>20) {
		Count=0;
		printf("\nC - Continue  A - Abort: ");
		rewind(stdin);
		if (toupper(getchar())=='A') {
			Count=0;
			return 1;
		}
		clrscr();
	}
	return 0;
}

int ShowSongLine(struct SongStruct *Song) {
	static int Count=0;
	
	if (!Song) return Count=0;
	
	printf("%-6s: ",ShowCasNr(Song->Side->Cassette,Song->RecSide ? 'A' : 'B'));
	printf("%s - %s\n",Song->Artist ? Song->Artist : Song->Side->Artist,Song->Title);
	
	if (++Count>20) {
		Count=0;
		printf("\nC - Continue  A - Abort: ");
		rewind(stdin);
		if (toupper(getchar())=='A') {
			Count=0;
			return 1;
		}
		clrscr();
	}
	return 0;
}

void ListFiltered(struct FilterStruct *Filter) {
  int Done;
  char Choice;
  struct CassetteStruct *Cassette;
  struct SongStruct *Song;
	
  do {
	BuildScreen("List filtered items");
	printf("C - Show filtered cassettes\n");
	printf("A - Show filtered albums\n");
	printf("S - Show filtered songs\n\n");
	printf("X - Exit\n\n");
	printf("Your choice: ");
	rewind(stdin);
	Choice=toupper(getchar());
	Done=0;
	switch (Choice) {
		case 'C':
			clrscr();
			ShowCassetteLine(NULL);
			Cassette=FirstCassette;
			while (Cassette && !Done) {
			  if (CheckCassette(Cassette,Filter)) Done=ShowCassetteLine(Cassette);
			  Cassette=Cassette->Next;
		        }
			Wait();
			break;
		case 'A':
			clrscr();
			ShowCassetteLine(NULL);
			Cassette=FirstCassette;
			while (Cassette && !Done) {
			  if (!Done && Cassette->SideA && CheckAlbum(Cassette->SideA,Filter)) Done=ShowAlbumLine(Cassette->SideA);
			  if (!Done && Cassette->SideB && CheckAlbum(Cassette->SideB,Filter)) Done=ShowAlbumLine(Cassette->SideB);
			  Cassette=Cassette->Next;
		        }
			Wait();
			break;
		case 'S':
			clrscr();
			ShowSongLine(NULL);
			Cassette=FirstCassette;
			while (Cassette) {
			  if (Cassette->SideA) {
				Song=Cassette->SideA->FirstSong;
				while (Song && !Done) {
     					if (CheckSong(Song,Filter)) Done=ShowSongLine(Song);
					Song=Song->Next;
				}
			  }
			  if (Cassette->SideB) {
				Song=Cassette->SideB->FirstSong;
				while (Song && !Done) {
     					if (CheckSong(Song,Filter)) Done=ShowSongLine(Song);
					Song=Song->Next;
				}
			  }
			  Cassette=Cassette->Next;
		        }
			Wait();
			break;
	}
  } while (Choice!='X');
}

void ListMarked() {
  int Done;
  char Choice;
  struct CassetteStruct *Cassette;
  struct SongStruct *Song;
	
  do {
	BuildScreen("List marked items");
	printf("C - Show marked cassettes\n");
	printf("S - Show marked songs\n\n");
	printf("X - Exit\n\n");
	printf("Your choice: ");
	rewind(stdin);
	Choice=toupper(getchar());
	Done=0;
	switch (Choice) {
		case 'C':
			clrscr();
			ShowCassetteLine(NULL);
			Cassette=FirstCassette;
			while (Cassette && !Done) {
			  if (Cassette->Mark) Done=ShowCassetteLine(Cassette);
			  Cassette=Cassette->Next;
		        }
			Wait();
			break;
		case 'S':
			clrscr();
			ShowSongLine(NULL);
			Cassette=FirstCassette;
			while (Cassette) {
			  if (Cassette->SideA) {
				Song=Cassette->SideA->FirstSong;
				while (Song && !Done) {
     					if (Song->Mark) Done=ShowSongLine(Song);
					Song=Song->Next;
				}
			  }
			  if (Cassette->SideB) {
				Song=Cassette->SideB->FirstSong;
				while (Song && !Done) {
     					if (Song->Mark) Done=ShowSongLine(Song);
					Song=Song->Next;
				}
			  }
			  Cassette=Cassette->Next;
		        }
			Wait();
			break;
	}
  } while (Choice!='X');
}

/********************************************************************/

void FreeFilter(struct FilterStruct *Filter) {
     int W;

     free(Filter->Name);
     for (W=0; W<10; ++W) {
        if (Filter->Artist[W]) free(Filter->Artist[W]);
        if (Filter->Album[W]) free(Filter->Album[W]);
        if (Filter->Title[W]) free(Filter->Title[W]);
     }
     free(Filter);
}

void FilterInfo(struct FilterStruct **Filter) {
     struct CassetteStruct *Cassette=FirstCassette;
     struct SongStruct *Song;
     unsigned Cassettes=0,Songs=0,Albums=0;
     int W;
     char Choice;

     if (!(*Filter)) return;

     BuildScreen("Filter info");
     printf("Filter specifications:\n");
     if ((*Filter)->Artist[0]) {
       printf(" - Selected artists: ");
       for (W=0; W<10; ++W)
         if ((*Filter)->Artist[W]) printf("\"%s\"; ",(*Filter)->Artist[W]);
       printf("\n");
     }
     if ((*Filter)->Album[0]) {
       printf(" - Selected albums: ");
       for (W=0; W<10; ++W)
         if ((*Filter)->Album[W]) printf("\"%s\"; ",(*Filter)->Album[W]);
       printf("\n");
     }
     if ((*Filter)->Title[0]) {
       printf(" - Selected songs: ");
       for (W=0; W<10; ++W)
         if ((*Filter)->Title[W]) printf("\"%s\"; ",(*Filter)->Title[W]);
       printf("\n");
     }
     if ((*Filter)->FirstCas[0]) {
       printf(" - Cassette range: ");
       for (W=0; W<10; ++W)
         if ((*Filter)->FirstCas[W]) printf("%d-%d; ",(*Filter)->FirstCas[W],(*Filter)->LastCas[W]);
       printf("\n");
     }
     if ((*Filter)->MusicType[0]) {
       printf(" - Musictypes: ");
       for (W=0; W<10; ++W)
         if ((*Filter)->MusicType[W]) printf("%s; ",ShowMusic((*Filter)->MusicType[W]));
       printf("\n");
     }
     if ((*Filter)->Volumes==1)
       printf(" - Only volumed albums are included\n");
     if ((*Filter)->Volumes==2)
       printf(" - Volumed albums are excluded\n");
     if ((*Filter)->Marked==1)
       printf(" - Only marked items are included\n");
     if ((*Filter)->Marked==2)
       printf(" - Marked items are excluded\n");

     printf("\n\nScanning database...\r");

     while (Cassette) {
       if (CheckCassette(Cassette,(*Filter))) ++Cassettes;
       if (Cassette->SideA) {
         if (CheckAlbum(Cassette->SideA,(*Filter))) ++Albums;
         Song=Cassette->SideA->FirstSong;
         while (Song) {
           if (CheckSong(Song,(*Filter))) ++Songs;
           Song=Song->Next;
         }
       }
       if (Cassette->SideB) {
         if (CheckAlbum(Cassette->SideB,(*Filter))) ++Albums;
         Song=Cassette->SideB->FirstSong;
         while (Song) {
           if (CheckSong(Song,(*Filter))) ++Songs;
           Song=Song->Next;
         }
       }
       Cassette=Cassette->Next;
     }
     printf(" - %05d Songs selected (out of %d)\n",Songs,SongCount);
     printf(" - %05d Albums selected\n",Albums);
     printf(" - %05d Cassettes selected (out of %d)\n\n",Cassettes,CassetteCount);
     
     do {
	     printf("\nU - Use filter   D - Don't use filter\nL - List selected items\n\n");
	     printf("Your choice: ");
	     rewind(stdin);
	     Choice=toupper(getchar());
	     switch (Choice) {
		case 'D':FreeFilter(*Filter);
			 *Filter=NULL;
			 break;
		case 'L':ListFiltered(*Filter);
		         break;
	     }
	} while (Choice!='D' && Choice!='U');
}

extern struct FlagStruct {
      struct FlagStruct *Next;
      char FlagName[6];
      unsigned Code;
} *FirstFlag;

void DefineFilter(struct FilterStruct **Filter) {
     int W,Done;
     char Buffer[64];
     char Choice;
     struct FlagStruct *TempFlag;

     if (!*Filter) {
       BuildScreen("Define filter");
       *Filter=calloc(1,sizeof (struct FilterStruct));
       printf("Enter filtername (or null for temporary filter): ");
       rewind(stdin);
       gets(Buffer);
       if (!Buffer[0]) strcpy(Buffer,"Temporary filter");
       (*Filter)->Name=malloc(strlen(Buffer)+1);
       strcpy((*Filter)->Name,Buffer);
     }

     do {
        BuildScreen("Define filter options");
        printf("[%c] Select specific artists    [%c] Cassette range\n",(*Filter)->Artist[0] ? 'X' : '.',(*Filter)->FirstCas[0] ? 'X' : '.');
        printf("[%c] Select a certain albums    [%c] Volumes\n",(*Filter)->Album[0] ? 'X' : '.',(*Filter)->Volumes ? 'X' : '.');
        printf("[%c] Select specific songs      [%c] Marked items\n",(*Filter)->Title[0] ? 'X' : '.',(*Filter)->Marked ? 'X' : '.');
        printf("                               [%c] Music types\n",(*Filter)->MusicType[0] ? 'X' : '.');
        revvideo();
        printf(" Filtername: %-66s\n\n",(*Filter)->Name);
        normvideo();
        printf("1 - Specify artists to include\n");
        printf("2 - Select albums to include\n");
        printf("3 - Select specific songs to include\n");
        printf("4 - Define cassette number range\n");
        printf("5 - Specify whether or not to include volumed albums\n");
        printf("6 - Include or exclude marked items\n");
        printf("7 - Define musictypes to include\n");
        printf("0 - Exit, save filter definition\n");
        printf("\nYour choice: ");
        rewind(stdin);
        Choice=getchar();
        switch (Choice) {
           case '1':BuildScreen("Specify included artists");
                    for (W=0; W<10; ++W) if ((*Filter)->Artist[W]) free((*Filter)->Artist[W]);
                    Done=0;
                    for (W=0; W<10; ++W) {
                      if (!Done) {
                         printf("Input filterentry (wildcards are allowed): ");
                         rewind(stdin);
                         gets(Buffer);
                         if (!Buffer[0])
                           Done=1;
                         else {
                           (*Filter)->Artist[W]=malloc(strlen(Buffer+1));
                           strcpy((*Filter)->Artist[W],Buffer);
                         }
                      }
                      if (Done) (*Filter)->Artist[W]=NULL;
                    }
                    break;
           case '2':BuildScreen("Specify included albums");
                    for (W=0; W<10; ++W) if ((*Filter)->Album[W]) free((*Filter)->Album[W]);
                    Done=0;
                    for (W=0; W<10; ++W) {
                      if (!Done) {
                         printf("Input filterentry (wildcards are allowed): ");
                         rewind(stdin);
                         gets(Buffer);
                         if (!Buffer[0])
                           Done=1;
                         else {
                           (*Filter)->Album[W]=malloc(strlen(Buffer+1));
                           strcpy((*Filter)->Album[W],Buffer);
                         }
                      }
                      if (Done) (*Filter)->Album[W]=NULL;
                    }
                    break;
           case '3':BuildScreen("Specify included songs");
                    for (W=0; W<10; ++W) if ((*Filter)->Title[W]) free((*Filter)->Title[W]);
                    Done=0;
                    for (W=0; W<10; ++W) {
                      if (!Done) {
                         printf("Input filterentry (wildcards are allowed): ");
                         rewind(stdin);
                         gets(Buffer);
                         if (!Buffer[0])
                           Done=1;
                         else {
                           (*Filter)->Title[W]=malloc(strlen(Buffer+1));
                           strcpy((*Filter)->Title[W],Buffer);
                         }
                      }
                      if (Done) (*Filter)->Title[W]=NULL;
                    }
                    break;
           case '4':BuildScreen("Define cassette range");
                    printf("Ranges consist of subranges. The format in which a subrange\n");
                    printf("must entered is: lowvalue - highvalue. Enter '.' to exit.\n\n");
                    Done=0;
                    for (W=0; W<10; ++W) {
                      if (!Done) {
                         printf("Enter subrange (LP=1000..1999 CD=2000...2999): ");
                         rewind(stdin);
                         scanf(" %d - %d",&(*Filter)->FirstCas[W],&(*Filter)->LastCas[W]);
                         if (!(*Filter)->FirstCas[W])
                           Done=1;
                      }
                      else
                         (*Filter)->FirstCas[W]=0;
                    }
                    break;
           case '5':BuildScreen("Volumed albums");
                    printf("1 - Only include volumed albums (eg. albums with the volumefield set)\n");
                    printf("2 - Exclude volumed albums\n\n");
                    printf("0 - No filtering on volumed albums\n\n");
                    rewind(stdin);
                    printf("Your choice: ");
                    scanf(" %d",&W);
                    (*Filter)->Volumes=W;
                    break;
           case '6':BuildScreen("Marked items");
                    printf("1 - Only include marked items (eg. songs or cassettes)\n");
                    printf("2 - Exclude marked items\n\n");
                    printf("0 - No filtering on the mark attribute\n\n");
                    rewind(stdin);
                    printf("Your choice: ");
                    scanf(" %d",&W);
                    (*Filter)->Marked=W;
                    break;
           case '7':BuildScreen("Define music types");
                    printf("Enter musictype flags separated by single spaces.\n\n");
                    Done=0;
                    for (W=0; W<10; ++W) {
                      if (!Done) {
                         printf("Enter flags (eg. SLW BLD or INS SYN DIS): ");
                         rewind(stdin);
                         gets(Buffer);
                         (*Filter)->MusicType[W]=0L;
                         while (Buffer[0]) {
                           TempFlag=FirstFlag;
                           while (TempFlag) {
                             if (!strnicmp(TempFlag->FlagName+1,Buffer,3)) {
                               (*Filter)->MusicType[W]|=1L<<TempFlag->Code;
                               break;
                             }
                             TempFlag=TempFlag->Next;
                           }
                           if (!TempFlag) Error(6);
			   if (Buffer[3])
                             strcpy(Buffer,Buffer+4);
                           else
                             break;
                         }
                         if (!(*Filter)->MusicType[W])
                           Done=1;
                      }
                      else
                         (*Filter)->MusicType[W]=0L;
                    }
                    break;
        }
     } while (Choice!='0');
}

/******************************************************************/
