/*
 * DSBDirect
 * Copyright (C) 2019 Fynn Godau
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 * This software is not affiliated with heinekingmedia GmbH, the
 * developer of the DSB platform.
 */

package godau.fynn.dsbdirect.manager;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import android.util.Log;
import godau.fynn.dsbdirect.R;
import godau.fynn.dsbdirect.Utility;
import godau.fynn.dsbdirect.table.Table;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownloadManager {

    private Context mContext;

    public DownloadManager(Context context) {
        mContext = context;
    }

    /**
     * Make a GET request (synchronously)
     *
     * @param url URL to be requested
     * @return Response
     * @throws IOException If networking error
     */
    private @NonNull
    InputStream request(String url) throws IOException {

        URL connectwat = new URL(url);
        HttpURLConnection urlConnection = (HttpURLConnection) connectwat.openConnection();

        return new BufferedInputStream(urlConnection.getInputStream());
    }

    /**
     * Logs in to DSB (synchronously)
     *
     * @param credentialsId   ID
     * @param credentialsPass Pass
     * @return The auth token returned from the server, if not invalid
     * @throws CredentialsIncorrectException If the credentials are invalid
     * @throws IOException                   If the request fails in general
     */
    public String downloadToken(String credentialsId, String credentialsPass)
            throws IOException {
        Log.d("DOWNLOAD", "downloading token");

        if (credentialsId.length() <= 0 || credentialsPass.length() <= 0) {
            // Empty credentials are not valid
            throw new CredentialsIncorrectException();
        }

        // Request
        String response = string(request(mContext.getString(R.string.uri_auth, credentialsId, credentialsPass)));

        // Read auth token from response
        String authToken = response.replace("\"", "");

        // Check for invalid credentials
        boolean credentialsInvalid = authToken.equals("00000000-0000-0000-0000-000000000000");
        if (credentialsInvalid) {
            throw new CredentialsIncorrectException();
        }

        // Store auth token
        new Utility(mContext).saveAuthToken(authToken);

        return authToken;
    }

    /**
     * Downloads available timetables from DSB in JSON format (synchronously)
     *
     * @param authToken Authentication token provided by {@link #downloadToken(String id, String pass)}
     * @return JSON response from server, if not invalid
     * @throws NoContentException          If server has linked to "No Content" page
     * @throws UnexpectedResponseException If response is invalid JSON
     * @throws IOException                 If request fails in general
     */
    public JSONArray downloadTimetableList(String authToken) throws IOException {
        Log.d("DOWNLOAD", "downloading timetable list");

        // Request
        String response = string(request(mContext.getString(R.string.uri_timetables, authToken)));

        try {
            // Make json
            JSONArray json = new JSONArray(response);

            // Check for no content
            String firstUri = json.getJSONObject(0).getString("timetableurl");

            boolean noContent = firstUri.equals("https://light.dsbcontrol.de/DSBlightWebsite/Homepage/NoContent.htm");

            if (noContent) {
                throw new NoContentException();
            } else {
                return json;
            }
        } catch (JSONException e) {
            // Response is invalid, throw further
            throw new UnexpectedResponseException(e.getCause());
        }
    }

    /**
     * Downloads an html table file (synchronously)
     *
     * @param table       The table which is to be downloaded
     * @param fileManager A file manager to be used to save the file
     * @return The downloaded html String
     * @throws IOException In case of networking error
     */
    public String downloadHtmlTable(final Table table, final FileManager fileManager) throws IOException {
        Log.d("DOWNLOAD", "downloading html file at " + table.getUri());

        // Request
        String html = string(request(table.getUri()));

        // If these characters appear, the wrong encoding has been used
        if (html.contains("ï»¿")) {
            try {
                html = new String(html.getBytes("ISO-8859-1"), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // So be it!
                e.printStackTrace();
            }

        }

        // Save file
        fileManager.saveFile(table, html);

        return html;
    }

    /**
     * Downloads a bitmap table file (synchronously)
     *
     * @param table       The table which is to be downloaded
     * @param fileManager A file manager to be used to save the file
     * @return The bitmap
     * @throws IOException In case of networking error
     */
    public Bitmap downloadImageTable(final Table table, final FileManager fileManager) throws IOException {
        // We're doing bitmaps.
        Log.d("DOWNLOAD", "downloading image file at " + table.getUri());

        // Request bitmap
        InputStream inputStream = request(table.getUri());

        // Create bitmap
        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

        // Save bitmap
        fileManager.saveFile(table, bitmap);

        // Return bitmap
        return bitmap;
    }

    public JSONObject downloadUpdateCheck() throws IOException {
        Log.d("DOWNLOAD", "downloading update check");

        try {
            return new JSONObject(string(request(mContext.getString(R.string.uri_versioncheck))));
        } catch (JSONException e) {
            throw new UnexpectedResponseException(e.getCause());
        }
    }

    // Thanks, https://stackoverflow.com/a/35446009
    private String string(InputStream in) throws IOException {
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = in.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }

        return result.toString("UTF-8");

    }

    static public class UnexpectedResponseException extends IOException {
        public UnexpectedResponseException() {
            super();
        }

        public UnexpectedResponseException(Throwable cause) {
            super(cause);
        }
    }

    static public class CredentialsIncorrectException extends UnexpectedResponseException {
    }

    static public class NoContentException extends UnexpectedResponseException {
    }
}
