/*
 * MediathekView
 * Copyright (C) 2014 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.controller.starter;

import mSearch.tool.Listener;
import mSearch.tool.Log;
import mediathek.config.Daten;
import mediathek.controller.starter.DirectHttpDownload.HttpDownloadState;
import mediathek.daten.DatenDownload;
import mediathek.gui.dialog.DialogContinueDownload;
import mediathek.gui.dialog.MeldungDownloadfehler;
import mediathek.gui.messages.DownloadFinishedEvent;
import mediathek.gui.messages.DownloadStartEvent;
import mediathek.tool.MVInfoFile;
import mediathek.tool.MVSubtitle;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import static mediathek.controller.starter.StarterClass.*;

/**
 * Download files via an external program.
 */
public class ExternalProgramDownload extends Thread
{

    private Daten daten;
    private DatenDownload datenDownload;
    private Start start;
    private File file;
    private String exMessage = "";
    private boolean retAbbrechen;
    private boolean dialogAbbrechenIsVis;
    private HttpDownloadState state = HttpDownloadState.DOWNLOAD;

    public ExternalProgramDownload(Daten daten, DatenDownload d)
    {
        super();
        setName("PROGRAMM DL THREAD: " + d.arr[DatenDownload.DOWNLOAD_TITEL]);

        this.daten = daten;
        datenDownload = d;
        start = datenDownload.start;
        start.status = Start.STATUS_RUN;
        file = new File(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD_DATEINAME]);
        notifyStartEvent(datenDownload);
        try
        {
            if (Boolean.parseBoolean(datenDownload.arr[DatenDownload.DOWNLOAD_INFODATEI]))
            {
                MVInfoFile infoFile = new MVInfoFile();
                infoFile.writeInfoFile(datenDownload);
            }
            if (Boolean.parseBoolean(datenDownload.arr[DatenDownload.DOWNLOAD_SUBTITLE]))
            {
                MVSubtitle subtitleFile = new MVSubtitle();
                subtitleFile.writeSubtitle(datenDownload);
            }

            Files.createDirectories(Paths.get(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD]));
        } catch (IOException ignored)
        {
        } catch (Exception ex)
        {
            Log.errorLog(469365281, ex);
        }
    }

    @Override
    public synchronized void run()
    {
        long filesize = -1;
        final int stat_start = 0;
        final int stat_laufen = 1;
        final int stat_restart = 3;
        final int stat_pruefen = 4;
        // ab hier ist schluss
        final int stat_fertig_ok = 10;
        final int stat_fertig_fehler = 11;
        final int stat_ende = 99;
        int stat = stat_start;

        daten.getMessageBus().publishAsync(new DownloadStartEvent());
        try
        {
            if (!cancelDownload())
            {
                while (stat < stat_ende)
                {
                    switch (stat)
                    {
                        case stat_start:
                            // versuch das Programm zu Starten
                            if (starten())
                            {
                                if (datenDownload.isDownloadManager())
                                {
                                    stat = stat_fertig_ok;
                                } else
                                {
                                    stat = stat_laufen;
                                }
                            } else
                            {
                                stat = stat_restart;
                            }
                            break;
                        case stat_laufen:
                            //hier läuft der Download bis zum Abbruch oder Ende
                            try
                            {
                                if (start.stoppen)
                                {
                                    stat = stat_fertig_ok;
                                    if (start.process != null)
                                    {
                                        start.process.destroy();
                                    }
                                } else
                                {
                                    int exitV = start.process.exitValue();
                                    if (exitV != 0)
                                    {
                                        stat = stat_restart;
                                    } else
                                    {
                                        stat = stat_pruefen;
                                    }
                                }
                            } catch (Exception ex)
                            {
                                try
                                {
                                    this.wait(2000);
                                } catch (InterruptedException ignored)
                                {
                                }
                            }
                            break;
                        case stat_restart:
                            if (!datenDownload.isRestart())
                            {
                                // dann wars das
                                stat = stat_fertig_fehler;
                            } else if (filesize == -1)
                            {
                                //noch nichts geladen
                                deleteIfEmpty(file);
                                if (file.exists())
                                {
                                    // dann bestehende Datei weitermachen
                                    filesize = file.length();
                                    stat = stat_start;
                                } else // counter prüfen und bei einem Maxwert cancelDownload, sonst endlos
                                    if (start.startcounter < Start.STARTCOUNTER_MAX)
                                    {
                                        // dann nochmal von vorne
                                        stat = stat_start;
                                    } else
                                    {
                                        // dann wars das
                                        stat = stat_fertig_fehler;
                                    }
                            } else //jetzt muss das File wachsen, sonst kein Restart
                                if (!file.exists())
                                {
                                    // dann wars das
                                    stat = stat_fertig_fehler;
                                } else if (file.length() > filesize)
                                {
                                    //nur weitermachen wenn die Datei tasächlich wächst
                                    filesize = file.length();
                                    stat = stat_start;
                                } else
                                {
                                    // dann wars das
                                    stat = stat_fertig_fehler;
                                }
                            break;
                        case stat_pruefen:
                            if (datenDownload.quelle == DatenDownload.QUELLE_BUTTON || datenDownload.isDownloadManager())
                            {
                                //für die direkten Starts mit dem Button und die remote downloads wars das dann
                                stat = stat_fertig_ok;
                            } else if (pruefen(daten, datenDownload, start))
                            {
                                //fertig und OK
                                stat = stat_fertig_ok;
                            } else
                            {
                                //fertig und fehlerhaft
                                stat = stat_fertig_fehler;
                            }
                            break;
                        case stat_fertig_fehler:
                            start.status = Start.STATUS_ERR;
                            stat = stat_ende;
                            break;
                        case stat_fertig_ok:
                            start.status = Start.STATUS_FERTIG;
                            stat = stat_ende;
                            break;
                    }
                }
            }
        } catch (Exception ex)
        {
            exMessage = ex.getLocalizedMessage();
            Log.errorLog(395623710, ex);
            SwingUtilities.invokeLater(() ->
            {
                if (!Daten.isAuto())
                {
                    new MeldungDownloadfehler(Daten.getInstance().getMediathekGui(), exMessage, datenDownload).setVisible(true);
                }
            });
        }
        finalizeDownload(datenDownload, start, state);
        daten.getMessageBus().publish(new DownloadFinishedEvent());
    }

    private boolean starten()
    {
        boolean ret = false;
        // die Reihenfolge: startcounter - startmeldung ist wichtig!
        start.startcounter++;
        startmeldung(datenDownload, start);
        RuntimeExec runtimeExec = new RuntimeExec(datenDownload.mVFilmSize, datenDownload.start,
                datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF], datenDownload.arr[DatenDownload.DOWNLOAD_PROGRAMM_AUFRUF_ARRAY]);
        start.process = runtimeExec.exec(true /*log*/);
        if (start.process != null)
        {
            ret = true;
        }
        return ret;
    }

    private boolean cancelDownload()
    {
        if (datenDownload.isDownloadManager())
        {
            // da kümmert sich ein anderes Programm darum
            return false;
        }
        if (!file.exists())
        {
            // dann ist alles OK
            return false;
        }
        if (Daten.isAuto())
        {
            // dann mit gleichem Namen und Datei vorher löschen
            try
            {
                Files.deleteIfExists(file.toPath());
                file = new File(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD_DATEINAME]);
            } catch (IOException ex)
            {
                // kann nicht gelöscht werden, evtl. klappt ja das Überschreiben
                Log.errorLog(795623145, ex, "file exists: " + datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD_DATEINAME]);
            }
            return false; //auf keinen Fall den Dialog starten :)
        }

        dialogAbbrechenIsVis = true;
        retAbbrechen = true;
        if (SwingUtilities.isEventDispatchThread())
        {
            retAbbrechen = abbrechen_();
        } else
        {
            SwingUtilities.invokeLater(() ->
            {
                retAbbrechen = abbrechen_();
                dialogAbbrechenIsVis = false;
            });
        }
        while (dialogAbbrechenIsVis)
        {
            try
            {
                wait(100);
            } catch (Exception ignored)
            {
            }
        }
        return retAbbrechen;
    }

    private boolean abbrechen_()
    {
        boolean result = false;
        if (file.exists())
        {
            DialogContinueDownload dialogContinueDownload = new DialogContinueDownload(Daten.getInstance().getMediathekGui(), datenDownload, false /*weiterführen*/);
            dialogContinueDownload.setVisible(true);

            switch (dialogContinueDownload.getResult())
            {
                case CANCELLED:
                    // dann wars das
                    state = DirectHttpDownload.HttpDownloadState.CANCEL;
                    result = true;
                    break;

                case CONTINUE:
                    // dann mit gleichem Namen und Datei vorher löschen
                    try
                    {
                        Files.deleteIfExists(file.toPath());
                        file = new File(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD_DATEINAME]);
                    } catch (Exception ex)
                    {
                        // kann nicht gelöscht werden, evtl. klappt ja das Überschreiben
                        Log.errorLog(945120398, ex, "file exists: " + datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD_DATEINAME]);
                    }
                    break;

                case RESTART_WITH_NEW_NAME:
                    if (dialogContinueDownload.isNewName())
                    {
                        // jetzt den Programmaufruf nochmal mit dem geänderten Dateinamen nochmal bauen
                        datenDownload.aufrufBauen();
                        Listener.notify(Listener.EREIGNIS_LISTE_DOWNLOADS, this.getClass().getSimpleName());
                        try
                        {
                            Files.createDirectories(Paths.get(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD]));
                        } catch (IOException ignored)
                        {
                        }
                        file = new File(datenDownload.arr[DatenDownload.DOWNLOAD_ZIEL_PFAD_DATEINAME]);
                    }
                    break;
            }
        }
        return result;
    }
}
