Messaging-App / src / MessagingServer.java
MessagingServer.java
Raw
import java.io.*;
import java.net.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Objects;

/**
 * Purdue CS180 - Summer 22 - Project 5 - MessagingServer
 * <p>
 * This class stores the server-side part of our application.
 * It is in charge of processing user input and returning
 * data to the client. The server can handle multiple clients
 * at a time.
 *
 * @author Jun Shern Lim, Ivan Yang, Shaofeng Yuan
 * @version 31st July 2022
 */


public class MessagingServer implements Runnable {

    public static final Object USER_DETAILS_GATEKEEPER = new Object();
    public static final Object STORES_GATEKEEPER = new Object();
    public static final Object NEW_MSG_GATEKEEPER = new Object();
    public static final Object BLOCK_GATEKEEPER = new Object();
    public static final Object INVISIBLE_GATEKEEPER = new Object();
    public static final ArrayList<Customer> CUSTOMER_GATEKEEPERS = new ArrayList<>();    //use this for gatekeeping
    private Socket socket;      //each instance of the server will have its own socket to connect to its client

    private static ArrayList<Integer> usedCustomerIDs = new ArrayList<>();     //array list of used customerIDs
    private static ArrayList<Integer> usedSellerIDs = new ArrayList<>();       //array list of used sellerIDs

    private Seller seller = null;      //each instance of a server will have its own seller/customer
    private Customer customer = null; //each instance of a server will have its own seller/customer


    //important to initialize the socket!!
    public MessagingServer(Socket socket) {
        this.socket = socket;
    }

    //initializes an array list of customer objects as gatekeepers
    public static void initializeCustomerGatekeepers() throws IOException {
        synchronized (USER_DETAILS_GATEKEEPER) {
            try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                String line = br.readLine();
                while (line != null) {
                    String[] lineSplit = line.split(",");
                    if (lineSplit[3].equals("customer")) {
                        CUSTOMER_GATEKEEPERS.add(new Customer(lineSplit[0], lineSplit[1], lineSplit[2], lineSplit[3]));
                    }
                    line = br.readLine();
                }
            } catch (FileNotFoundException e) {
                //do nothing
            }
        }
    }

    //returns a pointer to the customer object used to sync
    public static Customer getCustomerGatekeeper(int id) {
        for (Customer customer : CUSTOMER_GATEKEEPERS) {
            if (customer.getID() == id) {
                return customer;
            }
        }

        return null;
    }

    public static ArrayList<Integer> getUsedCustomerIDs() {
        return usedCustomerIDs;
    }

    public static void addToUsedCustomerIDs(int id) {
        usedCustomerIDs.add(id);
    }

    public static ArrayList<Integer> getUsedSellerIDs() {
        return usedSellerIDs;
    }

    public static void addToUsedSellerIDs(int id) {
        usedSellerIDs.add(id);
    }

    public static void readUsedIDs() throws IOException {
        synchronized (USER_DETAILS_GATEKEEPER) {
            try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                String line = br.readLine();
                while (line != null) {
                    String[] lineSplit = line.split(",");
                    if (lineSplit[3].equals("seller")) {
                        if (!usedSellerIDs.contains(
                                Character.getNumericValue(lineSplit[4].charAt(lineSplit[4].length() - 1)))) {
                            addToUsedSellerIDs(
                                    Character.getNumericValue(lineSplit[4].charAt(lineSplit[4].length() - 1)));
                        }

                    } else {
                        if (!usedCustomerIDs.contains(
                                Character.getNumericValue(lineSplit[4].charAt(lineSplit[4].length() - 1)))) {
                            addToUsedCustomerIDs(
                                    Character.getNumericValue(lineSplit[4].charAt(lineSplit[4].length() - 1)));
                        }
                    }
                    line = br.readLine();
                }
            } catch (FileNotFoundException e) {
                //don't need to do anything, means no users have been created yet
            }
        }

    }

    public static String getCurrentDateTime() {
        LocalDateTime curDateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-ddTHH:mm:ss");
        return curDateTime.format(formatter);
    }

    public static void main(String[] args) {

        //first create a server socket
        ServerSocket serverSocket;
        try {
            serverSocket = new ServerSocket(4242);

            System.out.printf("Server open. Waiting for connections on %s\n", serverSocket);

            //wait for connections
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.printf("Connection established with %s\n", socket);
                MessagingServer server = new MessagingServer(socket);
                //when thread starts to run, we can find the run method in the MessagingServer class
                //the socket is also already initialized in that class
                new Thread(server).start();
            }
        } catch (IOException e) {
            System.out.println("Could not establish connection with client");
            e.printStackTrace();
            return;
        }


    }

    public void run() {

        //read in all the taken ids
        try {
            readUsedIDs();
            initializeCustomerGatekeepers();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }


        ObjectOutputStream oos;     //to write objects to client
        ObjectInputStream ois;      //to read objects from client
        int choice;      //verifies user's choice for each menu
        try {
            oos = new ObjectOutputStream(socket.getOutputStream());
            ois = new ObjectInputStream(socket.getInputStream());
            oos.flush();
            do {
                try {
                    choice = startPageProcessing(oos, ois);

                    //end the thread if choice = 0;
                    if (choice == 0) {
                        System.out.println("Error in reading user info!");
                        return;
                    } else if (choice == 1) {
                        //start seller menu processing

                        sellerMenuProcessing(oos, ois);


                    } else if (choice == 2) {
                        //start customer menu processing
                        customerMenuProcessing(oos, ois);


                    }


                } catch (EOFException e) {
                    //checks if user closes the app
                    System.out.printf("Connection with %s ended.\n", socket);
                    return;
                } catch (SocketException e) {
                    System.out.printf("Connection with %s ended.\n", socket);
                    return;
                }

            } while (true);


        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }


    }

    //creates the logic for the start page
    //if return 0 -> user info array is null
    //if return 1 -> it's a seller
    //if return 2 -> it's a customer
    //it throws an EOFException
    public int startPageProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        int choice;  //checks what the user does - sign in, sign up or quit
        do {
            //first read in the choice
            choice = ois.readInt();

            //sign in
            if (choice == 0) {
                String[] signInDetails = (String[]) ois.readObject();
                String email = signInDetails[0];
                String password = signInDetails[1];
                User user = new User();

                //checks user's credentials, returns
                //0 - if email is not found
                //1 - if password is incorrect
                //2 - if everything is correct
                int check = user.checkCredentials(email, password);

                if (check == 0) {
                    oos.writeInt(0);
                    oos.flush();
                } else if (check == 1) {
                    oos.writeInt(1);
                    oos.flush();
                } else {
                    String[] userInfo = null;
                    synchronized (USER_DETAILS_GATEKEEPER) {
                        try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                            String line = br.readLine();

                            while (line != null) {
                                if (line.split(",")[1].equals(email)) {
                                    userInfo = line.split(",");
                                    break;
                                }
                                line = br.readLine();
                            }
                        }
                    }
                    oos.writeInt(2);
                    oos.flush();
                    oos.writeObject(userInfo);
                    if (userInfo == null) {
                        return 0;
                    } else if (userInfo[3].equalsIgnoreCase("seller")) {
                        seller = new Seller(userInfo[0], userInfo[1], userInfo[2], userInfo[3]);
                        return 1;
                    } else {
                        customer = new Customer(userInfo[0], userInfo[1], userInfo[2], userInfo[3]);
                        return 2;
                    }
                }
            } else if (choice == 1) {
                boolean navigation = true;
                //sign up
                if (ois.readInt() == 0) {
                    continue;
                }

                String[] signUpDetails = (String[]) ois.readObject();
                String name = signUpDetails[0];
                String email = signUpDetails[1];
                String password = signUpDetails[2];
                String rePass = signUpDetails[3];
                String userType = signUpDetails[4];
                String uniqueID;
                if (userType.equalsIgnoreCase("customer")) {
                    int check;
                    do {
                        check = checkSignUp(name, email, password, rePass);
                        oos.writeInt(check);
                        oos.flush();
                        if (check != 0) {
                            if (ois.readInt() == 0) {
                                navigation = false;
                                break;
                            }
                            signUpDetails = (String[]) ois.readObject();
                            name = signUpDetails[0];
                            email = signUpDetails[1];
                            password = signUpDetails[2];
                            rePass = signUpDetails[3];
                            userType = signUpDetails[4];
                        }
                    } while (check != 0);
                    if (navigation) {
                        customer = new Customer(name, email, password, userType);
                        uniqueID = customer.getCustomerID();
                        synchronized (USER_DETAILS_GATEKEEPER) {
                            try (PrintWriter pw = new PrintWriter(
                                    new FileOutputStream("loginDetails.txt", true))) {
                                pw.printf("%s,%s,%s,%s,%s\n", name, email, password,
                                        userType.toLowerCase(), uniqueID);
                            }
                        }

                        //add to gatekeeper
                        MessagingServer.CUSTOMER_GATEKEEPERS.add(new Customer(name, email, password, userType));

                    }
                } else if (userType.equalsIgnoreCase("seller")) {
                    int check;
                    do {

                        check = checkSignUp(name, email, password, rePass);
                        oos.writeInt(check);
                        oos.flush();
                        if (check != 0) {
                            if (ois.readInt() == 0) {
                                navigation = false;
                                break;
                            }
                            signUpDetails = (String[]) ois.readObject();
                            name = signUpDetails[0];
                            email = signUpDetails[1];
                            password = signUpDetails[2];
                            rePass = signUpDetails[3];
                            userType = signUpDetails[4];
                        }
                    } while (check != 0);
                    if (navigation) {
                        seller = new Seller(name, email, password, userType);
                        uniqueID = seller.getSellerID();
                        synchronized (USER_DETAILS_GATEKEEPER) {
                            try (PrintWriter pw = new PrintWriter(
                                    new FileOutputStream("loginDetails.txt", true))) {
                                pw.printf("%s,%s,%s,%s,%s\n", name, email, password, userType.toLowerCase(), uniqueID);
                            }
                        }
                    }

                }

            }

        } while (true);
    }


    public int checkSignUp(String name, String email, String password, String rePass) {
        try {
            if (User.isNameValid(name) != 0) {
                return (User.isNameValid(name)); // 1 for empty name String, 2 for invalid character in name String
            } else if (!User.isEmailValid(email)) {
                return 3;  // 3 for invalid email format
            } else if (!User.isEmailUnique(email)) {
                return 4; // 4 for occupied email
            } else if (!User.isPasswordValid(password)) {
                return 5; // 5 for invalid password format
            } else if (!password.equals(rePass)) {
                return 6; // 6 for password and re-enter password doesn't match
            } else {
                return 0; // 0 if it passes the test
            }
        } catch (IOException e) {
            return 7; //7 for IO Exception
        }
    }

    //have to update seller info for every menu
    public void updateSellerInfo() throws IOException {
        String[] lineSplit = null;

        synchronized (USER_DETAILS_GATEKEEPER) {
            //updates seller details and stores
            try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                String line = br.readLine();
                while (line != null) {
                    if (line.split(",")[4].equals(seller.getSellerID())) {
                        lineSplit = line.split(",");
                        break;

                    }
                    line = br.readLine();
                }

            } catch (FileNotFoundException e) {
                //don't do anything
            }
        }
        //reconstruct a new seller, this ensures all info is up-to-date
        //stores are also updated here.
        if (lineSplit != null) {
            seller = new Seller(lineSplit[0], lineSplit[1], lineSplit[2], lineSplit[3]);
        } else {
            System.out.println("Error in updateSellerInfo");
        }

    }

    //have to update customer info for every menu
    public void updateCustomerInfo() throws IOException {
        String[] lineSplit = null;
        synchronized (USER_DETAILS_GATEKEEPER) {
            try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                String line = br.readLine();
                while (line != null) {
                    if (line.split(",")[4].equals(customer.getCustomerID())) {
                        lineSplit = line.split(",");
                        break;
                    }
                    line = br.readLine();
                }

            } catch (FileNotFoundException e) {
                //don't do anything
            }
        }

        //reconstruct a new seller, this ensures all info is up-to-date
        //stores are also updated here.
        if (lineSplit != null) {
            customer = new Customer(lineSplit[0], lineSplit[1], lineSplit[2], lineSplit[3]);
        } else {
            System.out.println("Error in updateCustomerInfo");
        }


    }

    public void sellerMenuProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        //1 to enter new message menu processing
        //2 to enter store dashboard menu processing
        //3 to enter most common words menu
        //4 to enter create store menu processing
        //5 to enter view customer menu processing
        //6 to search for customer menu processing
        //7 to enter modify account processing

        do {
            updateSellerInfo();
            oos.writeInt(User.getNewMsgCountTotal(seller.getSellerID())
                         + User.getDisapMsgCountTotal(seller.getSellerID()));
            oos.flush();
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            } else if (choice == 1) {
                newMessageProcessingSeller(oos, ois);
            } else if (choice == 2) {
                statsProcessingSeller(oos, ois);
            } else if (choice == 4) {
                createStoreProcessing(oos, ois);
            } else if (choice == 5) {
                viewCustomerProcessing(oos, ois);
            } else if (choice == 6) {
                searchCustomerProcessing(oos, ois);
            } else if (choice == 7) {
                if (modAccountProcessing(oos, ois) == -1) {
                    break;
                }
            }
        } while (true);

    }

    public void newMessageProcessingSeller(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {

        do {
            //read in an array list of customers
            ArrayList<Customer> customers = readCustomers();
            ArrayList<Store> sellerStores = seller.getStores();
            ArrayList<NewMessage> newMessages = new ArrayList<>();

            for (Store store : sellerStores) {
                for (Customer cust : customers) {
                    int newMsgCount = User.getNewMsgCountPerStore(store.getStoreID(), cust.getCustomerID());
                    if (newMsgCount != 0) {
                        newMessages.add(new NewMessage(cust, store, newMsgCount));
                    }
                }
            }

            String[] sellerNewMessages = new String[newMessages.size()];

            for (int i = 0; i < sellerNewMessages.length; i++) {
                sellerNewMessages[i] = String.format("Store %s with Customer %s : %d new messages",
                        newMessages.get(i).getStore().getStoreName(), newMessages.get(i).getCustomer().getFullName(),
                        newMessages.get(i).getCountOfNewMessages());
            }

            oos.writeObject(sellerNewMessages);
            oos.flush();

            int choice = ois.readInt();
            if (choice == 0) {
                return;
            } else if (choice == -10) {
                continue;
            }

            int newMessageChoice = ois.readInt();
            sellerMessagingMenuProcessing(newMessages.get(newMessageChoice).getCustomer(),
                    newMessages.get(newMessageChoice).getStore(), oos, ois);
        } while (true);
    }

    public void statsProcessingSeller(ObjectOutputStream oos, ObjectInputStream ois) throws IOException {

        do {
            String[] storeNames = new String[seller.getStores().size()];
            for (int i = 0; i < storeNames.length; i++) {
                storeNames[i] = seller.getStores().get(i).getStoreName();
            }
            oos.writeObject(storeNames);
            oos.flush();

            if (ois.readInt() == 0) {
                return;
            }

            int storeChoice = ois.readInt();
            Store storeToUse = seller.getStores().get(storeChoice);

            do {
                //send most common words info
                mostCommonWordsProcessing(oos, storeToUse);

                //get an arraylist of customers
                ArrayList<Customer> customers = new ArrayList<>();
                synchronized (USER_DETAILS_GATEKEEPER) {
                    try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                        String line = br.readLine();
                        while (line != null) {
                            String[] splitLine = line.split(",");
                            if (splitLine[3].equals("customer")) {
                                customers.add(new Customer(splitLine[0], splitLine[1], splitLine[3], splitLine[4]));
                            }
                            line = br.readLine();
                        }
                    }
                }

                ArrayList<Integer> count = storeToUse.getStoreMessageCounts(customers);

                String[] storeStats = new String[count.size()];

                for (int i = 0; i < count.size(); i++) {
                    storeStats[i] = String.format("Store %s with Customer %s (%d messages received)",
                            storeToUse.getStoreName(), customers.get(i).getFullName(), count.get(i));
                }
                oos.writeObject(storeStats);
                oos.flush();
                if (sortingStatsSeller(oos, ois, storeStats, count) == 0) {
                    break;
                }
            } while (true);

        } while (true);

    }

    public int sortingStatsSeller(ObjectOutputStream oos, ObjectInputStream ois,
                                  String[] storeStats, ArrayList<Integer> count) throws IOException {
        do {

            //1 default
            //2 - increasing
            //3 - decreasing
            int choice = ois.readInt();
            if (choice == 0) {
                return 0;
            } else if (choice == -1) {
                return -1;
            } else if (choice == 1) {
                oos.writeObject(storeStats);
                oos.flush();
            } else {
                oos.writeObject(Store.sortStoreStats(storeStats, count, choice));
                oos.flush();
            }

        } while (true);

    }

    public void mostCommonWordsProcessing(ObjectOutputStream oos, Store chosenStore) throws IOException {
        ArrayList<String> words = new ArrayList<>();
        ArrayList<Integer> wordCount = new ArrayList<>();
        ArrayList<Customer> customers = readCustomers();

        for (Customer cust : customers) {
            try (BufferedReader br = new BufferedReader(
                    new FileReader(String.format("%s_%s.txt", chosenStore.getStoreID(), cust.getCustomerID())))) {
                String line = br.readLine();

                while (line != null) {
                    line = br.readLine();
                    String noPuncLine = line.replaceAll("\\p{Punct}", "").toLowerCase();
                    for (String word : noPuncLine.split(" ")) {
                        if (!words.contains(word)) {
                            words.add(word);
                            wordCount.add(1);
                        } else {
                            int indexToAdd = words.indexOf(word);
                            wordCount.set(indexToAdd, wordCount.get(indexToAdd) + 1);
                        }
                    }
                    line = br.readLine();
                }
            } catch (FileNotFoundException e) {
                //don't do anything
            }
            try (BufferedReader br = new BufferedReader(
                    new FileReader(String.format("%s_%s_new.txt", chosenStore.getStoreID(),
                            cust.getCustomerID())))) {
                String line = br.readLine();

                while (line != null) {
                    line = br.readLine();
                    String noPuncLine = line.replaceAll("\\p{Punct}", "").toLowerCase();
                    for (String word : noPuncLine.split(" ")) {
                        if (!words.contains(word)) {
                            words.add(word);
                            wordCount.add(1);
                        } else {
                            int indexToAdd = words.indexOf(word);
                            wordCount.set(indexToAdd, wordCount.get(indexToAdd) + 1);
                        }
                    }
                    line = br.readLine();
                }
            } catch (FileNotFoundException e) {
                //don't do anything
            }
        }


        if (words.size() != wordCount.size()) {
            System.out.println("Error in mostCommonWordsProcessing");
            return;
        }

        if (words.size() == 0) {
            oos.writeObject(new String[0]);
            oos.flush();
            return;
        }

        ArrayList<String> wordCountsToReturn = new ArrayList<>();
        int wordsAdded = 0;
        while (wordsAdded < words.size() || wordsAdded < 10) {
            int maxIndex = findMaxIndex(wordCount);
            wordCountsToReturn.add(words.get(maxIndex));
            wordCountsToReturn.add(String.valueOf(wordCount.get(maxIndex)));
            wordsAdded++;
            words.remove(maxIndex);
            wordCount.remove(maxIndex);
            if (words.size() == 0) {
                break;
            }
        }
        String[] toReturn = new String[wordCountsToReturn.size()];
        wordCountsToReturn.toArray(toReturn);
        oos.writeObject(toReturn);
        oos.flush();
    }

    public int findMaxIndex(ArrayList<Integer> counts) {
        if (counts == null) {
            return -1;
        }
        int max = 0;
        int index = 0;
        for (Integer count : counts) {
            if (count > max) {
                max = count;
                index = counts.indexOf(count);
            }
        }
        return index;
    }

    public int modAccountProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {

        do {
            boolean invalidInfoCheck = false;
            int choice = ois.readInt();
            if (choice == 0) {
                return 0;
            }
            if (choice == -1) {
                if (seller != null) {
                    User.deleteUserAccount(seller.getSellerID());
                    return -1;
                } else {
                    User.deleteUserAccount(customer.getCustomerID());
                    return -1;
                }

            }

            String[] userInfo = (String[]) ois.readObject();
            String name = userInfo[0];
            String email = userInfo[1];
            String password = userInfo[2];
            String rePass = userInfo[3];
            String userID = userInfo[4];

            String nameEmergency = "";
            String emailEmergency = "";
            String passwordEmergency = "";
            String userTypeEmergency = "";

            //first remove user detail from file
            synchronized (USER_DETAILS_GATEKEEPER) {
                String file = "";
                try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                    String line = br.readLine();
                    while (line != null) {
                        if (line.split(",")[4].equals(userID)) {
                            if (userID.contains("SELLER")) {
                                userTypeEmergency = "seller";
                            } else {
                                userTypeEmergency = "customer";
                            }
                            nameEmergency = line.split(",")[0];
                            emailEmergency = line.split(",")[1];
                            passwordEmergency = line.split(",")[2];
                            line = br.readLine();
                            continue;
                        }
                        file += line + "\n";
                        line = br.readLine();
                    }
                }

                try (PrintWriter pw = new PrintWriter(new FileOutputStream("loginDetails.txt"))) {
                    pw.print(file);
                }
            }

            do {
                int check;
                if (invalidInfoCheck) {
                    choice = ois.readInt();
                    if (choice == 0) {
                        try (PrintWriter pw = new PrintWriter(
                                new FileOutputStream("loginDetails.txt", true))) {
                            pw.printf("%s,%s,%s,%s,%s\n", nameEmergency, emailEmergency,
                                    passwordEmergency, userTypeEmergency, userID);
                        }
                        return 0;
                    }
                    if (choice == -1) {
                        if (seller != null) {
                            User.deleteUserAccount(seller.getSellerID());
                            return -1;
                        } else {
                            User.deleteUserAccount(customer.getCustomerID());
                            return -1;
                        }
                    }

                    userInfo = (String[]) ois.readObject();
                    name = userInfo[0];
                    email = userInfo[1];
                    password = userInfo[2];
                    rePass = userInfo[3];
                    userID = userInfo[4];

                    //first remove user detail from file
                    synchronized (USER_DETAILS_GATEKEEPER) {
                        String file = "";
                        try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                            String line = br.readLine();
                            while (line != null) {
                                if (line.contains(userID)) {
                                    line = br.readLine();
                                    continue;
                                }
                                file += line + "\n";
                                line = br.readLine();
                            }
                        }

                        try (PrintWriter pw = new PrintWriter(new FileOutputStream("loginDetails.txt"))) {
                            pw.print(file);
                        }
                    }
                }

                check = checkSignUp(name, email, password, rePass);
                oos.writeInt(check);
                oos.flush();

                if (check != 0) {
                    invalidInfoCheck = true;
                } else {
                    String userType;
                    if (userID.contains("SELLER")) {
                        userType = "seller";
                    } else {
                        userType = "customer";
                    }

                    try (PrintWriter pw = new PrintWriter(new FileOutputStream("loginDetails.txt", true))) {
                        pw.printf("%s,%s,%s,%s,%s\n", name, email, password, userType, userID);
                    }

                    if (userID.contains("SELLER")) {
                        updateSellerInfo();
                    } else {
                        if (!name.equals(customer.getFullName())) {
                            changeMessageFileNamesCustomer(name);
                        }
                        updateCustomerInfo();
                    }
                    break;
                }

            } while (true);

        } while (true);
    }

    public void changeMessageFileNamesCustomer(String newName) throws IOException {
        ArrayList<Store> stores = readStores();
        for (Store store : stores) {
            synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                String file = "";
                try (BufferedReader br = new BufferedReader(new FileReader(
                        String.format("%s_%s.txt", store.getStoreID(), customer.getCustomerID())))) {
                    int lineNumber = 1;
                    String line = br.readLine();
                    while (line != null) {
                        if (lineNumber % 2 != 0 && line.split(",")[0].equals(customer.getFullName())) {
                            String[] lineSplit = line.split(",");
                            lineSplit[0] = newName;
                            file += String.join(",", lineSplit) + "\n";
                            lineNumber++;
                            line = br.readLine();
                            continue;
                        }
                        file += line + "\n";
                        lineNumber++;
                        line = br.readLine();
                    }
                } catch (FileNotFoundException e) {
                    continue;
                }
                try (PrintWriter pw = new PrintWriter(new FileOutputStream(
                        String.format("%s_%s.txt", store.getStoreID(), customer.getCustomerID())))) {
                    pw.print(file);
                }
            }
        }
        for (Store store : stores) {
            synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                String file = "";
                try (BufferedReader br = new BufferedReader(new FileReader(
                        String.format("%s_%s.txt", customer.getCustomerID(), store.getStoreID())))) {
                    int lineNumber = 1;
                    String line = br.readLine();
                    while (line != null) {
                        if (lineNumber % 2 != 0 && line.split(",")[0].equals(customer.getFullName())) {
                            String[] lineSplit = line.split(",");
                            lineSplit[0] = newName;
                            file += String.join(",", lineSplit) + "\n";
                            lineNumber++;
                            line = br.readLine();
                            continue;
                        }
                        file += line + "\n";
                        lineNumber++;
                        line = br.readLine();
                    }
                } catch (FileNotFoundException e) {
                    continue;
                }
                try (PrintWriter pw = new PrintWriter(new FileOutputStream(
                        String.format("%s_%s.txt", customer.getCustomerID(), store.getStoreID())))) {
                    pw.print(file);
                }
            }
        }
        for (Store store : stores) {
            synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                String file = "";
                try (BufferedReader br = new BufferedReader(new FileReader(
                        String.format("%s_%s_new.txt", customer.getCustomerID(), store.getStoreID())))) {
                    int lineNumber = 1;
                    String line = br.readLine();
                    while (line != null) {
                        if (lineNumber % 2 != 0 && line.split(",")[0].equals(customer.getFullName())) {
                            String[] lineSplit = line.split(",");
                            lineSplit[0] = newName;
                            file += String.join(",", lineSplit) + "\n";
                            lineNumber++;
                            line = br.readLine();
                            continue;
                        }
                        file += line + "\n";
                        lineNumber++;
                        line = br.readLine();
                    }
                } catch (FileNotFoundException e) {
                    continue;
                }
                try (PrintWriter pw = new PrintWriter(new FileOutputStream(
                        String.format("%s_%s_new.txt", customer.getCustomerID(), store.getStoreID())))) {
                    pw.print(file);
                }
            }
        }
        for (Store store : stores) {
            synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                String file = "";
                try (BufferedReader br = new BufferedReader(new FileReader(
                        String.format("%s_%s_new.txt", store.getStoreID(), customer.getCustomerID())))) {
                    int lineNumber = 1;
                    String line = br.readLine();
                    while (line != null) {
                        if (lineNumber % 2 != 0 && line.split(",")[0].equals(customer.getFullName())) {
                            String[] lineSplit = line.split(",");
                            lineSplit[0] = newName;
                            file += String.join(",", lineSplit) + "\n";
                            lineNumber++;
                            line = br.readLine();
                            continue;
                        }
                        file += line + "\n";
                        lineNumber++;
                        line = br.readLine();
                    }
                } catch (FileNotFoundException e) {
                    continue;
                }
                try (PrintWriter pw = new PrintWriter(new FileOutputStream(
                        String.format("%s_%s_new.txt", store.getStoreID(), customer.getCustomerID())))) {
                    pw.print(file);
                }
            }
        }


    }

    public void createStoreProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        String[] storeDetails;
        do {
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            }
            storeDetails = (String[]) ois.readObject();

            if (Store.isStoreNameUnique(storeDetails[0])) {
                oos.writeInt(1);
                oos.flush();
                break;
            }
            oos.writeInt(-1);
            oos.flush();
        } while (true);

        new Store(storeDetails[0], storeDetails[1], seller.getSellerID(), seller.getFullName()).writeStoreToFile();
        updateSellerInfo();
    }

    //returns an array list of all customers
    public ArrayList<Customer> readCustomers() throws IOException {
        ArrayList<Customer> customers = new ArrayList<>();
        synchronized (USER_DETAILS_GATEKEEPER) {
            try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                String line = br.readLine();
                while (line != null) {
                    String[] lineSplit = line.split(",");
                    if (lineSplit[3].equals("customer")) {
                        customers.add(new Customer(lineSplit[0], lineSplit[1], lineSplit[2], lineSplit[3]));
                    }
                    line = br.readLine();
                }
            }
        }

        return customers;
    }

    //handles the search for customer feature
    public void searchCustomerProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        do {
            updateSellerInfo();
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            }
            //read search string
            String search = (String) ois.readObject();

            ArrayList<Customer> customerMatches = searchForCustomers(seller, search);
            String[] customerMatchesNames = new String[customerMatches.size()];
            for (int i = 0; i < customerMatchesNames.length; i++) {
                customerMatchesNames[i] = customerMatches.get(i).getFullName();
            }

            String[] storeNames = new String[seller.getStores().size()];
            for (int i = 0; i < storeNames.length; i++) {
                storeNames[i] = seller.getStores().get(i).getStoreName();
            }

//            if (storeNames.length == 0 || customerMatchesNames.length == 0) {
//                continue;
//            }

            do {
                oos.writeObject(customerMatchesNames);
                oos.flush();

                oos.writeObject(storeNames);
                oos.flush();


                choice = ois.readInt();
                if (choice == 0) {
                    break;
                }

                int customerChoice = ois.readInt();
                Customer customerChosen = customerMatches.get(customerChoice);

                int storeChoice = ois.readInt();
                Store chosenStore = seller.getStores().get(storeChoice);

                sellerMessagingMenuProcessing(customerChosen, chosenStore, oos, ois);
            } while (true);


        } while (true);
    }

    // the helper method to search for a customer, returns an array list of customer matches, already checks
    // for invisibility status here
    public ArrayList<Customer> searchForCustomers(Seller myself, String customerName) throws IOException {

        ArrayList<Customer> customerMatches = new ArrayList<>();
        ArrayList<Customer> customers = readCustomers();
        for (Customer cust : customers) {
            if (cust.getFullName().toLowerCase().contains(customerName.toLowerCase())) {
                if (!User.areTheyInvisible(myself.getSellerID(), cust.getCustomerID())) {
                    customerMatches.add(cust);
                }

            }
        }
        return customerMatches;
    }

    //handles the features to view customers
    public void viewCustomerProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        do {
            //1: next
            //0: return
            updateSellerInfo();

            //send customer info
            ArrayList<Customer> customers = readCustomers();
            //send array of customer names to client
            ArrayList<String> customerNames = new ArrayList<>();
            for (Customer cust : customers) {
                customerNames.add(cust.getFullName());
            }
            String[] namesToSend = new String[customerNames.size()];
            customerNames.toArray(namesToSend);
            oos.writeObject(namesToSend);
            oos.flush();
            //send store info
            ArrayList<String> storeNames = new ArrayList<>();
            for (Store store : seller.getStores()) {
                storeNames.add(store.getStoreName());
            }
            String[] storesToSend = new String[storeNames.size()];
            storeNames.toArray(storesToSend);
            oos.writeObject(storesToSend);
            oos.flush();

            int choice = ois.readInt();
            if (choice == 0) {
                return;
            }

            if (storesToSend.length == 0 || namesToSend.length == 0) {
                return;
            }

            int customerChoice = ois.readInt();     //index of customer chosen by the client

            Customer customerChosen = customers.get(customerChoice);

            int storeChoice = ois.readInt();
            Store chosenStore = seller.getStores().get(storeChoice);

            sellerMessagingMenuProcessing(customerChosen, chosenStore, oos, ois);

        } while (true);
    }

    //the messaging menu for sellers
    public void sellerMessagingMenuProcessing(
            Customer cust, Store store, ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {

        do {

            //if they are blocked - send 1, else send 0
            if (User.areTheyBlocked(store.getStoreID(), cust.getCustomerID())) {
                oos.writeInt(1);
                oos.flush();
            } else {
                oos.writeInt(0);
                oos.flush();
            }

            //if I am invisible to them, send 1, else send 0
            if (User.isInvisible(store.getStoreID(), cust.getCustomerID())) {
                oos.writeInt(1);
                oos.flush();
            } else {
                oos.writeInt(0);
                oos.flush();
            }

            oos.writeObject(cust.getFullName());
            oos.flush();
            ArrayList<Message> messages;
            synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                messages = User.getMsg(store.getStoreID(), cust.getCustomerID());
            }
            if (messages.size() == 0) {
                oos.writeObject(new String[0]);
                oos.flush();
            } else {
                ArrayList<String> messageInfo = new ArrayList<>();
                for (int i = 0; i < messages.size(); i++) {
                    if (i == 0 && messages.get(i).getIsNew()) {
                        messageInfo.add("---------------------------------------" +
                                        "NEW MESSAGES BELOW----------------------------------------");
                        messageInfo.add(messages.get(i).toString());
                        continue;
                    }
                    if (i + 1 < messages.size()) {
                        if (!messages.get(i).getIsNew() && messages.get(i + 1).getIsNew()) {
                            messageInfo.add(messages.get(i).toString());
                            messageInfo.add("---------------------------------------" +
                                            "NEW MESSAGES BELOW----------------------------------------");
                            continue;
                        }
                    }

                    messageInfo.add(messages.get(i).toString());
                }
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                    User.flushMsg(store.getStoreID(), cust.getCustomerID());
                }

                String[] messageInfoToSend = new String[messageInfo.size()];
                messageInfo.toArray(messageInfoToSend);
                oos.writeObject(messageInfoToSend);
                oos.flush();
            }


            //message menu choices:
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            } else if (choice == 1) {
                if (User.isBlocked(store.getStoreID(), cust.getCustomerID())) {
                    oos.writeInt(-1);
                    oos.flush();
                    continue;
                } else {
                    oos.writeInt(1);
                    oos.flush();
                }
                //-1 back
                //0 regular
                //1 regular disappearing
                //2 file import regular
                //3 file import disappearing
                int msgType = ois.readInt();

                if (msgType == -1) {
                    continue;
                } else if (msgType == 0) {
                    String messageContent = (String) ois.readObject();
                    //send a regular message

                    synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                        User.sendMsg(new Message(store.getStoreName(), store.getStoreID(),
                                messageContent, LocalDateTime.now().toString(),
                                false, true), cust.getCustomerID());
                    }


                } else if (msgType == 1) {
                    String messageContent = (String) ois.readObject();
                    //send a disappearing message
                    synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                        User.sendMsg(new Message(store.getStoreName(), store.getStoreID(),
                                messageContent, LocalDateTime.now().toString(),
                                true, true), cust.getCustomerID());
                    }


                } else if (msgType == 2) {
                    //send a regular file import
                    String[] fileMessage = (String[]) ois.readObject();
                    if (fileMessage == null) {
                        continue;
                    }
                    for (String line : fileMessage) {
                        synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                            User.sendMsg(new Message(store.getStoreName(), store.getStoreID(),
                                    line, LocalDateTime.now().toString(),
                                    false, true), cust.getCustomerID());
                        }

                    }

                } else if (msgType == 3) {
                    //send a disappearing file import
                    String[] fileMessage = (String[]) ois.readObject();
                    if (fileMessage == null) {
                        continue;
                    }
                    for (String line : fileMessage) {
                        synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                            User.sendMsg(new Message(store.getStoreName(), store.getStoreID(),
                                    line, LocalDateTime.now().toString(),
                                    true, true), cust.getCustomerID());
                        }

                    }

                }


            } else if (choice == 2) {
                //0 - back
                //1 - edit
                Message[] messagesToModify;
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                    messagesToModify = User.messagesToModify(store.getStoreID(), cust.getCustomerID());
                }
                String[] messagesToSend = new String[messagesToModify.length];
                for (int i = 0; i < messagesToSend.length; i++) {
                    messagesToSend[i] = messagesToModify[i].toString();
                }
                oos.writeObject(messagesToSend);
                oos.flush();

                if (messagesToSend.length == 0) {
                    ois.readInt();
                    continue;
                }

                int toEdit = ois.readInt();
                if (toEdit == 0) {
                    continue;
                }
                int messageToEditIndex = ois.readInt();
                Message messageToEdit = messagesToModify[messageToEditIndex];
                String editedMessage = (String) ois.readObject();
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                    User.editMsg(editedMessage, messageToEdit, store.getStoreID(), cust.getCustomerID());
                }


            } else if (choice == 3) {
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                    String fileContents = "";
                    try (BufferedReader br = new BufferedReader(new FileReader(String.format("%s_%s.txt",
                            store.getStoreID(), cust.getCustomerID())))) {
                        String line = br.readLine();
                        while (line != null) {
                            fileContents += line + "\n";
                            line = br.readLine();
                        }

                        if (fileContents.equals("")) {
                            oos.writeObject("");
                            oos.flush();
                            oos.writeObject("");
                            oos.flush();
                        } else {
                            oos.writeObject(String.format("%s_%s.csv", store.getStoreID(), cust.getCustomerID()));
                            oos.flush();
                            oos.writeObject(fileContents);
                            oos.flush();
                        }

                    } catch (FileNotFoundException e) {
                        oos.writeObject("");
                        oos.flush();
                        oos.writeObject("");
                        oos.flush();
                    }
                }

            } else if (choice == 4) {
                //0 - back
                //1 - delete
                Message[] messagesToModify;
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                    messagesToModify = User.messagesToModify(store.getStoreID(), cust.getCustomerID());
                }
                String[] messagesToSend = new String[messagesToModify.length];
                for (int i = 0; i < messagesToSend.length; i++) {
                    messagesToSend[i] = messagesToModify[i].toString();
                }
                oos.writeObject(messagesToSend);
                oos.flush();
                int toDelete = ois.readInt();
                if (toDelete == 0) {
                    continue;
                }
                int messageToDeleteIndex = ois.readInt();
                Message messageToDelete = messagesToModify[messageToDeleteIndex];

                synchronized (Objects.requireNonNull(getCustomerGatekeeper(cust.getID()))) {
                    User.deleteMsg(messageToDelete, store.getStoreID(), cust.getCustomerID());
                }

            } else if (choice == 5) {
                synchronized (BLOCK_GATEKEEPER) {
                    if (User.areTheyBlocked(store.getStoreID(), cust.getCustomerID())) {
                        User.unblockUser(store.getStoreID(), cust.getCustomerID());
                    } else {
                        User.blockUser(store.getStoreID(), cust.getCustomerID());
                    }
                }

            } else if (choice == 6) {
                synchronized (INVISIBLE_GATEKEEPER) {
                    if (User.isInvisible(store.getStoreID(), cust.getCustomerID())) {
                        User.becomeVisible(store.getStoreID(), cust.getCustomerID());
                    } else {
                        User.becomeInvisible(store.getStoreID(), cust.getCustomerID());
                    }
                }

            }
        } while (true);

    }

    public ArrayList<Seller> readSellers() throws IOException {
        ArrayList<Seller> sellers = new ArrayList<>();
        String line;
        synchronized (USER_DETAILS_GATEKEEPER) {
            try (BufferedReader br = new BufferedReader(new FileReader("loginDetails.txt"))) {
                do {
                    line = br.readLine();

                    if (line == null) {
                        break;
                    }
                    String[] lineSplit = line.split(",");
                    if (lineSplit[3].equals("seller")) {
                        sellers.add(new Seller(lineSplit[0], lineSplit[1], lineSplit[2], lineSplit[3]));
                    }
                } while (true);
            }
        }

        return sellers;
    }

    //read in stores from a file
    public ArrayList<Store> readStores() throws IOException {
        String line;
        ArrayList<Store> stores = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader("stores.txt"))) {
            do {
                line = br.readLine();

                if (line == null) {
                    break;
                }
                String[] lineSplit = line.split(",");
                stores.add(new Store(lineSplit[0], lineSplit[1], lineSplit[2], lineSplit[4]));
            } while (true);
        } catch (FileNotFoundException e) {
            //no need to throw any errors if file is not found
        }
        return stores;
    }

    public void customerMenuProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        //1 to enter new message menu processing
        //2 to enter statistics menu processing
        //3 to enter view stores menu processing
        //4 to search for seller menu processing
        //5 to enter modify account processing
        do {
            updateCustomerInfo();
            oos.writeInt(User.getNewMsgCountTotal(customer.getCustomerID())
                         + User.getDisapMsgCountTotal(customer.getCustomerID()));
            oos.flush();
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            } else if (choice == 1) {
                newMessageProcessingCustomer(oos, ois);
            } else if (choice == 2) {
                statsProcessingCustomer(oos, ois);
            } else if (choice == 3) {
                viewStoresMenuProcessing(oos, ois);
            } else if (choice == 4) {
                searchSellerProcessing(oos, ois);
            } else if (choice == 5) {
                if (modAccountProcessing(oos, ois) == -1) {
                    return;
                }
            }
        } while (true);
    }

    public void newMessageProcessingCustomer(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {

        do {
            //read in an array list of stores
            ArrayList<Store> stores = readStores();
            ArrayList<NewMessage> newMessages = new ArrayList<>();

            for (Store store : stores) {
                int newMsgCount = User.getNewMsgCountPerStore(customer.getCustomerID(), store.getStoreID());
                if (newMsgCount != 0) {
                    newMessages.add(new NewMessage(customer, store, newMsgCount));
                }
            }

            String[] customerNewMessages = new String[newMessages.size()];

            for (int i = 0; i < customerNewMessages.length; i++) {
                customerNewMessages[i] = String.format("Conversation with Store %s owned by %s: %d new messages",
                        newMessages.get(i).getStore().getStoreName(), newMessages.get(i).getStore().getSellerName(),
                        newMessages.get(i).getCountOfNewMessages());
            }

            oos.writeObject(customerNewMessages);
            oos.flush();
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            } else if (choice == -10) {
                continue;
            }

            int newMessageChoice = ois.readInt();
            customerMessagingMenuProcessing(newMessages.get(newMessageChoice).getStore(), oos, ois);
        } while (true);
    }

    public void statsProcessingCustomer(ObjectOutputStream oos, ObjectInputStream ois) throws IOException {

        do {
            ArrayList<Store> stores = readStores();
            ArrayList<Integer> countOfMessagesSent = customer.getNumMsgSentToStores(stores);
            ArrayList<Integer> countOfMessagesReceived = customer.getNumMsgReceivedFromStores(stores);

            if (countOfMessagesReceived.size() != stores.size() || countOfMessagesSent.size() != stores.size()) {
                System.out.println("Error in statsProcessingCustomer");
                return;
            }

            String[] messagesSentToStores = new String[countOfMessagesSent.size()];
            for (int i = 0; i < messagesSentToStores.length; i++) {
                messagesSentToStores[i] = String.format("Total number of messages sent to %s: %d",
                        stores.get(i).getStoreName(), countOfMessagesSent.get(i));
            }

            String[] messagesReceivedFromStores = new String[countOfMessagesReceived.size()];
            for (int i = 0; i < messagesReceivedFromStores.length; i++) {
                messagesReceivedFromStores[i] = String.format("Total number of messages received from %s: %d",
                        stores.get(i).getStoreName(), countOfMessagesReceived.get(i));
            }

            oos.writeObject(messagesSentToStores);
            oos.flush();
            oos.writeObject(messagesReceivedFromStores);
            oos.flush();


            if (sortingCustomerStats(countOfMessagesSent, messagesSentToStores, countOfMessagesReceived,
                    messagesReceivedFromStores, oos, ois) == 0) {
                break;
            }
        } while (true);

    }

    public int sortingCustomerStats(ArrayList<Integer> countOfMessagesSent,
                                    String[] messagesSentToStores,
                                    ArrayList<Integer> countOfMessagesReceived,
                                    String[] messagesReceivedFromStores,
                                    ObjectOutputStream oos,
                                    ObjectInputStream ois) throws IOException {
        //1 - default
        //2 - increasing
        //3 - decreasing
        do {
            int choice = ois.readInt();
            if (choice == 0) {
                return 0;
            } else if (choice == 1) {
                oos.writeObject(messagesSentToStores);
            } else if (choice == 2) {
                oos.writeObject(Customer.sortMessagesSent(countOfMessagesSent, messagesSentToStores, choice));
            } else if (choice == 3) {
                oos.writeObject(Customer.sortMessagesSent(countOfMessagesSent, messagesSentToStores, choice));
            } else if (choice == 4) {
                oos.writeObject(messagesReceivedFromStores);
            } else if (choice == 5) {
                oos.writeObject(Customer.sortMessagesReceived(countOfMessagesReceived, messagesReceivedFromStores,
                        choice));
            } else if (choice == 6) {
                oos.writeObject(Customer.sortMessagesReceived(countOfMessagesReceived, messagesReceivedFromStores,
                        choice));
            } else if (choice == -1) {
                return -1;
            }

            oos.flush();


        } while (true);

    }

    public void viewStoresMenuProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        do {
            //1: next
            //0: return
            updateCustomerInfo();

            //send store info
            ArrayList<Store> stores = readStores();
            ArrayList<String> storeNames = new ArrayList<>();
            for (Store store : stores) {
                storeNames.add(store.getStoreName());
            }
            String[] storesToSend = new String[storeNames.size()];
            storeNames.toArray(storesToSend);
            oos.writeObject(storesToSend);
            oos.flush();

            int choice = ois.readInt();
            if (choice == 0) {
                return;
            }
            if (storesToSend.length == 0) {
                return;
            }

            int storeChoice = ois.readInt();
            Store chosenStore = stores.get(storeChoice);

            customerMessagingMenuProcessing(chosenStore, oos, ois);

        } while (true);
    }

    //scans sellers list to retrieve sellers that match search description
    //returns an ArrayList<String> of matches
    public ArrayList<Seller> searchForSeller(Customer myself, String sellerName) throws IOException {

        ArrayList<Seller> sellerMatches = new ArrayList<>();
        ArrayList<Seller> sellers = readSellers();
        for (Seller sell : sellers) {
            if (sell.getFullName().toLowerCase().contains(sellerName.toLowerCase())) {
                if (!User.areTheyInvisible(myself.getCustomerID(), sell.getSellerID())) {
                    sellerMatches.add(sell);
                }

            }
        }
        return sellerMatches;
    }

    public void searchSellerProcessing(ObjectOutputStream oos, ObjectInputStream ois) throws IOException,
            ClassNotFoundException {
        do {
            updateCustomerInfo();
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            }
            //read search string
            String search = (String) ois.readObject();

            //returns list of seller who are not invis to me
            ArrayList<Seller> sellerMatches = searchForSeller(customer, search);

            ArrayList<String> matches = new ArrayList<>();
            ArrayList<Store> stores = new ArrayList<>();
            for (Seller sell : sellerMatches) {
                for (Store store : sell.getStores()) {
                    //indexes of both arraylists will match
                    matches.add(String.format("%s - %s", sell.getFullName(), store.getStoreName()));
                    stores.add(store);  //used to keep track of what store/seller combo the user picks
                }
            }

            String[] matchesToSend = new String[matches.size()];
            matches.toArray(matchesToSend);

//            if (storeNames.length == 0 || customerMatchesNames.length == 0) {
//                continue;
//            }

            do {
                oos.writeObject(matchesToSend);
                oos.flush();

                int backNext = ois.readInt();
                if (backNext == 0) {
                    break;
                }

                int sellerStoreChoice = ois.readInt();
                Store storeToUse = stores.get(sellerStoreChoice);

                customerMessagingMenuProcessing(storeToUse, oos, ois);
            } while (true);

        } while (true);
    }

    public void customerMessagingMenuProcessing(Store store, ObjectOutputStream oos, ObjectInputStream ois)
            throws IOException, ClassNotFoundException {

        do {

            //if they are blocked - send 1, else send 0
            if (User.areTheyBlocked(customer.getCustomerID(), store.getStoreID())) {
                oos.writeInt(1);
                oos.flush();
            } else {
                oos.writeInt(0);
                oos.flush();
            }

            //if I am invisible to them, send 1, else send 0
            if (User.isInvisible(customer.getCustomerID(), store.getStoreID())) {
                oos.writeInt(1);
                oos.flush();
            } else {
                oos.writeInt(0);
                oos.flush();
            }

            oos.writeObject(store.getStoreName());
            oos.flush();
            oos.writeObject(store.getSellerName());
            oos.flush();
            ArrayList<Message> messages = null;
            synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                messages = User.getMsg(customer.getCustomerID(), store.getStoreID());
            }
            if (messages.size() == 0) {
                oos.writeObject(new String[0]);
                oos.flush();
            } else {
                ArrayList<String> messageInfo = new ArrayList<>();
                for (int i = 0; i < messages.size(); i++) {
                    if (i == 0 && messages.get(i).getIsNew()) {
                        messageInfo.add("---------------------------------------" +
                                        "NEW MESSAGES BELOW----------------------------------------");
                        messageInfo.add(messages.get(i).toString());
                        continue;
                    }
                    if (i + 1 < messages.size()) {
                        if (!messages.get(i).getIsNew() && messages.get(i + 1).getIsNew()) {
                            messageInfo.add(messages.get(i).toString());
                            messageInfo.add("---------------------------------------" +
                                            "NEW MESSAGES BELOW----------------------------------------");
                            continue;
                        }
                    }

                    messageInfo.add(messages.get(i).toString());
                }
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                    User.flushMsg(customer.getCustomerID(), store.getStoreID());
                }

                String[] messageInfoToSend = new String[messageInfo.size()];
                messageInfo.toArray(messageInfoToSend);
                oos.writeObject(messageInfoToSend);
                oos.flush();
            }


            //message menu choices:
            int choice = ois.readInt();
            if (choice == 0) {
                return;
            } else if (choice == 1) {
                if (User.isBlocked(customer.getCustomerID(), store.getStoreID())) {
                    oos.writeInt(-1);
                    oos.flush();
                    continue;
                } else {
                    oos.writeInt(1);
                    oos.flush();
                }
                //-1 back
                //0 regular
                //1 regular disappearing
                //2 file import regular
                //3 file import disappearing
                int msgType = ois.readInt();

                if (msgType == -1) {
                    continue;
                } else if (msgType == 0) {
                    String messageContent = (String) ois.readObject();
                    //send a regular message

                    synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                        User.sendMsg(new Message(customer.getFullName(), customer.getCustomerID(),
                                messageContent, LocalDateTime.now().toString(),
                                false, true), store.getStoreID());
                    }


                } else if (msgType == 1) {
                    String messageContent = (String) ois.readObject();
                    //send a disappearing message
                    synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                        User.sendMsg(new Message(customer.getFullName(), customer.getCustomerID(),
                                messageContent, LocalDateTime.now().toString(),
                                true, true), store.getStoreID());
                    }


                } else if (msgType == 2) {
                    //send a regular file import
                    String[] fileMessage = (String[]) ois.readObject();
                    if (fileMessage == null) {
                        continue;
                    }
                    for (String line : fileMessage) {
                        synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                            User.sendMsg(new Message(customer.getFullName(), customer.getCustomerID(),
                                    line, LocalDateTime.now().toString(),
                                    false, true), store.getStoreID());
                        }

                    }

                } else if (msgType == 3) {
                    //send a disappearing file import
                    String[] fileMessage = (String[]) ois.readObject();
                    if (fileMessage == null) {
                        continue;
                    }
                    for (String line : fileMessage) {
                        synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                            User.sendMsg(new Message(customer.getFullName(), customer.getCustomerID(),
                                    line, LocalDateTime.now().toString(),
                                    true, true), store.getStoreID());
                        }

                    }

                }


            } else if (choice == 2) {
                //0 - back
                //1 - edit
                Message[] messagesToModify;
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                    messagesToModify = User.messagesToModify(customer.getCustomerID(), store.getStoreID());
                }
                String[] messagesToSend = new String[messagesToModify.length];
                for (int i = 0; i < messagesToSend.length; i++) {
                    messagesToSend[i] = messagesToModify[i].toString();
                }
                oos.writeObject(messagesToSend);
                oos.flush();

                if (messagesToSend.length == 0) {
                    ois.readInt();
                    continue;
                }

                int toEdit = ois.readInt();
                if (toEdit == 0) {
                    continue;
                }
                int messageToEditIndex = ois.readInt();
                Message messageToEdit = messagesToModify[messageToEditIndex];
                String editedMessage = (String) ois.readObject();
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                    User.editMsg(editedMessage, messageToEdit, customer.getCustomerID(), store.getStoreID());
                }


            } else if (choice == 3) {
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                    String fileContents = "";
                    try (BufferedReader br = new BufferedReader(new FileReader(String.format(
                            "%s_%s.txt", customer.getCustomerID(), store.getStoreID())))) {
                        String line = br.readLine();
                        while (line != null) {
                            fileContents += line + "\n";
                            line = br.readLine();
                        }

                        if (fileContents.equals("")) {
                            oos.writeObject("");
                            oos.flush();
                            oos.writeObject("");
                            oos.flush();
                        } else {
                            oos.writeObject(String.format("%s_%s.csv", customer.getCustomerID(), store.getStoreID()));
                            oos.flush();
                            oos.writeObject(fileContents);
                            oos.flush();
                        }

                    } catch (FileNotFoundException e) {
                        oos.writeObject("");
                        oos.flush();
                        oos.writeObject("");
                        oos.flush();
                    }
                }

            } else if (choice == 4) {
                //0 - back
                //1 - delete
                Message[] messagesToModify;
                synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                    messagesToModify = User.messagesToModify(customer.getCustomerID(), store.getStoreID());
                }
                String[] messagesToSend = new String[messagesToModify.length];
                for (int i = 0; i < messagesToSend.length; i++) {
                    messagesToSend[i] = messagesToModify[i].toString();
                }
                oos.writeObject(messagesToSend);
                oos.flush();
                int toDelete = ois.readInt();
                if (toDelete == 0) {
                    continue;
                }
                int messageToDeleteIndex = ois.readInt();
                Message messageToDelete = messagesToModify[messageToDeleteIndex];

                synchronized (Objects.requireNonNull(getCustomerGatekeeper(customer.getID()))) {
                    User.deleteMsg(messageToDelete, customer.getCustomerID(), store.getStoreID());
                }

            } else if (choice == 5) {
                synchronized (BLOCK_GATEKEEPER) {
                    if (User.areTheyBlocked(customer.getCustomerID(), store.getStoreID())) {
                        User.unblockUser(customer.getCustomerID(), store.getStoreID());
                    } else {
                        User.blockUser(customer.getCustomerID(), store.getStoreID());
                    }
                }

            } else if (choice == 6) {
                synchronized (INVISIBLE_GATEKEEPER) {
                    if (User.isInvisible(customer.getCustomerID(), store.getStoreID())) {
                        User.becomeVisible(customer.getCustomerID(), store.getStoreID());
                    } else {
                        User.becomeInvisible(customer.getCustomerID(), store.getStoreID());
                    }
                }

            }
        } while (true);

    }

}