import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; /** * Pet Care Scheduler * * @author John Ahlroos / 2025 * * Allows the user to manage Pet care appointments */ public class PetCareScheduler { private final List pets; private final String dataFileName; private final Scanner scanner; public PetCareScheduler(Scanner scanner, String dataFileName) { this.dataFileName = dataFileName; this.scanner = scanner; this.pets = new ArrayList<>(); } public static void main(String[] args) { System.out.println("PET CARE SCHEDULER v1.0"); System.out.println("================================================================="); System.out.println(); try (Scanner scanner = new Scanner(System.in)) { PetCareScheduler scheduler = new PetCareScheduler(scanner, "data.txt"); boolean loaded = scheduler.loadPetsFromFile(); if (!loaded) { System.err.println("Failed to load data file."); return; } System.out.println(); boolean exited = false; while (!exited) { System.out.println("Actions:"); System.out.println("\t1: Register Pet"); System.out.println("\t2: Schedule Appointment"); System.out.println("\t3: List Pets"); System.out.println("\t4: Print Report"); System.out.println("\t5: Save & Exit"); System.out.println(); System.out.print("Action: "); try { int action = Integer.parseInt(scanner.nextLine()); switch (action) { case 1: scheduler.registerPet(); break; case 2: scheduler.addAppointment(); break; case 3: scheduler.list(); break; case 4: scheduler.report(); break; case 5: boolean saved = scheduler.savePetsToFile(); if (!saved) { System.err.println("Failed to save data file! Latest changes were lost."); System.out.print("Exit anyway (yes/no)?"); String option = scanner.nextLine(); exited = option.equals("yes"); } else { exited = true; } break; default: System.out.println("Not a valid option!"); } } catch (NumberFormatException nfe) { System.err.println("Action must be a number!"); } } } catch (RuntimeException re) { System.err.println(re.getMessage()); } } /** * Register a new pet with the system */ public void registerPet() { System.out.println("================================================================="); String id = UUID.randomUUID().toString(); LocalDate registration = LocalDate.now(); System.out.print("Name: "); String name = scanner.nextLine(); if (name.isBlank()) { System.err.println("Name must not be blank!"); return; } System.out.print("Owner: "); String owner = scanner.nextLine(); if (owner.isBlank()) { System.err.println("Owner must not be blank!"); return; } System.out.print("Contact Information: "); String contact = scanner.nextLine(); System.out.print("Breed: "); String breed = scanner.nextLine(); System.out.print("Age: "); int age; try{ age = Integer.parseInt(scanner.nextLine()); } catch (NumberFormatException nfe) { System.err.println("Not a valid number!"); return; } Pet pet = new Pet(id,name,owner,contact,breed,age,registration); pets.add(pet); System.out.printf("New pet %s registered with id %s%n", pet.getName(), pet.getId()); System.out.println("================================================================="); System.out.println(); } /** * Add a new appointment for a pet */ public void addAppointment() { System.out.println("================================================================="); System.out.print("Pet ID: "); String petId = scanner.nextLine(); Optional pet = pets.stream().filter(p -> p.getId().equals(petId)).findFirst(); if (pet.isEmpty()) { System.err.printf("No Pet with id %s%n", petId); return; } System.out.printf("Type (%s): ", Arrays.stream(Appointment.Type.values()).map(Enum::name).collect(Collectors.joining("|"))); String typeStr = scanner.nextLine().toUpperCase(); if (Arrays.stream(Appointment.Type.values()).map(Enum::name).noneMatch(t -> t.equals(typeStr))) { System.err.printf("%s is not a valid appointment type%n", typeStr); return; } System.out.print("Date (dd/mm/yyyy): "); String dateStr = scanner.nextLine(); LocalDate date; try { date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("dd/MM/yyyy")); } catch (DateTimeParseException dtpe) { System.err.printf("%s is not a valid date%n", dateStr); return; } System.out.print("Time (hh:mm): "); String timeStr = scanner.nextLine(); LocalTime time; try { time = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm")); } catch (DateTimeParseException dtpe) { System.err.printf("%s is not a valid time%n", timeStr); return; } System.out.print("Notes: "); String notes = scanner.nextLine(); Appointment appointment = new Appointment(Appointment.Type.valueOf(typeStr), date, time, notes); pet.get().addAppointment(appointment); System.out.printf("Appointment added for pet %s at %s%n", pet.get().getName(), appointment.getDate().atTime(appointment.getTime())); System.out.println("================================================================="); System.out.println(); } /** * List all pets in the system */ public void list() { System.out.println("================================================================="); System.out.println(); for (Pet pet: pets) { System.out.printf("Id:\t\t\t%s%n", pet.getId()); System.out.printf("Name:\t\t%s%n", pet.getName()); System.out.printf("Owner:\t\t%s%n", pet.getOwner()); System.out.printf("Contact:\t%s%n", pet.getContactInformation()); System.out.printf("Breed:\t\t%s%n", pet.getBreed()); System.out.printf("Age:\t\t%d%n", pet.getAge()); System.out.printf("Registered:\t%s%n", pet.getRegistrationDate()); System.out.println("Upcoming Appointments:"); for (Appointment appointment : pet.getAppointments()) { if (appointment.getDate().atTime(appointment.getTime()).isAfter(LocalDateTime.now())) { System.out.printf("\t- %s %s %s%n", appointment.getDate().atTime(appointment.getTime()), appointment.getType(), appointment.getNotes()); } } System.out.println("Past Appointments:"); for (Appointment appointment : pet.getAppointments()) { if (appointment.getDate().atTime(appointment.getTime()).isBefore(LocalDateTime.now())) { System.out.printf("\t- %s %s %s%n", appointment.getDate().atTime(appointment.getTime()), appointment.getType(), appointment.getNotes()); } } System.out.println(); } System.out.println("================================================================="); System.out.println(); } /** * Generate a report of future and past appointments */ public void report() { System.out.println("================================================================="); System.out.println("Next Week: "); LocalDateTime nextMonday = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.MONDAY)).atTime(LocalTime.MIDNIGHT); LocalDateTime nextSunday = nextMonday.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); for (Pet pet : pets) { for (Appointment appointment : pet.getAppointments()) { LocalDateTime dt = appointment.getDate().atTime(appointment.getTime()); if (dt.isAfter(nextMonday) && dt.isBefore(nextSunday)) { System.out.printf("%s: %s %s %s%n", pet.getId(), dt, pet.getName(), pet.getOwner()); } } } System.out.println(); System.out.println("Due Visits: "); LocalDateTime dueDate = LocalDate.now().minusMonths(6).atTime(LocalTime.MIDNIGHT); for (Pet pet : pets) { for (Appointment appointment : pet.getAppointments()) { LocalDateTime dt = appointment.getDate().atTime(appointment.getTime()); if (dt.isBefore(dueDate)) { System.out.printf("%s: %s %s %s%n", pet.getId(), dt, pet.getName(), pet.getOwner()); } } } System.out.println("================================================================="); System.out.println(); } /** * Load pets from file */ private boolean loadPetsFromFile() { File dataFile = new File(".", dataFileName); if (!dataFile.exists()) { System.out.println("No data file exists, loading empty pet database"); return true; } try (BufferedReader reader = Files.newBufferedReader(dataFile.toPath())) { String line; while((line = reader.readLine()) != null) { StringTokenizer petDeserializer = new StringTokenizer(line, "|"); Pet pet = new Pet( petDeserializer.nextToken(), // id petDeserializer.nextToken(), // name petDeserializer.nextToken(), // owner petDeserializer.nextToken(), // contactInformation petDeserializer.nextToken(), // breed Integer.parseInt(petDeserializer.nextToken()), //age LocalDate.parse(petDeserializer.nextToken(), DateTimeFormatter.ISO_DATE) // registrationDate ); while((line = reader.readLine()) != null) { if (line.isEmpty()) { break; } StringTokenizer appointmentDeserializer = new StringTokenizer(line, "|"); Appointment appointment = new Appointment( Appointment.Type.valueOf(appointmentDeserializer.nextToken()), // type LocalDate.parse(appointmentDeserializer.nextToken(), DateTimeFormatter.ISO_DATE), // date LocalTime.parse(appointmentDeserializer.nextToken(), DateTimeFormatter.ISO_TIME), // time appointmentDeserializer.hasMoreTokens() ? appointmentDeserializer.nextToken() : "" // notes ); pet.addAppointment(appointment); } pets.add(pet); } System.out.printf("%d pets loaded successfully from data file %s.%n", pets.size(), dataFileName); return true; } catch (IOException | RuntimeException e) { System.err.printf("Failed to read data file. %s%n", e.getMessage()); return false; } } /** * Save pets to file */ private boolean savePetsToFile() { if (pets == null || pets.isEmpty()) { System.out.println("No pets to save"); return true; } File dataFile = new File(".", dataFileName); try { Path parent = dataFile.toPath().getParent(); if (parent != null) { Files.createDirectories(parent); } } catch (IOException e) { System.err.println("Failed to create data directory"); return false; } OpenOption[] fileOptions = new OpenOption[]{ StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING }; try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(dataFile.toPath(), fileOptions))) { for (Pet pet : pets) { StringJoiner petSerializer = new StringJoiner("|"); petSerializer.add(Objects.toString(pet.getId(), UUID.randomUUID().toString())); petSerializer.add(Objects.toString(pet.getName()," ")); petSerializer.add(Objects.toString(pet.getOwner(), " ")); petSerializer.add(Objects.toString(pet.getContactInformation(), "None")); petSerializer.add(Objects.toString(pet.getBreed(), "Unknown")); petSerializer.add(String.valueOf(pet.getAge())); petSerializer.add(Optional.ofNullable(pet.getRegistrationDate()).orElse(LocalDate.now()).format(DateTimeFormatter.ISO_DATE)); writer.println(petSerializer); for (Appointment appointment : pet.getAppointments()) { StringJoiner appointmentSerializer = new StringJoiner("|"); appointmentSerializer.add(appointment.getType().name()); appointmentSerializer.add(appointment.getDate().format(DateTimeFormatter.ISO_DATE)); appointmentSerializer.add(appointment.getTime().format(DateTimeFormatter.ISO_TIME)); appointmentSerializer.add(appointment.getNotes()); writer.println(appointmentSerializer); } writer.println(); } System.out.printf("%d pets saved successfully to fata file %s.%n", pets.size(), dataFileName); return true; } catch (IOException | RuntimeException e) { System.err.printf("Failed to write to file. %s%n", e.getMessage()); return false; } } }