/*AT51P1*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>

#define MOD_CHECK 11   /*number used for modulas check digit*/
#define MAX_LINES 60   /*maximum lines per printed page*/
#define HEADER_LINES 6 /*lines printed by header*/

							  /*array of error messages*/
char *ERR_MSG[7] = {"Type code out of bounds\n",
						  "Customer Code Check digit out of bounds\n",
						  "Part Number check digit out of bounds\n",
						  "Invoice or Reciept quantity out of bounds\n",
						  "Customer Address has incorrect format\n",
						  "Customer Balance has incorrect format\n",
						  "Customer Credit Limit has incorrect format\n"
						 };

							  /*function proto-types*/
int Open_Files(void);
void Report_Header(void);
int Check_Lines(int L_Count);
void Read_Sort(void);
void Validate_I_R_REC(struct I_R_RECORD *Check_I_R, int *lines);
void Validate_DELETE_RECORD(struct DELETE_RECORD *Check_Delete, int *lines);
void Validate_CREATION_RECORD(struct CREATION_RECORD *Check_Create, int *lines);
int Check_Cust_Code(char C_Code[]);
int Check_Part_Num(char P_Num[]);
void Close_Files(void);


							  /*structure definitions*/
struct I_R_RECORD { char Rec_Type;
					char Cust_Code[6];
					char Part_Num[7];
					int I_R_Quant;
				   };

struct DELETE_RECORD { char Rec_Type;
					   char Cust_Code[6];
					  };

struct CREATION_RECORD { char Rec_Type;
						 char Cust_Code[6];
						 char Cust_Name[21];
						 char Cust_Address[61];
						 char Cust_Balance[10];
						 char Credit_Limit[9];
						};

							  /*a union of the 3 record structures*/
union Struct_Type { struct I_R_RECORD I_R;
					struct DELETE_RECORD D_R;
					struct CREATION_RECORD C_R;
				   };

FILE *print;			  /*file pointers for input output and printer files*/
FILE *in_data;
FILE *valid;

void main()
{
	int file_success;

	printf("\n\t\t\t\tZENITH PAINTS\n");
	printf("\t\t\t File Validation Program");
	printf("\n\n\t\t Please ensure the correct disk is in drive 'A'");
	printf("\n\t\t\t  Hit any key to continue\n\n");
	getch();

							  /*check for file opening errors*/
	file_success = Open_Files();

	if( !file_success ) /*exit program if file error */
			exit(1);

	printf("\n\t\t\tProcessing files please wait.......");
	Report_Header();    /*call functions*/
	Read_Sort();
	Close_Files();
	printf("\n\n\t\t\t    Validation complete");
	printf("\n\t\t\t   Hit any key to finish");
	getch();
	printf("\n\n\n\t\t\t     Program Finished");

}

                       /*START OF FUNCTION DEFFINITIONS*/	

int Open_Files(void)
{
	int success = 1, fail = 0;

							  /*open input file and check for errors*/
	if ( (in_data = fopen ( "A:AT51TD.DAT","rt" )) == NULL )
	{
		printf("error opening file for read");
		printf("\nTerminating program");
		return(fail);
	}

							  /*open printer and check for errors*/
	if ( (print = fopen ( "LPT1:","w" )) == NULL )
	{
		printf("error opening printer");
		printf("\nTerminating program");
		return(fail);
	}



							  /*open file for valid records and check for errors*/
	if ( (valid = fopen ( "A:AT51VF.DAT","w+t" )) == NULL )
	{
		printf("error creating file for write");
		printf("\nTerminating program");
		return(fail);
	}
	return(success);    /* to calling function*/
}                      /*end of open_files*/


void Report_Header(void)
{
	time_t t;           /*use of time functions to get current date */
	struct tm *tp;      /*from system negates need for any user input*/
	int d,m,y;
	t = time(NULL);
	tp = localtime(&t);
	d = tp->tm_mday;
	m = tp->tm_mon;
	y = tp->tm_year;
	static int page = 1;

							  /*print heading*/
	fprintf(print,"                                        ");
	fprintf(print,"  ZENITH PAINTS\n");
	fprintf(print,"                                        ");
	fprintf(print,"                                        ");
	fprintf(print,"   Report Date: %02d/%02d/%02d\n", d, m+1, y-100);
	fprintf(print,"                              ");
	fprintf(print,"   FILE VALIDATION ERROR REPORT");
	fprintf(print,"                      ");
	fprintf(print,"   Page             %d",page);
	fprintf(print,"\n\n");

	page++;             /*increment page counter*/

	return;
}


int Check_Lines(int L_Count)
{                      /*if lines exceed MAX_LINES print new heading*/
	if (L_Count >= MAX_LINES)
	{
		fprintf(print,"\f");
		Report_Header();
		L_Count = HEADER_LINES;
		return(L_Count);
	}
							  /*if not return count*/
	return(L_Count);
}


void Read_Sort(void)
{
	union Struct_Type Rec;
	char Temp_Type,Unknown_Code,Dummy;
	char Unknown[103];
	int lines = HEADER_LINES;

							  /*get type code*/
	while ((fscanf(in_data,"%c", &Temp_Type)) != EOF)
	{                   /*upper case type code*/
		toupper(Temp_Type);

		switch ( Temp_Type )
		{                /*check validity and record type of type code*/
			case 'C':Rec.C_R.Rec_Type = Temp_Type;
							  /*get rest of record from file*/
						fgets(Rec.C_R.Cust_Code,6,in_data);
						fgets(Rec.C_R.Cust_Name,21,in_data);
						fgets(Rec.C_R.Cust_Address,61,in_data);
						fgets(Rec.C_R.Cust_Balance,10,in_data);
						fgets(Rec.C_R.Credit_Limit,8,in_data);
							  /*use dummy variable to get rid of '\n' at record end*/
						fscanf(in_data,"%c", &Dummy);
							  /*validate record*/
						Validate_CREATION_RECORD(&Rec.C_R, &lines);
							  /*increment line count*/
						lines = Check_Lines(lines);
						break;
			case 'D':Rec.D_R.Rec_Type = Temp_Type;
							  /*get rest of record from file*/
						fscanf(in_data,"%5s\n", &Rec.D_R.Cust_Code);
							  /*validate record*/
						Validate_DELETE_RECORD(&Rec.D_R, &lines);
						lines = Check_Lines(lines);
						break;
			case 'I' :
			case 'R' :Rec.I_R.Rec_Type = Temp_Type;
							  /*get rest of record from file*/
						 fscanf(in_data,"%5s%6s%4d\n",
											 &Rec.I_R.Cust_Code,
											 &Rec.I_R.Part_Num,
											 &Rec.I_R.I_R_Quant);
							  /*validate record*/
						 Validate_I_R_REC(&Rec.I_R, &lines);
						 lines = Check_Lines(lines);
						 break;
			default  :Unknown_Code = Temp_Type;
							  /*string to hold largest type of record expected*/
						 fgets(Unknown, 103, in_data);
						 fprintf(print,"\n\n%c %s",Unknown_Code, Unknown);
						 fputs(ERR_MSG[0],print);
						 lines+=4;
						 lines = Check_Lines(lines);
						 break;
		}
	 }

		return;
}


void Validate_I_R_REC(struct I_R_RECORD *Check_I_R, int *lines)
{
	int Ok, Struct_Printed = 0, Err_Found=0;


	Ok = Check_Cust_Code(Check_I_R->Cust_Code);
	if (!Ok)
	{                   /*if error then print record*/
		fprintf(print,"\n\n%c %s %s %d\n",Check_I_R->Rec_Type,
												Check_I_R->Cust_Code,
												Check_I_R->Part_Num,
												Check_I_R->I_R_Quant);
							  /*set flag to show record printed*/
		Struct_Printed = 1;
							  /*print error message*/
		fputs(ERR_MSG[1],print);
		(*lines) +=4;      /*increment and check lines*/
		*lines = Check_Lines(*lines);
		Err_Found=1;

	}
							  /*as previous but for different feild*/
	Ok = Check_Part_Num(Check_I_R->Part_Num);
	if (!Ok)
	{
		if (!Struct_Printed)
		{
			fprintf(print,"\n\n%c %s %s %d\n",Check_I_R->Rec_Type,
													Check_I_R->Cust_Code,
													Check_I_R->Part_Num,
													Check_I_R->I_R_Quant);
			Struct_Printed = 1;
			(*lines)+=3;
			*lines = Check_Lines(*lines);
		}

		fputs(ERR_MSG[2],print);

		(*lines)++;
		*lines = Check_Lines(*lines);
		Err_Found=1;
	}

	if (Check_I_R->I_R_Quant > 9999)
	{
		if (!Struct_Printed)
		{
			fprintf(print,"\n\n%c %s %s %d\n",Check_I_R->Rec_Type,
													Check_I_R->Cust_Code,
													Check_I_R->Part_Num,
													Check_I_R->I_R_Quant);
			Struct_Printed = 1;
			(*lines)+=3;
			*lines = Check_Lines(*lines);
		}
		fputs(ERR_MSG[3],print);
		(*lines)++;
		*lines = Check_Lines(*lines);
		Err_Found=1;
	}

	if (!Err_Found)     /*if no errors print to validated file*/
		fprintf(valid,"%c%s%s%d\n",Check_I_R->Rec_Type,
											Check_I_R->Cust_Code,
											Check_I_R->Part_Num,
											Check_I_R->I_R_Quant);

	return;
}


void Validate_DELETE_RECORD(struct DELETE_RECORD *Check_Delete, int *lines)
{                      /*this function uses the same principle as */
							  /*Validate_I_R_REC to test for errors*/
	int ok;

	ok = Check_Cust_Code(Check_Delete->Cust_Code);

	if (!ok)
	{
		fprintf(print,"\n\n%c %s\n",Check_Delete->Rec_Type,
										Check_Delete->Cust_Code);
		fputs(ERR_MSG[1],print);
		(*lines) += 4;
		*lines = Check_Lines(*lines);
		return;
	}

	fprintf(valid,"%c%s\n",Check_Delete->Rec_Type,
								  Check_Delete->Cust_Code);

	return;
}


void Validate_CREATION_RECORD(struct CREATION_RECORD *Check_Create, int *lines)
{                      /*this function uses the same principle as */
							  /*Validate_I_R_REC and Validate_DELETE_RECORD*/
							  /* to test for errors*/
	int ok, Struct_Printed = 0, Error = 0, len, i, count = 0;

	ok = Check_Cust_Code(Check_Create->Cust_Code);

	if (!ok)
	{
		fprintf(print,"\n\n%c %s %s %s %s %s\n",Check_Create->Rec_Type,
														Check_Create->Cust_Code,
														Check_Create->Cust_Name,
														Check_Create->Cust_Address,
														Check_Create->Cust_Balance,
														Check_Create->Credit_Limit);
		fputs(ERR_MSG[1],print);
		Struct_Printed = 1;
		(*lines)+=3;
		*lines = Check_Lines(*lines);
		Error = 1;
	}
							  /*get length of address string*/
	len = strlen(Check_Create->Cust_Address);
							  /*count semi colons to test format*/
	for (i = 0; i<=len;i++)
	{
		if (Check_Create->Cust_Address[i] == ';')
			count++;

	}
	if (count < 4)
	{
		if(!Struct_Printed)
		{
			fprintf(print,"\n\n%c %s %s %s %s %s\n",Check_Create->Rec_Type,
															Check_Create->Cust_Code,
															Check_Create->Cust_Name,
															Check_Create->Cust_Address,
															Check_Create->Cust_Balance,
															Check_Create->Credit_Limit);
			Struct_Printed = 1;
			(*lines)+=3;
			*lines = Check_Lines(*lines);
			Error = 1;
		}

		fputs(ERR_MSG[4],print);
		(*lines)++;
		*lines = Check_Lines(*lines);
		Error = 1;
	}
							  /*get string length*/
	len = strlen(Check_Create->Cust_Balance);
	for (i = 0; i < len; i++)
	{                   /*test for digits*/
		if( !isdigit(Check_Create->Cust_Balance[i]))
		{
			if(!Struct_Printed)
			{
				fprintf(print,"\n\n%c %s %s %s %s %s\n",Check_Create->Rec_Type,
																Check_Create->Cust_Code,
																Check_Create->Cust_Name,
																Check_Create->Cust_Address,
																Check_Create->Cust_Balance,
																Check_Create->Credit_Limit);
				Struct_Printed = 1;
				(*lines)+=3;
				*lines = Check_Lines(*lines);
			}
			fputs(ERR_MSG[5],print);
			(*lines)++;
			*lines = Check_Lines(*lines);
			Error = 1;
			break;


		}

	}
							  /*use same process as testing customer balance*/
	len = strlen(Check_Create->Credit_Limit);
	for (i = 0; i < len; i++)
	{
		if( !isdigit(Check_Create->Credit_Limit[i]))
		{
			if(!Struct_Printed)
			{
				fprintf(print,"\n\n%c %s %s %s %s %s\n",Check_Create->Rec_Type,
																Check_Create->Cust_Code,
																Check_Create->Cust_Name,
																Check_Create->Cust_Address,
																Check_Create->Cust_Balance,
																Check_Create->Credit_Limit);
				Struct_Printed = 1;
				(*lines)+=3;
				*lines = Check_Lines(*lines);
			}
			fputs(ERR_MSG[6],print);
			(*lines)++;
			*lines = Check_Lines(*lines);
			Error = 1;
			break;


		}

	}

	if (!Error)         /*if no errors print to validated file*/
		fprintf(valid,"%c%s%s%s%s%s\n",Check_Create->Rec_Type,
												 Check_Create->Cust_Code,
												 Check_Create->Cust_Name,
												 Check_Create->Cust_Address,
												 Check_Create->Cust_Balance,
												 Check_Create->Credit_Limit);

	return;
}


int Check_Cust_Code(char C_Code[])
{
	int i, sum = 0, Rem, total, Ret_Val = 1;
	int Cust_Weight[] = {5,4,3,2},num;
	char Check_Digit;
							  /*store check digit*/
	Check_Digit = C_Code[4];

							  /*accumulate sums of customer code and weight*/
	for (i = 0; i < 4; i++)
	{                   /*use '-48' to get int value of char*/
		sum+= (C_Code[i]-48) * Cust_Weight[i];
	}
							  /*get remainder useing mod 11*/
	Rem = sum % MOD_CHECK;
							  /*get value that check digit should be*/
	total = MOD_CHECK - Rem;
							  /*get int value of char to use if in 1 - 9*/
	num = atoi(&Check_Digit);
							  /*test check digit*/
	switch (total)
	{

		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9: if ( num != total)
						  Ret_Val = 0;
				  break;
							  /*test for subtraction of 1 or 0*/
		case 10: if ( Check_Digit != '0')
							Ret_Val = 0;
				  break;
		case 11: if ( Check_Digit != 'x' && Check_Digit != 'X')
							Ret_Val = 0;
				  break;
	}

	return(Ret_Val);
}


int Check_Part_Num(char P_Num[])
{                      /*this function uses same principle as Check_Cust_Code*/
	int i,sum = 0,  Rem, total, Ret_Val = 1;
	int Part_Weight[] = {6,5,4,3,2}, num;
	char Check_Digit;

	Check_Digit = P_Num[5];

	for (i = 0; i < 5; i++)
	{
		sum+= (P_Num[i]-48) * Part_Weight[i];
	}

	Rem = sum % MOD_CHECK;
	total = MOD_CHECK - Rem;
	num = atoi(&Check_Digit);

	switch (total)
	{
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9: if ( num != total)
						  Ret_Val = 0;
				  break;
		case 10: if ( Check_Digit != '0')
							Ret_Val = 0;
				  break;
		case 11: if ( Check_Digit != 'x' && Check_Digit != 'X')
							Ret_Val = 0;
				  break;
	}

	return(Ret_Val);
}


void Close_Files(void)
{                      /*close all files*/
	fclose(print);
	fclose(in_data);
	fclose(valid);
}

							  /*END OF FUNCTION DEFFINITIONS*/