/*
 * Copyright (C) 2021 eichehome
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package server;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.logging.Logger;
import java.util.logging.Level;
import utils.Client;

/**
 * Diese Klasse stellt das zentrale Verzeichniss aller Clients dar.
 *
 * @version 1.0.0
 * @author eichehome
 */
public class ClientStore {

    /**
     * Attribut, welches ein Array aller verbundenen Clients enthält.
     */
    private Client[] clients = null;

    /**
     * Funktion, welche einen Client dem Verzeichniss aller verbundenen Clients
     * hinzufügt.
     *
     * @param newClient Der neue Client, der hinzugefügt werden soll.
     */
    public void addClient(Client newClient) {
        if (this.clients != null) {
            Client[] temp = new Client[this.clients.length + 1];
            System.arraycopy(clients, 0, temp, 0, clients.length);
            temp[temp.length - 1] = newClient;//Letzter Eintrag ist der neue Client.
            this.clients = temp;
        } else {
            Client[] temp = new Client[1];
            temp[0] = newClient;
            this.clients = temp;
        }
        Logger.getLogger(ClientStore.class.getName()).log(Level.INFO, "ClientStore: Client hinzugefügt");
    }

    /**
     * Funktion, welche einen Client aus dem Verzeichnis aller verbundenen
     * Clients entfernt.
     *
     * @param sender Der Pusher-Thread des Clients, welcher aus dem Verzeichnis
     * entfernt werden soll.
     * @return Gibt die zu dem Client gehörenden Streams und Threads zurück.
     * @throws IllegalArgumentException Falls der Thread nicht existiert oder
     * keine Clients vorhanden sind, wird eine Exeption mit der passenden
     * Fehlermeldung geworfen.
     */
    public Client removeClient(Thread sender) {
        if (this.clients != null) {
            int index = -1;
            //Suche nach dem Element in dem Array
            for (int i = 0; i < this.clients.length; i++) {
                if (this.clients[i].getSender().equals(sender)) {
                    index = i;
                    break;
                }
            }
            if (index != -1) {
                Client[] temp = new Client[this.clients.length - 1];
                //Teil des Arrays vor dem Index des zu entfernenden Elements kopieren.
                System.arraycopy(this.clients, 0, temp, 0, index);
                //Teil des Arrays nach dem Index des zu entfernenden Elements 1 Index nach vorne kopieren.
                System.arraycopy(this.clients, (index + 1), temp, index, (this.clients.length - (index + 1)));
                Client result = this.clients[index];
                this.clients = temp;
                Logger.getLogger(ClientStore.class.getName()).log(Level.INFO, "ClientStore: Client gelöscht");
                return result;
            } else {
                throw new IllegalArgumentException("Element not found");
            }
        } else {
            throw new IllegalArgumentException("No clients present");
        }
    }

    /**
     * Funktion, welche alle Streams zu den Clients, die derzeit Verbunden sind,
     * zurückgibt.
     *
     * @return Ein Array aus allen Streams
     */
    public ObjectOutputStream[] getAllOutputStreams() {
        ObjectOutputStream[] streams = new ObjectOutputStream[this.clients.length];
        for (int i = 0; i < this.clients.length; i++) {
            streams[i] = this.clients[i].getOutputStream();
        }
        Logger.getLogger(ClientStore.class.getName()).log(Level.INFO, "ClientStore: Alle Streams zusammengesucht");
        return streams;
    }

    /**
     * Funktion, die den zu einem Thread gehörenden OutputStream schließt.
     *
     * @param sender Der Thread, dessen OutputStream geschlossen werden soll
     * @throws IllegalArgumentException Falls der Thread nicht existiert oder
     * keine Clients vorhanden sind, wird eine Exeption mit der passenden
     * Fehlermeldung geworfen.
     */
    public void close(Thread sender) {
        if (this.clients != null) {
            int index = -1;
            for (int i = 0; i < this.clients.length; i++) {
                if (this.clients[i].getSender().equals(sender)) {
                    index = i;
                }
            }
            if (index != -1) {
                try {
                    this.clients[index].getOutputStream().close();
                } catch (IOException ex) {
                    Logger.getLogger(ClientStore.class.getName()).log(Level.SEVERE, String.format("ClientStore: Fehler: %s", ex));
                }
            } else {
                throw new IllegalArgumentException("Element not found");
            }
        } else {
            throw new IllegalArgumentException("No Clients Present");
        }
    }

}
