/*
 * MediathekView
 * Copyright (C) 2008 W. Xaver
 * W.Xaver[at]googlemail.com
 * http://zdfmediathk.sourceforge.net/
 *
 * 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
 * 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 mediathek.config;

import com.jidesoft.utils.SystemInfo;
import mSearch.daten.ListeFilme;
import mSearch.filmlisten.WriteFilmlistJson;
import mSearch.tool.Listener;
import mSearch.tool.Log;
import mSearch.tool.ReplaceList;
import mSearch.tool.SysMsg;
import mediathek.MediathekGui;
import mediathek.controller.IoXmlLesen;
import mediathek.controller.IoXmlSchreiben;
import mediathek.controller.MVUsedUrls;
import mediathek.controller.starter.StarterClass;
import mediathek.daten.*;
import mediathek.filmlisten.FilmeLaden;
import mediathek.gui.*;
import mediathek.gui.dialog.DialogMediaDB;
import mediathek.gui.filmInformation.IFilmInformation;
import mediathek.tool.GuiFunktionen;
import mediathek.tool.MVFont;
import mediathek.tool.MVMessageDialog;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

public class Daten
{

    private static Daten instance;

    public static final String LINE_SEPARATOR = System.getProperty("line.separator");
    // flags
    private static boolean debug = false; // Debugmodus
    private static boolean startMaximized = false; // Fenster maximieren
    private static boolean auto = false; // Version: MediathekAuto
    private static boolean reset = false; // Programm auf Starteinstellungen zurücksetzen
    //alle Programmeinstellungen

    // zentrale Klassen
    public static final MVColor mVColor = new MVColor(); // verwendete Farben
    private FilmeLaden filmeLaden; // erledigt das updaten der Filmliste
    private ListeFilme listeFilme = null;
    private ListeFilme listeFilmeNachBlackList = null; // ist DIE Filmliste
    private ListeFilme listeFilmeHistory = null; // für die HEUTIGE HISTORY
    private ListeDownloads listeDownloads = null; // Filme die als "Download: Tab Download" geladen werden sollen
    private ListeDownloads listeDownloadsButton = null; // Filme die über "Tab Filme" als Button/Film abspielen gestartet werden
    private ListeBlacklist listeBlacklist = null;
    private ListeMediaDB listeMediaDB = null;
    private ListeMediaPath listeMediaPath = null;
    private ListeAbo listeAbo = null;
    private DownloadInfos downloadInfos = null;

    // Verzeichnis zum Speichern der Programmeinstellungen
    private static String basisverzeichnis;
    public static ListePset listePset = null;
    public MVUsedUrls history = null; // alle angesehenen Filme
    public MVUsedUrls erledigteAbos = null; // erfolgreich geladenen Abos

    public StarterClass starterClass = null; // Klasse zum Ausführen der Programme (für die Downloads): VLC, flvstreamer, ...

    // Gui
    private MediathekGui mediathekGui; // JFrame der Gui
    public static GuiFilme guiFilme = null; // Tab mit den Filmen
    public static GuiDownloads guiDownloads = null; // Tab mit den Downloads
    public static GuiAbo guiAbo = null; // Tab mit den Abos
    public static GuiDebug guiDebug = null;
    public static GuiMeldungen guiMeldungen = null;

    public static IFilmInformation filmInfo = null; // Infos zum Film
    private DialogMediaDB dialogMediaDB;

    private boolean alreadyMadeBackup = false;

    public static void setDebug(final boolean aIsDebug)
    {
        debug = aIsDebug;
    }

    public static void setStartMaximized(final boolean aIsStartMaximized)
    {
        startMaximized = aIsStartMaximized;
    }

    public static void setAuto(final boolean aIsAuto)
    {
        auto = aIsAuto;
    }

    public static void setReset(final boolean aIsReset)
    {
        reset = aIsReset;
    }

    public static boolean isDebug()
    {
        return debug;
    }

    public static boolean isStartMaximized()
    {
        return startMaximized;
    }

    public static boolean isAuto()
    {
        return auto;
    }

    public static boolean isReset()
    {
        return reset;
    }

    public static final Daten getInstance(String aBasisverzeichnis)
    {
        basisverzeichnis = aBasisverzeichnis;
        return getInstance();
    }

    public static final Daten getInstance(String aBasisverzeichnis, MediathekGui aMediathekGui1)
    {
        basisverzeichnis = aBasisverzeichnis;
        return getInstance(aMediathekGui1);
    }

    private static final Daten getInstance(MediathekGui aMediathekGui)
    {
        return instance == null ? instance = new Daten(aMediathekGui) : instance;
    }

    public static final Daten getInstance()
    {
        return instance == null ? instance =  new Daten() : instance;
    }


    private Daten()
    {
        mediathekGui = null;
        start();
    }

    private Daten(MediathekGui aMediathekGui)
    {
        mediathekGui = aMediathekGui;
        start();
    }

    private void start()
    {
        listeFilme = new ListeFilme();
        filmeLaden = new FilmeLaden(this);
        listeFilmeHistory = new ListeFilme();

        updateSplashScreen("Lade Blacklist...");
        listeFilmeNachBlackList = new ListeFilme();
        listeBlacklist = new ListeBlacklist();

        updateSplashScreen("Lade Programmsets...");
        listePset = new ListePset();

        updateSplashScreen("Lade Abos...");
        listeAbo = new ListeAbo(this);

        updateSplashScreen("Lade Downloads...");
        listeDownloads = new ListeDownloads(this);
        listeDownloadsButton = new ListeDownloads(this);

        updateSplashScreen("Lade erledigte Abos...");
        erledigteAbos = new MVUsedUrls(Konstanten.FILE_ERLEDIGTE_ABOS, getSettingsDirectory_String(), Listener.EREIGNIS_LISTE_ERLEDIGTE_ABOS);

        updateSplashScreen("Lade History...");
        history = new MVUsedUrls(Konstanten.FILE_HISTORY, getSettingsDirectory_String(), Listener.EREIGNIS_LISTE_HISTORY_GEAENDERT);

        listeMediaDB = new ListeMediaDB(this);
        listeMediaPath = new ListeMediaPath();

        downloadInfos = new DownloadInfos(this);
        starterClass = new StarterClass(this);

        Timer timer = new Timer(1000, e ->
        {
            downloadInfos.makeDownloadInfos();
            Listener.notify(Listener.EREIGNIS_TIMER, Daten.class.getName());
        });
        timer.setInitialDelay(4000); // damit auch alles geladen ist
        timer.start();
    }

    public static String getUserAgent()
    {
        return MVConfig.get(MVConfig.Configs.SYSTEM_PARAMETER_USERAGENT);
    }

    /**
     * Liefert den Pfad zur Filmliste
     *
     * @return Den Pfad als String
     */
    public static String getDateiFilmliste()
    {
        String strFile;

        if (SystemInfo.isMacOSX())
        {
            //place filmlist into OS X user cache directory in order not to backup it all the time in TimeMachine...
            strFile = GuiFunktionen.getHomePath() + File.separator + "Library/Caches/MediathekView" + File.separator + Konstanten.JSON_DATEI_FILME;
        } else
        {
            strFile = getSettingsDirectory_String() + File.separator + Konstanten.JSON_DATEI_FILME;
        }

        return strFile;
    }

    /**
     * Return the location of the settings directory.
     * If it does not exist, create one.
     *
     * @return Path to the settings directory
     * @throws IllegalStateException Will be thrown if settings directory don't exist and if there is an error on creating it.
     */
    public static Path getSettingsDirectory() throws IllegalStateException
    {
        final Path baseDirectoryPath;
        if (basisverzeichnis == null || basisverzeichnis.isEmpty())
        {
            baseDirectoryPath = Paths.get(System.getProperty("user.home"), Konstanten.VERZEICHNIS_EINSTELLUNGEN);
        } else
        {
            baseDirectoryPath = Paths.get(basisverzeichnis);
        }


        if (Files.notExists(baseDirectoryPath))
        {
            try
            {
                Files.createDirectories(baseDirectoryPath);
            } catch (IOException ioException)
            {
                Messages.logMessage(Messages.ERROR_CANT_CREATE_FOLDER, ioException, baseDirectoryPath.toString());
                throw new IllegalStateException(Messages.ERROR_CANT_CREATE_FOLDER.getTextFormatted(baseDirectoryPath.toString()), ioException);
            }
        }

        return baseDirectoryPath;
    }

    public static String getSettingsDirectory_String()
    {
        return getSettingsDirectory().toString();
    }

    /**
     * Return the path to "mediathek.xml"
     *
     * @return Path object to mediathek.xml file
     */
    public static Path getMediathekXmlFilePath()
    {
        return Daten.getSettingsDirectory().resolve(Konstanten.CONFIG_FILE);
    }

    /**
     * Return the path to "mediathek.xml_copy_"
     * first copy exists
     *
     * @param xmlFilePath Path to file.
     */
    public static void getMediathekXmlCopyFilePath(ArrayList<Path> xmlFilePath)
    {
        for (int i = 1; i <= MAX_COPY; ++i)
        {
            Path path = Daten.getSettingsDirectory().resolve(Konstanten.CONFIG_FILE_COPY + i);
            if (Files.exists(path))
            {
                xmlFilePath.add(path);
            }
        }
    }

    public void filmlisteSpeichern()
    {
        new WriteFilmlistJson().filmlisteSchreibenJson(getDateiFilmliste(), listeFilme);
    }

    /**
     * Update the {@link java.awt.SplashScreen} only if we have a Swing UI.
     *
     * @param text The displayed text on the splash graphics.
     */
    private void updateSplashScreen(String text)
    {
        if (mediathekGui != null)
        {
            mediathekGui.updateSplashScreenText(text);
        }
    }

    public boolean allesLaden()
    {
        updateSplashScreen("Lade Konfigurationsdaten...");

        if (!load())
        {
            SysMsg.sysMsg("Weder Konfig noch Backup konnte geladen werden!");
            // teils geladene Reste entfernen
            clearKonfig();
            return false;
        }
        SysMsg.sysMsg("Konfig wurde gelesen!");
        mVColor.load(); // Farben einrichten
        MVFont.initFont(); // Fonts einrichten

        // erst die Systemdaten, dann die Filmliste
        updateSplashScreen("Lade Filmliste...");
        return true;
    }

    private void clearKonfig()
    {
        listePset.clear();
        ReplaceList.list.clear();
        listeAbo.clear();
        listeDownloads.clear();
        listeBlacklist.clear();
    }

    private boolean load()
    {
        boolean ret = false;
        Path xmlFilePath = Daten.getMediathekXmlFilePath();

        if (Files.exists(xmlFilePath))
        {
            if (IoXmlLesen.datenLesen(xmlFilePath))
            {
                return true;
            } else
            {
                // dann hat das Laden nicht geklappt
                SysMsg.sysMsg("Konfig konnte nicht gelesen werden!");
            }
        } else
        {
            // dann hat das Laden nicht geklappt
            SysMsg.sysMsg("Konfig existiert nicht!");
        }

        // versuchen das Backup zu laden
        if (loadBackup())
        {
            ret = true;
        }
        return ret;
    }

    private boolean loadBackup()
    {
        boolean ret = false;
        ArrayList<Path> path = new ArrayList<>();
        Daten.getMediathekXmlCopyFilePath(path);
        if (path.isEmpty())
        {
            SysMsg.sysMsg("Es gibt kein Backup");
            return false;
        }

        // dann gibts ein Backup
        SysMsg.sysMsg("Es gibt ein Backup");
        mediathekGui.closeSplashScreen();
        int r = JOptionPane.showConfirmDialog(null, "Die Einstellungen sind beschädigt\n"
                + "und können nicht geladen werden.\n"
                + "Soll versucht werden, mit gesicherten\n"
                + "Einstellungen zu starten?\n\n"
                + "(ansonsten startet das Programm mit\n"
                + "Standardeinstellungen)", "Gesicherte Einstellungen laden?", JOptionPane.YES_NO_OPTION);

        if (r != JOptionPane.OK_OPTION)
        {
            SysMsg.sysMsg("User will kein Backup laden.");
            return false;
        }

        for (Path p : path)
        {
            // teils geladene Reste entfernen
            clearKonfig();
            SysMsg.sysMsg(new String[]{"Versuch Backup zu laden:", p.toString()});
            if (IoXmlLesen.datenLesen(p))
            {
                SysMsg.sysMsg(new String[]{"Backup hat geklappt:", p.toString()});
                ret = true;
                break;
            }

        }
        return ret;
    }

    public void allesSpeichern()
    {
        konfigCopy();
        IoXmlSchreiben.datenSchreiben();
        if (Daten.isReset())
        {
            // das Programm soll beim nächsten Start mit den Standardeinstellungen gestartet werden
            // dazu wird den Ordner mit den Einstellungen umbenannt
            String dir1 = getSettingsDirectory_String();
            if (dir1.endsWith(File.separator))
            {
                dir1 = dir1.substring(0, dir1.length() - 1);
            }

            try
            {
                final Path path1 = Paths.get(dir1);
                final String dir2 = dir1 + "--" + new SimpleDateFormat("yyyy.MM.dd__HH.mm.ss").format(new Date());

                Files.move(path1, Paths.get(dir2), StandardCopyOption.REPLACE_EXISTING);
                Files.deleteIfExists(path1);
            } catch (IOException e)
            {
                SysMsg.sysMsg("Die Einstellungen konnten nicht zurückgesetzt werden.");
                if (mediathekGui != null)
                {
                    MVMessageDialog.showMessageDialog(mediathekGui, "Die Einstellungen konnten nicht zurückgesetzt werden.\n"
                            + "Sie müssen jetzt das Programm beenden und dann den Ordner:\n"
                            + getSettingsDirectory_String() + '\n'
                            + "von Hand löschen und dann das Programm wieder starten.\n\n"
                            + "Im Forum finden Sie weitere Hilfe.", "Fehler", JOptionPane.ERROR_MESSAGE);
                }
                Log.errorLog(465690123, e);
            }
        }
    }

    /**
     * Maximum number of backup files to be stored.
     */
    private final static int MAX_COPY = 5;

    /**
     * Create backup copies of settings file.
     */
    private void konfigCopy()
    {
        if (!alreadyMadeBackup)
        {
            // nur einmal pro Programmstart machen
            SysMsg.sysMsg("-------------------------------------------------------");
            SysMsg.sysMsg("Einstellungen sichern");

            try
            {
                final Path xmlFilePath = Daten.getMediathekXmlFilePath();
                long creatTime = -1;

                Path xmlFilePathCopy_1 = Daten.getSettingsDirectory().resolve(Konstanten.CONFIG_FILE_COPY + 1);
                if (Files.exists(xmlFilePathCopy_1))
                {
                    BasicFileAttributes attrs = Files.readAttributes(xmlFilePathCopy_1, BasicFileAttributes.class);
                    FileTime d = attrs.lastModifiedTime();
                    creatTime = d.toMillis();
                }

                if (creatTime == -1 || creatTime < getHeute_0Uhr())
                {
                    // nur dann ist die letzte Kopie älter als einen Tag
                    for (int i = MAX_COPY; i > 1; --i)
                    {
                        xmlFilePathCopy_1 = Daten.getSettingsDirectory().resolve(Konstanten.CONFIG_FILE_COPY + (i - 1));
                        final Path xmlFilePathCopy_2 = Daten.getSettingsDirectory().resolve(Konstanten.CONFIG_FILE_COPY + i);
                        if (Files.exists(xmlFilePathCopy_1))
                        {
                            Files.move(xmlFilePathCopy_1, xmlFilePathCopy_2, StandardCopyOption.REPLACE_EXISTING);
                        }
                    }
                    if (Files.exists(xmlFilePath))
                    {
                        Files.move(xmlFilePath, Daten.getSettingsDirectory().resolve(Konstanten.CONFIG_FILE_COPY + 1), StandardCopyOption.REPLACE_EXISTING);
                    }
                    SysMsg.sysMsg("Einstellungen wurden gesichert");
                } else
                {
                    SysMsg.sysMsg("Einstellungen wurden heute schon gesichert");
                }
            } catch (IOException e)
            {
                SysMsg.sysMsg("Die Einstellungen konnten nicht komplett gesichert werden!");
                Log.errorLog(795623147, e);
            }

            alreadyMadeBackup = true;
            SysMsg.sysMsg("-------------------------------------------------------");
        }
    }

    /**
     * Return the number of milliseconds from today´s midnight.
     *
     * @return Number of milliseconds from today´s midnight.
     */
    public static long getHeute_0Uhr()
    {
        final Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return cal.getTimeInMillis();
    }

    public FilmeLaden getFilmeLaden()
    {
        return filmeLaden;
    }

    public ListeFilme getListeFilme()
    {
        return listeFilme;
    }

    public ListeFilme getListeFilmeNachBlackList()
    {
        return listeFilmeNachBlackList;
    }

    public ListeFilme getListeFilmeHistory()
    {
        return listeFilmeHistory;
    }

    public ListeDownloads getListeDownloads()
    {
        return listeDownloads;
    }

    public ListeDownloads getListeDownloadsButton()
    {
        return listeDownloadsButton;
    }

    public ListeBlacklist getListeBlacklist()
    {
        return listeBlacklist;
    }

    public ListeMediaDB getListeMediaDB()
    {
        return listeMediaDB;
    }

    public ListeMediaPath getListeMediaPath()
    {
        return listeMediaPath;
    }

    public ListeAbo getListeAbo()
    {
        return listeAbo;
    }

    public DownloadInfos getDownloadInfos()
    {
        return downloadInfos;
    }

    public MediathekGui getMediathekGui()
    {
        return mediathekGui;
    }

    public void setListeFilme(final ListeFilme listeFilme)
    {
        this.listeFilme = listeFilme;
    }

    public void setDialogMediaDB(final DialogMediaDB aDialogMediaDB)
    {
        dialogMediaDB = aDialogMediaDB;
    }

    public DialogMediaDB getDialogMediaDB()
    {
        return dialogMediaDB;
    }
}
