<commit_before>/*
 * Copyright 2013 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.battlelancer.seriesguide.dataliberation;

import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Environment;
import android.widget.Toast;

import com.battlelancer.seriesguide.dataliberation.model.Episode;
import com.battlelancer.seriesguide.dataliberation.model.List;
import com.battlelancer.seriesguide.dataliberation.model.ListItem;
import com.battlelancer.seriesguide.dataliberation.model.Season;
import com.battlelancer.seriesguide.dataliberation.model.Show;
import com.battlelancer.seriesguide.provider.SeriesContract;
import com.battlelancer.seriesguide.provider.SeriesContract.Episodes;
import com.battlelancer.seriesguide.provider.SeriesContract.ListItemTypes;
import com.battlelancer.seriesguide.provider.SeriesContract.ListItems;
import com.battlelancer.seriesguide.provider.SeriesContract.Seasons;
import com.battlelancer.seriesguide.provider.SeriesContract.Shows;
import com.battlelancer.seriesguide.util.Lists;
import com.battlelancer.thetvdbapi.TheTVDB.ShowStatus;
import com.google.myjson.Gson;
import com.google.myjson.stream.JsonWriter;
import com.uwetrottmann.androidutils.AndroidUtils;
import com.uwetrottmann.seriesguide.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

/**
 * Export the show database to a human-readable JSON file on external storage.
 * By default meta-data like descriptions, ratings, actors, etc. will not be
 * included.
 */
public class JsonExportTask extends AsyncTask<Void, Void, Integer> {

    public static final String EXPORT_FOLDER = "SeriesGuide";
    public static final String EXPORT_FOLDER_AUTO = "SeriesGuide" + File.separator + "AutoBackup";
    public static final String EXPORT_JSON_FILE_SHOWS = "sg-shows-export.json";
    public static final String EXPORT_JSON_FILE_LISTS = "sg-lists-export.json";

    private static final int SUCCESS = 1;
    private static final int ERROR_STORAGE_ACCESS = 0;
    private static final int ERROR = -1;

    public interface ShowStatusExport {
        String CONTINUING = "continuing";
        String ENDED = "ended";
        String UNKNOWN = "unknown";
    }

    public interface ListItemTypesExport {
        String SHOW = "show";
        String SEASON = "season";
        String EPISODE = "episode";
    }

    private Context mContext;
    private OnTaskFinishedListener mListener;
    private boolean mIsFullDump;
    private boolean mIsAutoBackupMode;

    public static File getExportPath(boolean isAutoBackupMode) {
        return new File(
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                isAutoBackupMode ? EXPORT_FOLDER_AUTO : EXPORT_FOLDER);
    }

    /**
     * Exports the show and lists database to a JSON file each into the
     * Downloads folder on external storage. By default dumps only minimum
     * required data and shows result toasts.
     */
    public JsonExportTask(Context context, OnTaskFinishedListener listener) {
        mContext = context;
        mListener = listener;
    }

    /**
     * Same as {@link JsonExportTask} but allows to set parameters.
     * 
     * @param isFullDump Whether to also export meta-data like descriptions,
     *            ratings, actors, etc. Increases file size about 2-4 times.
     * @param isSilentMode Whether to show result toasts.
     */
    public JsonExportTask(Context context, OnTaskFinishedListener listener, boolean isFullDump,
            boolean isSilentMode) {
        this(context, listener);
        mIsFullDump = isFullDump;
        mIsAutoBackupMode = isSilentMode;
    }

    @Override
    protected Integer doInBackground(Void... params) {
        // Ensure external storage is available
        if (!AndroidUtils.isExtStorageAvailable()) {
            return ERROR_STORAGE_ACCESS;
        }

        // Ensure the export directory exists
        File path = getExportPath(mIsAutoBackupMode);
        path.mkdirs();

        /*
         * Export shows.
         */
        final Cursor shows = mContext.getContentResolver().query(
                Shows.CONTENT_URI,
                mIsFullDump ? ShowsQuery.PROJECTION_FULL : ShowsQuery.PROJECTION,
                null, null, ShowsQuery.SORT);
        if (shows == null) {
            return ERROR;
        }
        if (shows.getCount() == 0) {
            // There are no shows? Done.
            return SUCCESS;
        }

        File backup = new File(path, EXPORT_JSON_FILE_SHOWS);
        try {
            OutputStream out = new FileOutputStream(backup);

            writeJsonStreamShows(out, shows);
        } catch (IOException e) {
            // Backup failed
            return ERROR;
        } finally {
            shows.close();
        }

        /*
         * Export lists.
         */
        final Cursor lists = mContext.getContentResolver().query(SeriesContract.Lists.CONTENT_URI,
                ListsQuery.PROJECTION, null, null, ListsQuery.SORT);
        if (lists == null) {
            return ERROR;
        }
        if (lists.getCount() == 0) {
            // There are no lists? Done.
            return SUCCESS;
        }

        File backupLists = new File(path, EXPORT_JSON_FILE_LISTS);
        try {
            OutputStream out = new FileOutputStream(backupLists);

            writeJsonStreamLists(out, lists);
        } catch (IOException e) {
            return ERROR;
        } finally {
            lists.close();
        }

        return SUCCESS;
    }

    @Override
    protected void onPostExecute(Integer result) {
        if (!mIsAutoBackupMode) {
            int messageId;
            switch (result) {
                case SUCCESS:
                    messageId = R.string.backup_success;
                    break;
                case ERROR_STORAGE_ACCESS:<commit_msg>Store last auto backup time in new JsonExportTask.<commit_after>/*
 * Copyright 2013 <NAME>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.battlelancer.seriesguide.dataliberation;

import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.widget.Toast;

import com.battlelancer.seriesguide.dataliberation.model.Episode;
import com.battlelancer.seriesguide.dataliberation.model.List;
import com.battlelancer.seriesguide.dataliberation.model.ListItem;
import com.battlelancer.seriesguide.dataliberation.model.Season;
import com.battlelancer.seriesguide.dataliberation.model.Show;
import com.battlelancer.seriesguide.provider.SeriesContract;
import com.battlelancer.seriesguide.provider.SeriesContract.Episodes;
import com.battlelancer.seriesguide.provider.SeriesContract.ListItemTypes;
import com.battlelancer.seriesguide.provider.SeriesContract.ListItems;
import com.battlelancer.seriesguide.provider.SeriesContract.Seasons;
import com.battlelancer.seriesguide.provider.SeriesContract.Shows;
import com.battlelancer.seriesguide.ui.SeriesGuidePreferences;
import com.battlelancer.seriesguide.util.Lists;
import com.battlelancer.thetvdbapi.TheTVDB.ShowStatus;
import com.google.myjson.Gson;
import com.google.myjson.stream.JsonWriter;
import com.uwetrottmann.androidutils.AndroidUtils;
import com.uwetrottmann.seriesguide.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

/**
 * Export the show database to a human-readable JSON file on external storage.
 * By default meta-data like descriptions, ratings, actors, etc. will not be
 * included.
 */
public class JsonExportTask extends AsyncTask<Void, Void, Integer> {

    public static final String EXPORT_FOLDER = "SeriesGuide";
    public static final String EXPORT_FOLDER_AUTO = "SeriesGuide" + File.separator + "AutoBackup";
    public static final String EXPORT_JSON_FILE_SHOWS = "sg-shows-export.json";
    public static final String EXPORT_JSON_FILE_LISTS = "sg-lists-export.json";

    private static final int SUCCESS = 1;
    private static final int ERROR_STORAGE_ACCESS = 0;
    private static final int ERROR = -1;

    public interface ShowStatusExport {
        String CONTINUING = "continuing";
        String ENDED = "ended";
        String UNKNOWN = "unknown";
    }

    public interface ListItemTypesExport {
        String SHOW = "show";
        String SEASON = "season";
        String EPISODE = "episode";
    }

    private Context mContext;
    private OnTaskFinishedListener mListener;
    private boolean mIsFullDump;
    private boolean mIsAutoBackupMode;

    public static File getExportPath(boolean isAutoBackupMode) {
        return new File(
                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                isAutoBackupMode ? EXPORT_FOLDER_AUTO : EXPORT_FOLDER);
    }

    /**
     * Exports the show and lists database to a JSON file each into the
     * Downloads folder on external storage. By default dumps only minimum
     * required data and shows result toasts.
     */
    public JsonExportTask(Context context, OnTaskFinishedListener listener) {
        mContext = context;
        mListener = listener;
    }

    /**
     * Same as {@link JsonExportTask} but allows to set parameters.
     * 
     * @param isFullDump Whether to also export meta-data like descriptions,
     *            ratings, actors, etc. Increases file size about 2-4 times.
     * @param isSilentMode Whether to show result toasts.
     */
    public JsonExportTask(Context context, OnTaskFinishedListener listener, boolean isFullDump,
            boolean isSilentMode) {
        this(context, listener);
        mIsFullDump = isFullDump;
        mIsAutoBackupMode = isSilentMode;
    }

    @Override
    protected Integer doInBackground(Void... params) {
        // Ensure external storage is available
        if (!AndroidUtils.isExtStorageAvailable()) {
            return ERROR_STORAGE_ACCESS;
        }

        // Ensure the export directory exists
        File path = getExportPath(mIsAutoBackupMode);
        path.mkdirs();

        /*
         * Export shows.
         */
        final Cursor shows = mContext.getContentResolver().query(
                Shows.CONTENT_URI,
                mIsFullDump ? ShowsQuery.PROJECTION_FULL : ShowsQuery.PROJECTION,
                null, null, ShowsQuery.SORT);
        if (shows == null) {
            return ERROR;
        }
        if (shows.getCount() == 0) {
            // There are no shows? Done.
            return SUCCESS;
        }

        File backup = new File(path, EXPORT_JSON_FILE_SHOWS);
        try {
            OutputStream out = new FileOutputStream(backup);

            writeJsonStreamShows(out, shows);
        } catch (IOException e) {
            // Backup failed
            return ERROR;
        } finally {
            shows.close();
        }

        /*
         * Export lists.
         */
        final Cursor lists = mContext.getContentResolver().query(SeriesContract.Lists.CONTENT_URI,
                ListsQuery.PROJECTION, null, null, ListsQuery.SORT);
        if (lists == null) {
            return ERROR;
        }
        if (lists.getCount() == 0) {
            // There are no lists? Done.
            return SUCCESS;
        }

        File backupLists = new File(path, EXPORT_JSON_FILE_LISTS);
        try {
            OutputStream out = new FileOutputStream(backupLists);

            writeJsonStreamLists(out, lists);
        } catch (IOException e) {
            return ERROR;
        } finally {
            lists.close();
        }

        if (mIsAutoBackupMode) {
            // store current time = last backup time
            final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
            prefs.edit().putLong(SeriesGuidePreferences.KEY_LASTBACKUP, System.currentTimeMillis())
                    .commit();
        }

        return SUCCESS;
    }

    @Override
    protected void onPostExecute(Integer result) {
        if (!mIsAutoBackupMode) {
            int messageId;
            switch (result) {
                case SUCCESS:
                    messageId = R.string.backup_success;
                    break;
                case ERROR_STORAGE_ACCESS:
{'fraction_non_alphanumeric': 0.080204021289178, 'fraction_numerical': 0.0022176227084565346, 'mean_word_length': 2.6903982542280414, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3439586', 'n_tokens_mistral': 3776, 'n_tokens_neox': 3660, 'n_words': 995}