package app.fedilab.android.client;
/* Copyright 2017 Thomas Schneider
 *
 * This file is a part of Fedilab
 *
 * 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.
 *
 * Fedilab 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 Fedilab; if not,
 * see <http://www.gnu.org/licenses>. */

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.asynctasks.UpdateAccountInfoAsyncTask;
import app.fedilab.android.client.Entities.Account;
import app.fedilab.android.client.Entities.AccountAdmin;
import app.fedilab.android.client.Entities.AccountCreation;
import app.fedilab.android.client.Entities.AdminAction;
import app.fedilab.android.client.Entities.Announcement;
import app.fedilab.android.client.Entities.Application;
import app.fedilab.android.client.Entities.Attachment;
import app.fedilab.android.client.Entities.Card;
import app.fedilab.android.client.Entities.Conversation;
import app.fedilab.android.client.Entities.Emojis;
import app.fedilab.android.client.Entities.Error;
import app.fedilab.android.client.Entities.Filters;
import app.fedilab.android.client.Entities.HowToVideo;
import app.fedilab.android.client.Entities.IdentityProof;
import app.fedilab.android.client.Entities.Instance;
import app.fedilab.android.client.Entities.InstanceNodeInfo;
import app.fedilab.android.client.Entities.InstanceReg;
import app.fedilab.android.client.Entities.InstanceSocial;
import app.fedilab.android.client.Entities.Mention;
import app.fedilab.android.client.Entities.NodeInfo;
import app.fedilab.android.client.Entities.Notification;
import app.fedilab.android.client.Entities.Peertube;
import app.fedilab.android.client.Entities.Poll;
import app.fedilab.android.client.Entities.PollOptions;
import app.fedilab.android.client.Entities.Reaction;
import app.fedilab.android.client.Entities.Relationship;
import app.fedilab.android.client.Entities.Report;
import app.fedilab.android.client.Entities.Results;
import app.fedilab.android.client.Entities.Schedule;
import app.fedilab.android.client.Entities.Status;
import app.fedilab.android.client.Entities.StoredStatus;
import app.fedilab.android.client.Entities.Tag;
import app.fedilab.android.client.Entities.Trends;
import app.fedilab.android.client.Entities.TrendsHistory;
import app.fedilab.android.fragments.DisplayNotificationsFragment;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.sqlite.AccountDAO;
import app.fedilab.android.sqlite.Sqlite;
import app.fedilab.android.sqlite.TimelineCacheDAO;


/**
 * Created by Thomas on 23/04/2017.
 * Manage Calls to the REST API
 * Modify the 16/11/2017 with httpsurlconnection
 */

public class API {


    private Account account;
    private Context context;
    private Results results;
    private Attachment attachment;
    private List<Account> accounts;
    private List<Status> statuses;
    private int tootPerPage, accountPerPage, notificationPerPage;
    private int actionCode;
    private String instance;
    private String prefKeyOauthTokenT;
    private APIResponse apiResponse;
    private Error APIError;
    private List<String> domains;

    public API(Context context) {
        this.context = context;
        if (context == null) {
            APIError = new Error();
            return;
        }
        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        tootPerPage = sharedpreferences.getInt(Helper.SET_TOOT_PER_PAGE, Helper.TOOTS_PER_PAGE);
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED && tootPerPage > 30) {
            tootPerPage = 30;
        }
        accountPerPage = Helper.ACCOUNTS_PER_PAGE;
        notificationPerPage = Helper.NOTIFICATIONS_PER_PAGE;
        this.prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
        if (Helper.getLiveInstance(context) != null)
            this.instance = Helper.getLiveInstance(context);
        else {
            SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
            String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
            String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(context));
            Account account = new AccountDAO(context, db).getUniqAccount(userId, instance);
            if (account == null) {
                APIError = new Error();
                APIError.setError(context.getString(R.string.toast_error));
                return;
            }
            this.instance = account.getInstance().trim();
        }
        apiResponse = new APIResponse();
        APIError = null;
    }

    public API(Context context, String instance, String token) {
        this.context = context;
        if (context == null) {
            apiResponse = new APIResponse();
            APIError = new Error();
            return;
        }
        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        tootPerPage = sharedpreferences.getInt(Helper.SET_TOOT_PER_PAGE, Helper.TOOTS_PER_PAGE);
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED && tootPerPage > 30) {
            tootPerPage = 30;
        }
        accountPerPage = Helper.ACCOUNTS_PER_PAGE;
        notificationPerPage = Helper.NOTIFICATIONS_PER_PAGE;
        if (instance != null)
            this.instance = instance;
        else
            this.instance = Helper.getLiveInstance(context);

        if (token != null)
            this.prefKeyOauthTokenT = token;
        else
            this.prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null);
        apiResponse = new APIResponse();
        APIError = null;
    }

    /**
     * Parse json response an unique Car
     *
     * @param resobj JSONObject
     * @return Card
     */
    private static Card parseCardResponse(JSONObject resobj) {

        Card card = new Card();
        try {
            card.setUrl(resobj.getString("url"));
            card.setTitle(resobj.getString("title"));
            card.setDescription(resobj.getString("description"));
            card.setImage(resobj.getString("image"));

            card.setType(resobj.getString("type"));
            try {
                card.setAuthor_name(resobj.getString("author_name"));
            } catch (Exception e) {
                card.setAuthor_name(null);
            }
            try {
                card.setAuthor_url(resobj.getString("author_url"));
            } catch (Exception e) {
                card.setAuthor_url(null);
            }
            try {
                card.setHtml(resobj.getString("html"));
            } catch (Exception e) {
                card.setHtml(null);
            }
            try {
                card.setEmbed_url(resobj.getString("embed_url"));
            } catch (Exception e) {
                card.setEmbed_url(null);
            }
            try {
                card.setProvider_name(resobj.getString("provider_name"));
            } catch (Exception e) {
                card.setProvider_name(null);
            }
            try {
                card.setProvider_url(resobj.getString("provider_url"));
            } catch (Exception e) {
                card.setProvider_url(null);
            }
            try {
                card.setHeight(resobj.getInt("height"));
            } catch (Exception e) {
                card.setHeight(0);
            }
            try {
                card.setWidth(resobj.getInt("width"));
            } catch (Exception e) {
                card.setWidth(0);
            }
        } catch (JSONException e) {
            card = null;
        }
        return card;
    }

    /**
     * Parse json response an unique instance social result
     *
     * @param resobj JSONObject
     * @return InstanceSocial
     */
    public static InstanceSocial parseInstanceSocialResponse(JSONObject resobj) {

        InstanceSocial instanceSocial = new InstanceSocial();
        try {


            instanceSocial.setUptime(Float.parseFloat(resobj.getString("uptime")));
            instanceSocial.setUp(resobj.getBoolean("up"));

            instanceSocial.setConnections(Long.parseLong(resobj.getString("connections")));
            instanceSocial.setDead(resobj.getBoolean("dead"));


            instanceSocial.setId(resobj.getString("id"));

            instanceSocial.setInfo(resobj.getString("info"));
            instanceSocial.setVersion(resobj.getString("version"));
            instanceSocial.setName(resobj.getString("name"));
            instanceSocial.setObs_rank(resobj.getString("obs_rank"));
            instanceSocial.setThumbnail(resobj.getString("thumbnail"));
            instanceSocial.setIpv6(resobj.getBoolean("ipv6"));
            instanceSocial.setObs_score(resobj.getInt("obs_score"));
            instanceSocial.setOpen_registrations(resobj.getBoolean("open_registrations"));

            instanceSocial.setUsers(Long.parseLong(resobj.getString("users")));
            instanceSocial.setStatuses(Long.parseLong(resobj.getString("statuses")));

            instanceSocial.setHttps_rank(resobj.getString("https_rank"));
            instanceSocial.setHttps_score(resobj.getInt("https_score"));
            if (!resobj.isNull("added_at")) {
                instanceSocial.setAdded_at(Helper.mstStringToDate(resobj.getString("added_at")));
            }
            instanceSocial.setChecked_at(Helper.mstStringToDate(resobj.getString("checked_at")));
            instanceSocial.setUpdated_at(Helper.mstStringToDate(resobj.getString("updated_at")));


        } catch (Exception e) {
            e.printStackTrace();
        }
        return instanceSocial;
    }

    /**
     * Parse json response for unique how to
     *
     * @param resobj JSONObject
     * @return Peertube
     */
    public static Peertube parsePeertube(String instance, JSONObject resobj) {
        Peertube peertube = new Peertube();
        try {
            peertube.setId(resobj.getString("id"));
            peertube.setCache(resobj);
            peertube.setUuid(resobj.getString("uuid"));
            peertube.setName(resobj.getString("name"));
            peertube.setDescription(resobj.getString("description"));
            peertube.setEmbedPath(resobj.getString("embedPath"));
            peertube.setPreviewPath(resobj.getString("previewPath"));
            peertube.setThumbnailPath(resobj.getString("thumbnailPath"));
            peertube.setAccount(parseAccountResponsePeertube(instance, resobj.getJSONObject("account")));
            peertube.setInstance(instance);
            peertube.setView(resobj.getInt("views"));
            peertube.setLike(resobj.getInt("likes"));
            peertube.setDislike(resobj.getInt("dislikes"));
            peertube.setDuration(resobj.getInt("duration"));
            try {
                peertube.setCreated_at(Helper.mstStringToDate(resobj.getString("createdAt")));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return peertube;
    }

    /**
     * Parse json response for unique how to
     *
     * @param resobj JSONObject
     * @return Peertube
     */
    private static Peertube parseSinglePeertube(String instance, JSONObject resobj) {
        Peertube peertube = new Peertube();
        try {
            peertube.setId(resobj.getString("id"));
            peertube.setUuid(resobj.getString("uuid"));
            peertube.setName(resobj.getString("name"));
            peertube.setCache(resobj);
            peertube.setInstance(instance);
            peertube.setHost(resobj.getJSONObject("account").getString("host"));
            peertube.setDescription(resobj.getString("description"));
            peertube.setEmbedPath(resobj.getString("embedPath"));
            peertube.setPreviewPath(resobj.getString("previewPath"));
            peertube.setThumbnailPath(resobj.getString("thumbnailPath"));
            peertube.setView(resobj.getInt("views"));
            peertube.setLike(resobj.getInt("likes"));
            peertube.setCommentsEnabled(resobj.getBoolean("commentsEnabled"));
            peertube.setDislike(resobj.getInt("dislikes"));
            peertube.setDuration(resobj.getInt("duration"));
            peertube.setAccount(parseAccountResponsePeertube(instance, resobj.getJSONObject("account")));
            try {
                peertube.setCreated_at(Helper.mstStringToDate(resobj.getString("createdAt")));
            } catch (ParseException e) {
                e.printStackTrace();
            }

            ArrayList<String> resolutions = new ArrayList<>();
            if (resobj.has("streamingPlaylists") && resobj.getJSONArray("streamingPlaylists").length() > 0) {
                JSONArray files = resobj.getJSONArray("streamingPlaylists").getJSONObject(0).getJSONArray("files");

                for (int j = 0; j < files.length(); j++) {
                    JSONObject attObj = files.getJSONObject(j);
                    resolutions.add(attObj.getJSONObject("resolution").getString("id"));
                }
                peertube.setResolution(resolutions);
                peertube.setStreamService(true);
            } else {
                JSONArray files = resobj.getJSONArray("files");

                for (int j = 0; j < files.length(); j++) {
                    JSONObject attObj = files.getJSONObject(j);
                    resolutions.add(attObj.getJSONObject("resolution").getString("id"));
                }
                peertube.setResolution(resolutions);
                peertube.setStreamService(false);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return peertube;
    }

    /**
     * Parse json response for peertube comments
     *
     * @param resobj JSONObject
     * @return Peertube
     */
    private static List<Status> parseSinglePeertubeComments(Context context, String instance, JSONObject resobj) {
        List<Status> statuses = new ArrayList<>();
        try {
            JSONArray jsonArray = resobj.getJSONArray("data");
            int i = 0;
            while (i < jsonArray.length()) {
                Status status = new Status();
                JSONObject comment = jsonArray.getJSONObject(i);
                status.setId(comment.getString("id"));
                status.setUri(comment.getString("url"));
                status.setUrl(comment.getString("url"));
                status.setSensitive(false);
                status.setSpoiler_text("");
                status.setContent(context, comment.getString("text"));
                status.setIn_reply_to_id(comment.getString("inReplyToCommentId"));
                status.setAccount(parseAccountResponsePeertube(instance, comment.getJSONObject("account")));
                status.setCreated_at(Helper.mstStringToDate(comment.getString("createdAt")));
                status.setMentions(new ArrayList<>());
                status.setEmojis(new ArrayList<>());
                status.setMedia_attachments(new ArrayList<>());
                status.setVisibility("public");
                status.setViewType(context);
                SpannableString spannableString;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                    spannableString = new SpannableString(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
                else
                    spannableString = new SpannableString(Html.fromHtml(status.getContent()));
                status.setContentSpan(new SpannableString(spannableString));
                i++;
                statuses.add(status);
            }
        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        return statuses;
    }

    /**
     * Parse json response for unique how to
     *
     * @param resobj JSONObject
     * @return HowToVideo
     */
    private static HowToVideo parseHowTo(JSONObject resobj) {
        HowToVideo howToVideo = new HowToVideo();
        try {
            howToVideo.setId(resobj.getString("id"));
            howToVideo.setUuid(resobj.getString("uuid"));
            howToVideo.setName(resobj.getString("name"));
            howToVideo.setDescription(resobj.getString("description"));
            howToVideo.setEmbedPath(resobj.getString("embedPath"));
            howToVideo.setPreviewPath(resobj.getString("previewPath"));
            howToVideo.setThumbnailPath(resobj.getString("thumbnailPath"));
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return howToVideo;
    }

    /**
     * Parse json response for several scheduled toots
     *
     * @param jsonObject JSONObject
     * @return List<Status>
     */
    private static Schedule parseSimpleSchedule(Context context, JSONObject jsonObject) {
        Schedule schedule = new Schedule();
        try {
            JSONObject resobj = jsonObject.getJSONObject("params");
            Status status = parseSchedule(context, resobj);
            List<Attachment> attachements = parseAttachmentResponse(jsonObject.getJSONArray("media_attachments"));
            status.setMedia_attachments((ArrayList<Attachment>) attachements);
            schedule.setStatus(status);
            schedule.setAttachmentList(attachements);
            schedule.setId(jsonObject.getString("id"));
            schedule.setScheduled_at(Helper.mstStringToDate(jsonObject.getString("scheduled_at")));
        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        return schedule;
    }

    /**
     * Parse json response for several scheduled toots
     *
     * @param jsonArray JSONArray
     * @return List<Status>
     */
    private static List<Schedule> parseSchedule(Context context, JSONArray jsonArray) {

        List<Schedule> schedules = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                Schedule schedule = new Schedule();
                JSONObject resobj = jsonArray.getJSONObject(i).getJSONObject("params");
                Status status = parseSchedule(context, resobj);
                List<Attachment> attachements = parseAttachmentResponse(jsonArray.getJSONObject(i).getJSONArray("media_attachments"));
                status.setMedia_attachments((ArrayList<Attachment>) attachements);
                schedule.setStatus(status);
                schedule.setAttachmentList(attachements);
                schedules.add(schedule);
                schedule.setId(jsonArray.getJSONObject(i).getString("id"));
                schedule.setScheduled_at(Helper.mstStringToDate(jsonArray.getJSONObject(i).getString("scheduled_at")));
                i++;
            }
        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        return schedules;
    }

    /**
     * Parse json response for several status
     *
     * @param jsonArray JSONArray
     * @return List<Status>
     */
    private static List<Status> parseStatuses(Context context, JSONArray jsonArray) {

        List<Status> statuses = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Status status = parseStatuses(context, resobj);
                i++;
                statuses.add(status);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return statuses;
    }

    /**
     * Parse json response for several reactions
     *
     * @param jsonArray JSONArray
     * @return List<Reaction>
     */
    private static List<Reaction> parseReaction(JSONArray jsonArray) {
        List<Reaction> reactions = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Reaction reaction = parseReaction(resobj);
                i++;
                reactions.add(reaction);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return reactions;
    }

    /**
     * Parse a reaction
     *
     * @param resobj JSONObject
     * @return Reaction
     */
    private static Reaction parseReaction(JSONObject resobj) {
        Reaction reaction = new Reaction();
        try {
            reaction.setName(resobj.getString("name"));
            reaction.setCount(resobj.getInt("count"));
            reaction.setMe(resobj.getBoolean("me"));
            if (resobj.has("url")) {
                reaction.setUrl(resobj.getString("url"));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return reaction;
    }

    /**
     * Parse json response for several announcements
     *
     * @param jsonArray JSONArray
     * @return List<Announcement>
     */
    private static List<Announcement> parseAnnouncement(Context context, JSONArray jsonArray) {

        List<Announcement> announcements = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Announcement announcement = parseAnnouncement(context, resobj);
                i++;
                announcements.add(announcement);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
        return announcements;
    }

    /**
     * Parse an annoucement
     *
     * @param context Context
     * @param resobj  JSONObject
     * @return Announcement
     */
    private static Announcement parseAnnouncement(Context context, JSONObject resobj) {
        Announcement announcement = new Announcement();
        try {
            Account account = new Account();
            account.setBot(false);
            account.setUsername("admin");
            account.setUuid("admin");
            account.setId("admin");
            account.setDisplay_name("admin");
            announcement.setMedia_attachments(new ArrayList<>());
            announcement.setAccount(account);
            announcement.setId(resobj.getString("id"));
            announcement.setContent(context, resobj.getString("content"));
            if (resobj.getString("published_at").compareTo("null") != 0) {
                announcement.setCreated_at(Helper.mstStringToDate(resobj.getString("published_at")));
            }
            if (resobj.getString("updated_at").compareTo("null") != 0) {
                announcement.setUpdatedAt(Helper.mstStringToDate(resobj.getString("updated_at")));
            }
            if (resobj.getString("starts_at").compareTo("null") != 0) {
                announcement.setStartAt(Helper.mstStringToDate(resobj.getString("starts_at")));
            }
            if (resobj.getString("ends_at").compareTo("null") != 0) {
                announcement.setEndAt(Helper.mstStringToDate(resobj.getString("ends_at")));
            }


            announcement.setRead(resobj.getBoolean("read"));

            announcement.setReactions(parseReaction(resobj.getJSONArray("reactions")));


            List<Tag> tags = new ArrayList<>();
            JSONArray arrayTag = resobj.getJSONArray("tags");
            for (int j = 0; j < arrayTag.length(); j++) {
                JSONObject tagObj = arrayTag.getJSONObject(j);
                Tag tag = new Tag();
                tag.setName(tagObj.getString("name"));
                tag.setUrl(tagObj.getString("url"));
                tags.add(tag);
            }
            announcement.setTags(tags);

            List<Emojis> emojiList = new ArrayList<>();
            try {
                JSONArray emojisTag = resobj.getJSONArray("emojis");
                for (int j = 0; j < emojisTag.length(); j++) {
                    JSONObject emojisObj = emojisTag.getJSONObject(j);
                    Emojis emojis = parseEmojis(emojisObj);
                    emojiList.add(emojis);
                }
                announcement.setEmojis(emojiList);
            } catch (Exception e) {
                announcement.setEmojis(new ArrayList<>());
            }

            List<Mention> mentions = new ArrayList<>();

            try {
                JSONArray arrayMention = resobj.getJSONArray("mentions");
                for (int j = 0; j < arrayMention.length(); j++) {
                    JSONObject menObj = arrayMention.getJSONObject(j);
                    Mention mention = new Mention();
                    mention.setId(menObj.getString("id"));
                    mention.setUrl(menObj.getString("url"));
                    mention.setAcct(menObj.getString("acct"));
                    mention.setUsername(menObj.getString("username"));
                    mentions.add(mention);
                }
                announcement.setMentions(mentions);
            } catch (Exception e) {
                announcement.setMentions(new ArrayList<>());
            }

        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        Status.fillSpan(new WeakReference<>(context), announcement);
        return announcement;
    }

    /**
     * Parse a poll
     *
     * @param resobj JSONObject
     * @return Poll
     */
    private static Poll parsePoll(JSONObject resobj) {
        Poll poll = new Poll();
        try {
            poll.setId(resobj.getString("id"));
            poll.setExpires_at(Helper.mstStringToDate(resobj.getString("expires_at")));
            poll.setExpired(resobj.getBoolean("expired"));
            poll.setMultiple(resobj.getBoolean("multiple"));
            poll.setVotes_count(resobj.getInt("votes_count"));
            if (resobj.has("voters_count")) {
                poll.setVoters_count(resobj.getInt("voters_count"));
            } else {
                poll.setVoters_count(resobj.getInt("votes_count"));
            }
            List<Emojis> emojiList = new ArrayList<>();
            try {
                JSONArray emojisPoll = resobj.getJSONArray("emojis");
                for (int j = 0; j < emojisPoll.length(); j++) {
                    JSONObject emojisObj = emojisPoll.getJSONObject(j);
                    Emojis emojis = parseEmojis(emojisObj);
                    emojiList.add(emojis);
                }
                poll.setEmojis(emojiList);
            } catch (Exception e) {
                poll.setEmojis(new ArrayList<>());
            }
            poll.setVoted(resobj.getBoolean("voted"));
            JSONArray options = resobj.getJSONArray("options");
            List<PollOptions> pollOptions = new ArrayList<>();
            for (int i = 0; i < options.length(); i++) {
                JSONObject option = options.getJSONObject(i);
                PollOptions pollOption = new PollOptions();
                pollOption.setTitle(option.getString("title"));
                pollOption.setVotes_count(option.getInt("votes_count"));
                pollOptions.add(pollOption);
            }
            poll.setOptionsList(pollOptions);
            JSONArray own_votes = resobj.getJSONArray("own_votes");
            List<Integer> own_votes_list = new ArrayList<>();
            for (int i = 0; i < own_votes.length(); i++) {
                int own_vote = own_votes.getInt(i);
                own_votes_list.add(own_vote);
            }
            poll.setOwn_votes(own_votes_list);
        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        return poll;
    }


    /**
     * Parse json response for unique status
     *
     * @param resobj JSONObject
     * @return Status
     */
    public static Status parseStatuses(Context context, JSONObject resobj) {
        return parseStatuses(context, resobj, true);
    }

    /**
     * Parse json response for unique status
     *
     * @param context   Context
     * @param resobj    JSONObject
     * @param recursive boolean
     * @return Status
     */
    private static Status parseStatuses(Context context, JSONObject resobj, boolean recursive) {
        Status status = new Status();
        try {
            status.setId(resobj.getString("id"));
            if (resobj.has("uri")) {
                status.setUri(resobj.getString("uri"));
            } else {
                status.setUri(resobj.getString("id"));
            }
            if (resobj.has("created_at")) {
                status.setCreated_at(Helper.mstStringToDate(resobj.getString("created_at")));
            } else {
                status.setCreated_at(new Date());
            }

            if (!resobj.isNull("in_reply_to_id")) {
                status.setIn_reply_to_id(resobj.getString("in_reply_to_id"));
            }
            if (!resobj.isNull("in_reply_to_account_id")) {
                status.setIn_reply_to_account_id(resobj.getString("in_reply_to_account_id"));
            }
            if (resobj.has("sensitive")) {
                status.setSensitive(resobj.getBoolean("sensitive"));
            } else {
                status.setSensitive(false);
            }
            if (resobj.has("spoiler_text")) {
                status.setSpoiler_text(resobj.getString("spoiler_text"));
            }
            try {
                status.setVisibility(resobj.getString("visibility"));
            } catch (Exception e) {
                status.setVisibility("public");
            }
            try {
                status.setLanguage(resobj.getString("language"));
            } catch (Exception e) {
                status.setLanguage("ja");
            }

            List<Reaction> reactions = new ArrayList<>();
            if (resobj.has("pleroma") && resobj.getJSONObject("pleroma").has("emoji_reactions")) {
                try {
                    reactions = parseReaction(resobj.getJSONObject("pleroma").getJSONArray("emoji_reactions"));
                } catch (Exception ignored) {
                }
            }
            status.setReactions(reactions);

            if (resobj.has("url")) {
                status.setUrl(resobj.getString("url"));
            }
            ArrayList<Attachment> attachments = new ArrayList<>();
            //Retrieves attachments
            if (resobj.has("media_attachments")) {
                JSONArray arrayAttachement = resobj.getJSONArray("media_attachments");

                for (int j = 0; j < arrayAttachement.length(); j++) {
                    JSONObject attObj = arrayAttachement.getJSONObject(j);
                    Attachment attachment = new Attachment();
                    attachment.setId(attObj.getString("id"));
                    attachment.setPreview_url(attObj.getString("preview_url"));
                    attachment.setRemote_url(attObj.getString("remote_url"));
                    attachment.setType(attObj.getString("type"));
                    attachment.setText_url(attObj.getString("text_url"));
                    attachment.setUrl(attObj.getString("url"));
                    try {
                        attachment.setDescription(attObj.getString("description"));
                    } catch (JSONException ignore) {
                    }
                    attachments.add(attachment);
                }
            }
            try {
                status.setCard(parseCardResponse(resobj.getJSONObject("card")));
            } catch (Exception e) {
                status.setCard(null);
            }

            status.setMedia_attachments(attachments);
            //Retrieves mentions
            List<Mention> mentions = new ArrayList<>();
            if (resobj.has("mentions")) {
                JSONArray arrayMention = resobj.getJSONArray("mentions");
                for (int j = 0; j < arrayMention.length(); j++) {
                    JSONObject menObj = arrayMention.getJSONObject(j);
                    Mention mention = new Mention();
                    mention.setId(menObj.getString("id"));
                    mention.setUrl(menObj.getString("url"));
                    mention.setAcct(menObj.getString("acct"));
                    mention.setUsername(menObj.getString("username"));
                    mentions.add(mention);
                }
            }
            status.setMentions(mentions);
            //Retrieves tags
            List<Tag> tags = new ArrayList<>();
            if (resobj.has("tags")) {
                JSONArray arrayTag = resobj.getJSONArray("tags");
                for (int j = 0; j < arrayTag.length(); j++) {
                    JSONObject tagObj = arrayTag.getJSONObject(j);
                    Tag tag = new Tag();
                    tag.setName(tagObj.getString("name"));
                    tag.setUrl(tagObj.getString("url"));
                    tags.add(tag);
                }
            }
            status.setTags(tags);
            //Retrieves emjis
            List<Emojis> emojiList = new ArrayList<>();
            if (resobj.has("emojis")) {
                try {
                    JSONArray emojisTag = resobj.getJSONArray("emojis");
                    for (int j = 0; j < emojisTag.length(); j++) {
                        JSONObject emojisObj = emojisTag.getJSONObject(j);
                        Emojis emojis = parseEmojis(emojisObj);
                        emojiList.add(emojis);
                    }
                } catch (Exception ignored) {
                }
            }
            status.setEmojis(emojiList);
            //Retrieve Application
            Application application = new Application();
            try {
                resobj.getJSONObject("application");
                application.setName(resobj.getJSONObject("application").getString("name"));
                application.setWebsite(resobj.getJSONObject("application").getString("website"));
            } catch (Exception e) {
                application = new Application();
            }
            status.setApplication(application);

            status.setAccount(parseAccountResponse(resobj.getJSONObject("account")));
            status.setContent(context, resobj.getString("content"));
            if (!resobj.isNull("favourites_count")) {
                status.setFavourites_count(resobj.getInt("favourites_count"));
            } else {
                status.setFavourites_count(0);
            }
            if (!resobj.isNull("reblogs_count")) {
                status.setReblogs_count(resobj.getInt("reblogs_count"));
            } else {
                status.setReblogs_count(0);
            }

            try {
                status.setReplies_count(resobj.getInt("replies_count"));
            } catch (Exception e) {
                status.setReplies_count(-1);
            }
            try {
                status.setReblogged(resobj.getBoolean("reblogged"));
            } catch (Exception e) {
                status.setReblogged(false);
            }
            try {
                status.setFavourited(resobj.getBoolean("favourited"));
            } catch (Exception e) {
                status.setFavourited(false);
            }
            try {
                status.setMuted(resobj.getBoolean("muted"));
            } catch (Exception e) {
                status.setMuted(false);
            }
            try {
                status.setPinned(resobj.getBoolean("pinned"));
            } catch (JSONException e) {
                status.setPinned(false);
            }
            try {
                if (recursive) {
                    status.setReblog(parseStatuses(context, resobj.getJSONObject("reblog"), false));
                }
            } catch (Exception ignored) {
            }

            if (resobj.has("poll") && !resobj.isNull("poll")) {
                Poll poll = parsePoll(resobj.getJSONObject("poll"));
                status.setPoll(poll);
            }

        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        status.setViewType(context);
        Status.fillSpan(new WeakReference<>(context), status);
        return status;
    }

    /**
     * Parse json response for unique schedule
     *
     * @param resobj JSONObject
     * @return Status
     */
    private static Status parseSchedule(Context context, JSONObject resobj) {
        Status status = new Status();
        try {
            status.setIn_reply_to_id(resobj.getString("in_reply_to_id"));
            if (!resobj.isNull("sensitive")) {
                status.setSensitive(resobj.getBoolean("sensitive"));
            } else {
                status.setSensitive(false);
            }
            status.setSpoiler_text(resobj.getString("spoiler_text"));
            try {
                status.setVisibility(resobj.getString("visibility"));
            } catch (Exception e) {
                status.setVisibility("public");
            }
            status.setContent(context, resobj.getString("text"));
        } catch (JSONException ignored) {
        }
        return status;
    }

    /**
     * Parse json response for several notes (Misskey)
     *
     * @param jsonArray JSONArray
     * @return List<Status>
     */
    private static List<Status> parseNotes(Context context, String instance, JSONArray jsonArray) {

        List<Status> statuses = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Status status = parseNotes(context, instance, resobj);
                i++;
                statuses.add(status);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
        return statuses;
    }

    /**
     * Parse json response for unique note (misskey)
     *
     * @param resobj JSONObject
     * @return Status
     */
    private static Status parseNotes(Context context, String instance, JSONObject resobj) {
        Status status = new Status();
        try {

            status.setId(resobj.getString("id"));
            status.setUri("https://" + instance + "/notes/" + resobj.getString("id"));
            status.setCreated_at(Helper.mstStringToDate(resobj.getString("createdAt")));
            status.setIn_reply_to_id(resobj.getString("replyId"));
            status.setSensitive(false);
            resobj.get("cw");
            if (!resobj.getString("cw").equals("null"))
                status.setSpoiler_text(resobj.getString("cw"));
            try {
                status.setVisibility(resobj.getString("visibility"));
            } catch (Exception e) {
                status.setVisibility("public");
                e.printStackTrace();
            }
            status.setUrl("https://" + instance + "/notes/" + resobj.getString("id"));
            //Retrieves attachments
            if (resobj.has("media")) {
                JSONArray arrayAttachement = resobj.getJSONArray("media");
                ArrayList<Attachment> attachments = new ArrayList<>();
                for (int j = 0; j < arrayAttachement.length(); j++) {
                    JSONObject attObj = arrayAttachement.getJSONObject(j);
                    Attachment attachment = new Attachment();
                    attachment.setId(attObj.getString("id"));
                    attachment.setPreview_url(attObj.getString("thumbnailUrl"));
                    attachment.setRemote_url(attObj.getString("url"));
                    if (attObj.getString("type").contains("/")) {
                        attachment.setType(attObj.getString("type").split("/")[0]);
                    } else
                        attachment.setType(attObj.getString("type"));
                    attachment.setText_url(attObj.getString("url"));
                    attachment.setUrl(attObj.getString("url"));
                    if (attObj.getString("isSensitive").equals("true")) {
                        status.setSensitive(true);
                    }
                    try {
                        attachment.setDescription(attObj.getString("comment"));
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    attachments.add(attachment);
                }
                status.setMedia_attachments(attachments);
            } else {
                status.setMedia_attachments(new ArrayList<>());
            }
            try {
                status.setCard(parseCardResponse(resobj.getJSONObject("card")));
            } catch (Exception e) {
                status.setCard(null);
            }


            //Retrieves mentions
            List<Mention> mentions = new ArrayList<>();

            status.setAccount(parseMisskeyAccountResponse(instance, resobj.getJSONObject("user")));

            status.setContent(context, resobj.getString("text"));
            try {
                status.setReplies_count(resobj.getInt("repliesCount"));
            } catch (Exception e) {
                status.setReplies_count(-1);
            }
            try {
                status.setFavourited(resobj.getBoolean("isFavorited"));
            } catch (Exception e) {
                status.setFavourited(false);
            }
            if (resobj.has("bookmarked") && !resobj.isNull("bookmarked")) {
                status.setBookmarked(true);
            } else {
                status.setBookmarked(false);
            }
            try {
                resobj.getJSONObject("renoteId");
                if (!resobj.getJSONObject("renoteId").toString().equals("null"))
                    status.setReblog(parseStatuses(context, resobj.getJSONObject("renote")));
            } catch (Exception ignored) {
            }

            status.setMentions(mentions);
            //Retrieves tags
            List<Tag> tags = new ArrayList<>();
            if (resobj.has("tags")) {
                JSONArray arrayTag = resobj.getJSONArray("tags");
                for (int j = 0; j < arrayTag.length(); j++) {
                    JSONObject tagObj = arrayTag.getJSONObject(j);
                    Tag tag = new Tag();
                    tag.setName(tagObj.getString("name"));
                    tag.setUrl(tagObj.getString("url"));
                    tags.add(tag);
                }
            }
            status.setTags(tags);

            //Retrieves emjis
            List<Emojis> emojiList = new ArrayList<>();
            if (resobj.has("emojis")) {
                JSONArray emojisTag = resobj.getJSONArray("emojis");
                for (int j = 0; j < emojisTag.length(); j++) {
                    JSONObject emojisObj = emojisTag.getJSONObject(j);
                    Emojis emojis = parseMisskeyEmojis(emojisObj);
                    emojiList.add(emojis);
                }
                status.setEmojis(emojiList);
            }
            status.setEmojis(emojiList);
            //Retrieve Application
            Application application = new Application();
            try {
                resobj.getJSONObject("application");
                application.setName(resobj.getJSONObject("application").getString("name"));
                application.setWebsite(resobj.getJSONObject("application").getString("website"));
            } catch (Exception e) {
                application = new Application();
            }
            status.setApplication(application);


        } catch (JSONException ignored) {
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Status.fillSpan(new WeakReference<>(context), status);
        return status;
    }

    /**
     * Parse json response for emoji
     *
     * @param resobj JSONObject
     * @return Emojis
     */
    private static Emojis parseEmojis(JSONObject resobj) {
        Emojis emojis = new Emojis();
        try {
            emojis.setShortcode(resobj.getString("shortcode"));
            emojis.setStatic_url(resobj.getString("static_url"));
            emojis.setUrl(resobj.getString("url"));
            try {
                emojis.setVisible_in_picker((resobj.getBoolean("visible_in_picker")));
            } catch (Exception e) {
                emojis.setVisible_in_picker(true);
            }
        } catch (Exception ignored) {
        }
        return emojis;
    }

    /**
     * Parse json response for emoji
     *
     * @param resobj JSONObject
     * @return Emojis
     */
    private static Emojis parseMisskeyEmojis(JSONObject resobj) {
        Emojis emojis = new Emojis();
        try {
            emojis.setShortcode(resobj.getString("name"));
            emojis.setStatic_url(resobj.getString("url"));
            emojis.setUrl(resobj.getString("url"));
        } catch (Exception ignored) {
        }
        return emojis;
    }

    /**
     * Parse json response for emoji
     *
     * @param resobj JSONObject
     * @return Emojis
     */
    private static app.fedilab.android.client.Entities.List parseList(JSONObject resobj) {
        app.fedilab.android.client.Entities.List list = new app.fedilab.android.client.Entities.List();
        try {
            list.setId(resobj.getString("id"));
            list.setTitle(resobj.getString("title"));
        } catch (Exception ignored) {
        }
        return list;
    }

    /**
     * Parse json response an unique peertube account
     *
     * @param resobj JSONObject
     * @return Account
     */
    private static Account parseAccountResponsePeertube(String instance, JSONObject resobj) {
        Account account = new Account();
        try {
            account.setId(resobj.getString("id"));
            account.setUsername(resobj.getString("name"));
            account.setAcct(resobj.getString("name") + "@" + resobj.getString("host"));
            account.setDisplay_name(resobj.getString("displayName"));
            account.setHost(resobj.getString("host"));
            if (resobj.has("createdAt"))
                account.setCreated_at(Helper.mstStringToDate(resobj.getString("createdAt")));
            else
                account.setCreated_at(new Date());

            if (resobj.has("followersCount"))
                account.setFollowers_count(resobj.getInt("followersCount"));
            else
                account.setFollowers_count(0);
            if (resobj.has("followingCount"))
                account.setFollowing_count(resobj.getInt("followingCount"));
            else
                account.setFollowing_count(0);
            account.setStatuses_count(0);
            if (resobj.has("description"))
                account.setNote(resobj.getString("description"));
            else
                account.setNote("");
            account.setUrl(resobj.getString("url"));
            account.setSocial("PEERTUBE");
            if (resobj.has("avatar") && !resobj.getString("avatar").equals("null")) {
                account.setAvatar("https://" + instance + resobj.getJSONObject("avatar").get("path"));
                account.setAvatar_static("https://" + instance + resobj.getJSONObject("avatar").get("path"));
            } else
                account.setAvatar(null);
            account.setAvatar_static(resobj.getString("avatar"));
        } catch (JSONException ignored) {
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return account;
    }

    /**
     * Parse json response an unique report for admins
     *
     * @param resobj JSONObject
     * @return AccountAdmin
     */
    private static Report parseReportAdminResponse(Context context, JSONObject resobj) {

        Report report = new Report();
        try {
            report.setId(resobj.getString("id"));
            if (!resobj.isNull("action_taken")) {
                report.setAction_taken(resobj.getBoolean("action_taken"));
            } else if (!resobj.isNull("state")) {
                report.setAction_taken(!resobj.getString("state").equals("open"));
            }
            if (!resobj.isNull("comment")) {
                report.setComment(resobj.getString("comment"));
            } else if (!resobj.isNull("content")) {
                report.setComment(resobj.getString("content"));
            }


            report.setCreated_at(Helper.mstStringToDate(resobj.getString("created_at")));
            if (!resobj.isNull("updated_at")) {
                report.setUpdated_at(Helper.mstStringToDate(resobj.getString("updated_at")));
            }
            if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                if (!resobj.isNull("account")) {
                    report.setAccount(parseAccountAdminResponse(resobj.getJSONObject("account")));
                }
                if (!resobj.isNull("target_account")) {
                    report.setTarget_account(parseAccountAdminResponse(resobj.getJSONObject("target_account")));
                }
                if (!resobj.isNull("assigned_account")) {
                    report.setAssigned_account(parseAccountAdminResponse(resobj.getJSONObject("assigned_account")));
                }
            } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {

                if (!resobj.isNull("account")) {
                    Account account = parseAccountResponse(resobj.getJSONObject("account"));
                    AccountAdmin accountAdmin = new AccountAdmin();
                    accountAdmin.setId(account.getId());
                    accountAdmin.setUsername(account.getAcct());
                    accountAdmin.setAccount(account);
                    report.setTarget_account(accountAdmin);
                }

                if (!resobj.isNull("actor")) {
                    Account account = parseAccountResponse(resobj.getJSONObject("actor"));
                    AccountAdmin accountAdmin = new AccountAdmin();
                    accountAdmin.setId(account.getId());
                    accountAdmin.setUsername(account.getAcct());
                    accountAdmin.setAccount(account);
                    report.setAccount(accountAdmin);
                }

            }

            if (!resobj.isNull("action_taken_by_account")) {
                report.setAction_taken_by_account(parseAccountAdminResponse(resobj.getJSONObject("action_taken_by_account")));
            }
            report.setStatuses(parseStatuses(context, resobj.getJSONArray("statuses")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return report;
    }

    /**
     * Parse json response an unique account for admins
     *
     * @param resobj JSONObject
     * @return Account
     */
    private static AccountAdmin parseAccountAdminResponse(JSONObject resobj) {

        AccountAdmin accountAdmin = new AccountAdmin();
        try {
            accountAdmin.setId(resobj.getString("id"));
            if (!resobj.isNull("username")) {
                accountAdmin.setUsername(resobj.getString("username"));
            }
            if (!resobj.isNull("nickname")) {
                accountAdmin.setUsername(resobj.getString("nickname"));
            }
            if (!resobj.isNull("created_at")) {
                accountAdmin.setCreated_at(Helper.mstStringToDate(resobj.getString("created_at")));
            }
            if (!resobj.isNull("email")) {
                accountAdmin.setEmail(resobj.getString("email"));
            }
            if (!resobj.isNull("role")) {
                accountAdmin.setRole(resobj.getString("role"));
            }
            if (!resobj.isNull("roles")) {
                if (resobj.getJSONObject("roles").getBoolean("admin")) {
                    accountAdmin.setRole("admin");
                } else if (resobj.getJSONObject("roles").getBoolean("moderator")) {
                    accountAdmin.setRole("moderator");
                } else {
                    accountAdmin.setRole("user");
                }
            }
            if (!resobj.isNull("ip")) {
                accountAdmin.setIp(resobj.getString("ip"));
            }
            if (!resobj.isNull("domain")) {
                accountAdmin.setDomain(resobj.getString("domain"));
            }

            if (!resobj.isNull("account")) {
                accountAdmin.setAccount(parseAccountResponse(resobj.getJSONObject("account")));
            } else {
                Account account = new Account();
                account.setId(accountAdmin.getId());
                account.setAcct(accountAdmin.getUsername());
                account.setDisplay_name(accountAdmin.getUsername());
                accountAdmin.setAccount(account);
            }
            if (!resobj.isNull("confirmed")) {
                accountAdmin.setConfirmed(resobj.getBoolean("confirmed"));
            } else {
                accountAdmin.setConfirmed(true);
            }
            if (!resobj.isNull("suspended")) {
                accountAdmin.setSuspended(resobj.getBoolean("suspended"));
            } else {
                accountAdmin.setSuspended(false);
            }
            if (!resobj.isNull("silenced")) {
                accountAdmin.setSilenced(resobj.getBoolean("silenced"));
            } else {
                accountAdmin.setSilenced(false);
            }
            if (!resobj.isNull("disabled")) {
                accountAdmin.setDisabled(resobj.getBoolean("disabled"));
            } else {
                if (!resobj.isNull("deactivated")) {
                    accountAdmin.setDisabled(resobj.getBoolean("deactivated"));
                } else {
                    accountAdmin.setDisabled(false);
                }
            }

            if (!resobj.isNull("approved")) {
                accountAdmin.setApproved(resobj.getBoolean("approved"));
            } else {
                accountAdmin.setApproved(true);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return accountAdmin;
    }


    /**
     * Parse json response an unique account
     *
     * @param resobj JSONObject
     * @return Account
     */
    private static Account parseAccountResponse(JSONObject resobj) {
        return parseAccountResponse(resobj, true);
    }

    /**
     * Parse json response an unique account
     *
     * @param resobj    JSONObject
     * @param recursive boolean
     * @return Account
     */
    private static Account parseAccountResponse(JSONObject resobj, boolean recursive) {

        Account account = new Account();
        try {
            account.setId(resobj.getString("id"));
            account.setUuid(resobj.getString("id"));
            account.setUsername(resobj.getString("username"));
            account.setAcct(resobj.getString("acct"));
            account.setDisplay_name(resobj.getString("display_name"));
            if (account.getDisplay_name() == null || account.getDisplay_name().compareTo("") == 0) {
                account.setDisplay_name(resobj.getString("username"));
            }
            account.setLocked(resobj.getBoolean("locked"));
            if (resobj.has("created_at") && !resobj.isNull("created_at")) {
                account.setCreated_at(Helper.mstStringToDate(resobj.getString("created_at")));
            } else {
                account.setCreated_at(new Date());
            }
            if (!resobj.isNull("followers_count")) {
                account.setFollowers_count(resobj.getInt("followers_count"));
            } else {
                account.setFollowers_count(0);
            }
            if (!resobj.isNull("following_count")) {
                account.setFollowing_count(resobj.getInt("following_count"));
            } else {
                account.setFollowing_count(0);
            }
            if (!resobj.isNull("statuses_count")) {
                account.setStatuses_count(resobj.getInt("statuses_count"));
            } else {
                account.setStatuses_count(0);
            }
            account.setNote(resobj.getString("note"));
            try {
                account.setBot(resobj.getBoolean("bot"));
            } catch (Exception e) {
                account.setBot(false);
            }
            try {
                if (recursive) {
                    account.setMoved_to_account(parseAccountResponse(resobj.getJSONObject("moved"), false));
                }
            } catch (Exception ignored) {
                account.setMoved_to_account(null);
            }
            account.setUrl(resobj.getString("url"));
            account.setAvatar(resobj.getString("avatar"));
            account.setAvatar_static(resobj.getString("avatar_static"));
            account.setHeader(resobj.getString("header"));
            account.setHeader_static(resobj.getString("header_static"));
            try {
                if (resobj.has("pleroma")) {
                    account.setSocial("PLEROMA");
                    try {
                        account.setModerator(resobj.getJSONObject("pleroma").getBoolean("is_moderator"));
                        account.setAdmin(resobj.getJSONObject("pleroma").getBoolean("is_admin"));
                    } catch (Exception ignored) {
                        account.setModerator(false);
                        account.setAdmin(false);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            try {
                if (resobj.has("fields") && resobj.get("fields") instanceof JSONArray) {
                    JSONArray fields = resobj.getJSONArray("fields");
                    LinkedHashMap<String, String> fieldsMap = new LinkedHashMap<>();
                    LinkedHashMap<String, Boolean> fieldsMapVerified = new LinkedHashMap<>();
                    for (int j = 0; j < fields.length(); j++) {
                        fieldsMap.put(fields.getJSONObject(j).getString("name"), fields.getJSONObject(j).getString("value"));
                        try {
                            fields.getJSONObject(j).getString("verified_at");
                            fieldsMapVerified.put(fields.getJSONObject(j).getString("name"), !fields.getJSONObject(j).getString("verified_at").equals("null"));
                        } catch (Exception e) {
                            fieldsMapVerified.put(fields.getJSONObject(j).getString("name"), false);
                        }

                    }
                    account.setFields(fieldsMap);
                    account.setFieldsVerified(fieldsMapVerified);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }


            //Retrieves emjis
            List<Emojis> emojiList = new ArrayList<>();
            try {
                JSONArray emojisTag = resobj.getJSONArray("emojis");
                for (int j = 0; j < emojisTag.length(); j++) {
                    JSONObject emojisObj = emojisTag.getJSONObject(j);
                    Emojis emojis = parseEmojis(emojisObj);
                    emojiList.add(emojis);
                }
                account.setEmojis(emojiList);
            } catch (Exception e) {
                account.setEmojis(new ArrayList<>());
                e.printStackTrace();
            }
            if (resobj.has("source")) {
                JSONObject source = resobj.getJSONObject("source");
                try {
                    if (source.has("privacy")) {
                        account.setPrivacy(source.getString("privacy"));
                    } else {
                        account.setPrivacy("public");
                    }
                    if (source.has("sensitive")) {
                        account.setSensitive(source.getBoolean("sensitive"));
                    } else {
                        account.setSensitive(false);
                    }

                } catch (Exception e) {
                    account.setPrivacy("public");
                    account.setSensitive(false);
                    e.printStackTrace();
                }
            }
        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        return account;
    }

    /**
     * Parse json response an unique account
     *
     * @param resobj JSONObject
     * @return Account
     */
    private static Account parseMisskeyAccountResponse(String instance, JSONObject resobj) {

        Account account = new Account();
        try {
            account.setId(resobj.getString("id"));
            account.setUsername(resobj.getString("username"));
            String host;
            String acct;
            if (resobj.isNull("host")) {
                acct = resobj.getString("username");
            } else {
                host = resobj.getString("host");
                acct = resobj.getString("username") + "@" + host;
            }
            account.setAcct(acct);
            account.setDisplay_name(resobj.getString("name"));
            account.setCreated_at(new Date());

            account.setUrl("https://" + instance + "/@" + account.getUsername());
            account.setAvatar(resobj.getString("avatarUrl"));
            account.setAvatar_static(resobj.getString("avatarUrl"));
            try {
                account.setBot(resobj.getBoolean("isBot"));
            } catch (Exception e) {
                account.setBot(false);
            }
            //Retrieves emjis
            List<Emojis> emojiList = new ArrayList<>();
            if (resobj.has("emojis")) {
                JSONArray emojisTag = resobj.getJSONArray("emojis");
                for (int j = 0; j < emojisTag.length(); j++) {
                    JSONObject emojisObj = emojisTag.getJSONObject(j);
                    Emojis emojis = parseEmojis(emojisObj);
                    emojiList.add(emojis);
                }
            }
            account.setEmojis(emojiList);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return account;
    }

    /**
     * Parse json response for list of relationship
     *
     * @param jsonArray JSONArray
     * @return List<Relationship>
     */
    private static List<Attachment> parseAttachmentResponse(JSONArray jsonArray) {

        List<Attachment> attachments = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Attachment attachment = parseAttachmentResponse(resobj);
                attachments.add(attachment);
                i++;
            }
        } catch (JSONException ignored) {
        }
        return attachments;
    }

    /**
     * Parse json response an unique attachment
     *
     * @param resobj JSONObject
     * @return Relationship
     */
    public static Attachment parseAttachmentResponse(JSONObject resobj) {

        Attachment attachment = new Attachment();
        try {
            attachment.setId(resobj.getString("id"));
            attachment.setType(resobj.getString("type"));
            attachment.setUrl(resobj.getString("url"));
            try {
                attachment.setDescription(resobj.getString("description"));
            } catch (JSONException ignore) {
            }
            try {
                attachment.setRemote_url(resobj.getString("remote_url"));
            } catch (JSONException ignore) {
            }
            try {
                attachment.setPreview_url(resobj.getString("preview_url"));
            } catch (JSONException ignore) {
            }
            try {
                attachment.setMeta(resobj.getString("meta"));
            } catch (JSONException ignore) {
            }
            try {
                attachment.setText_url(resobj.getString("text_url"));
            } catch (JSONException ignore) {
            }

        } catch (JSONException ignored) {
        }
        return attachment;
    }

    /**
     * Parse json response an unique notification
     *
     * @param resobj JSONObject
     * @return Account
     */
    public static Notification parseNotificationResponse(Context context, JSONObject resobj) {


        Notification notification = new Notification();
        try {
            notification.setId(resobj.getString("id"));
            notification.setType(resobj.getString("type"));
            notification.setCreated_at(Helper.mstStringToDate(resobj.getString("created_at")));
            notification.setAccount(parseAccountResponse(resobj.getJSONObject("account")));
            if (resobj.has("status")) {
                try {
                    notification.setStatus(parseStatuses(context, resobj.getJSONObject("status"), false));
                } catch (Exception ignored) {
                }
            }
            notification.setCreated_at(Helper.mstStringToDate(resobj.getString("created_at")));
        } catch (JSONException ignored) {
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return notification;
    }

    /**
     * Parse xml response for Nitter
     *
     * @param xml String
     * @return List<Status>
     */

    private List<Status> parseNitter(String xml) {
        final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        String nitterHost = sharedpreferences.getString(Helper.SET_NITTER_HOST, Helper.DEFAULT_NITTER_HOST).toLowerCase();
        List<Status> statuses = new ArrayList<>();
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XmlPullParser xpp = factory.newPullParser();

            xpp.setInput(new StringReader(xml));
            int eventType = xpp.getEventType();
            Account account = null;
            Status status = null;
            HashMap<String, String> mappedProfile = new HashMap<>();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    if (xpp.getName().compareTo("item") == 0) {
                        status = new Status();
                        status.setReplies_count(0);
                        status.setFavourites_count(0);
                        status.setReblogs_count(0);
                        status.setFavourited(false);
                        status.setReblogged(false);
                        status.setEmojiFound(true);
                        status.setPollEmojiFound(true);
                        status.setEmojiTranslateFound(true);
                        status.setMedia_attachments(new ArrayList<>());
                        account = new Account();
                    } else if (xpp.getName().compareTo("creator") == 0) {
                        eventType = xpp.next();
                        if (eventType == XmlPullParser.TEXT) {
                            if (account != null) {
                                account.setAcct(xpp.getText().replace("@", "") + "@" + nitterHost);
                                account.setDisplay_name(xpp.getText().replace("@", ""));
                                account.setUsername(xpp.getText().replace("@", ""));
                                account.setId("https://" + nitterHost + "/" + xpp.getText());
                                account.setUuid("https://" + nitterHost + "/" + xpp.getText());
                                account.setUrl("https://" + nitterHost + "/" + xpp.getText());
                                if (!mappedProfile.containsKey(xpp.getText())) {
                                    HttpsConnection httpsConnection = new HttpsConnection(context, nitterHost);
                                    try {
                                        String response = httpsConnection.get("https://" + nitterHost + "/" + xpp.getText() + "/rss", 10, null, null);
                                        XmlPullParserFactory factory2 = XmlPullParserFactory.newInstance();
                                        factory2.setNamespaceAware(true);
                                        XmlPullParser xpp2 = factory2.newPullParser();

                                        xpp2.setInput(new StringReader(response));
                                        int eventType2 = xpp2.getEventType();
                                        while (eventType2 != XmlPullParser.END_DOCUMENT) {
                                            if (eventType2 == XmlPullParser.START_TAG) {
                                                if (xpp2.getName().compareTo("url") == 0) {
                                                    eventType2 = xpp2.next();
                                                    if (eventType2 == XmlPullParser.TEXT) {
                                                        mappedProfile.put(xpp.getText(), xpp2.getText());
                                                    }
                                                    break;
                                                }
                                            }
                                            eventType2 = xpp2.next();
                                        }
                                    } catch (NoSuchAlgorithmException | KeyManagementException | HttpsConnection.HttpsConnectionException e) {
                                        e.printStackTrace();
                                    }
                                }
                                account.setAvatar(mappedProfile.get(xpp.getText()));
                                account.setAvatar_static(mappedProfile.get(xpp.getText()));
                            }
                        }
                    } else if (xpp.getName().compareTo("pubDate") == 0) {
                        eventType = xpp.next();
                        if (eventType == XmlPullParser.TEXT && status != null) {
                            if (xpp.getText() != null) {
                                try {
                                    DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
                                    Date date = formatter.parse(xpp.getText());
                                    status.setCreated_at(date);
                                } catch (ParseException e) {
                                    status.setCreated_at(new Date());
                                }
                            }
                        }
                    } else if (xpp.getName().compareTo("description") == 0) {
                        eventType = xpp.next();
                        if (eventType == XmlPullParser.TEXT && status != null) {
                            if (xpp.getText() != null) {
                                String description = xpp.getText();
                                Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
                                Matcher matcher = imgPattern.matcher(description);
                                ArrayList<Attachment> attachments = new ArrayList<>();
                                while (matcher.find()) {
                                    description = description.replaceAll(Pattern.quote(matcher.group()), "");
                                    Attachment attachment = new Attachment();
                                    attachment.setType("image");
                                    attachment.setDescription("");
                                    attachment.setUrl(matcher.group(1));
                                    attachment.setPreview_url(matcher.group(1));
                                    attachment.setId(matcher.group(1));
                                    attachments.add(attachment);
                                }
                                status.setMedia_attachments(attachments);
                                status.setContent(context, description);
                            }
                        }
                    } else if (xpp.getName().compareTo("guid") == 0) {
                        eventType = xpp.next();
                        if (eventType == XmlPullParser.TEXT && status != null) {
                            if (xpp.getText() != null) {
                                status.setUri(xpp.getText());
                                Pattern idPattern = Pattern.compile("([0-9])+");
                                Matcher matcher = idPattern.matcher(xpp.getText());
                                while (matcher.find()) {
                                    status.setId(matcher.group(0));
                                }
                            }
                        }
                    } else if (xpp.getName().compareTo("link") == 0) {
                        eventType = xpp.next();
                        if (eventType == XmlPullParser.TEXT && status != null) {
                            if (xpp.getText() != null) {
                                status.setUrl(xpp.getText());
                            }
                        }
                    }
                } else if (eventType == XmlPullParser.END_TAG) {
                    if (xpp.getName().compareTo("item") == 0) {
                        if (status != null) {
                            status.setAccount(account);
                            Status.fillSpan(new WeakReference<>(context), status);
                            statuses.add(status);
                        }
                        account = null;
                        status = null;
                    }
                }
                eventType = xpp.next();
            }
        } catch (XmlPullParserException | IOException e) {
            e.printStackTrace();
        }
        return statuses;
    }

    private List<IdentityProof> parseIdentityProof(JSONArray jsonArray) {
        List<IdentityProof> identityProofs = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                IdentityProof identityProof = parseIdentityProof(resobj);
                i++;
                identityProofs.add(identityProof);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
        return identityProofs;
    }

    private IdentityProof parseIdentityProof(JSONObject jsonObject) {
        IdentityProof identityProof = new IdentityProof();
        try {
            identityProof.setProfile_url(jsonObject.getString("profile_url"));
            identityProof.setProof_url(jsonObject.getString("proof_url"));
            identityProof.setProvider(jsonObject.getString("provider"));
            identityProof.setProvider_username(jsonObject.getString("provider_username"));
            identityProof.setUpdated_at(Helper.mstStringToDate(jsonObject.getString("updated_at")));

        } catch (JSONException | ParseException e) {
            e.printStackTrace();
        }
        return identityProof;
    }

    /**
     * Execute admin get actions
     *
     * @param action type of the action
     * @param id     String can for an account or a status
     * @return APIResponse
     */
    public APIResponse adminGet(adminAction action, String id, AdminAction adminAction) {
        apiResponse = new APIResponse();
        HashMap<String, String> params = null;
        String endpoint = null;
        String url_action = null;
        switch (action) {
            case GET_ACCOUNTS:
                params = new HashMap<>();
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    if (adminAction.isLocal())
                        params.put("local", String.valueOf(adminAction.isLocal()));
                    if (adminAction.isRemote())
                        params.put("remote", String.valueOf(adminAction.isRemote()));
                    if (adminAction.isActive())
                        params.put("active", String.valueOf(adminAction.isActive()));
                    if (adminAction.isPending())
                        params.put("pending", String.valueOf(adminAction.isPending()));
                    if (adminAction.isDisabled())
                        params.put("disabled", String.valueOf(adminAction.isDisabled()));
                    if (adminAction.isSilenced())
                        params.put("silenced", String.valueOf(adminAction.isSilenced()));
                    if (adminAction.isSuspended())
                        params.put("suspended", String.valueOf(adminAction.isSuspended()));
                    endpoint = "/admin/accounts";
                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    if (adminAction.isLocal())
                        params.put("filters", "local");
                    if (adminAction.isRemote())
                        params.put("filters", "external");
                    if (adminAction.isActive())
                        params.put("filters", "active");
                    if (adminAction.isDisabled())
                        params.put("filters", "deactivated");
                    endpoint = "/admin/users";
                }
                break;
            case GET_ONE_ACCOUNT:
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    endpoint = String.format("/admin/accounts/%s", id);
                } else {
                    try {
                        id = URLEncoder.encode(id, "UTF-8");
                    } catch (UnsupportedEncodingException ignored) {
                    }
                    params = new HashMap<>();
                    params.put("query", id);
                    endpoint = "/admin/users";

                }
                break;
            case GET_REPORTS:
                endpoint = "/admin/reports";
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    if (!adminAction.isUnresolved()) {
                        params = new HashMap<>();
                        params.put("resolved", "present");
                    }
                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    if (adminAction.isUnresolved()) {
                        params = new HashMap<>();
                        params.put("state", "open");
                    } else {
                        params = new HashMap<>();
                        params.put("state", "resolved");
                    }
                }
                break;
            case GET_ONE_REPORT:
                endpoint = String.format("/admin/reports/%s", id);
                break;
        }
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
            url_action = getAbsoluteUrl(endpoint);

        } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
            url_action = Helper.instanceWithProtocol(this.context, this.instance) + "/api/pleroma" + endpoint;
        }
        if (url_action == null) {
            apiResponse = new APIResponse();
            APIError = new Error();
            APIError.setError(context.getString(R.string.toast_error));
            apiResponse.setError(APIError);
            return apiResponse;
        }
        try {
            String response = new HttpsConnection(context, this.instance).get(url_action, 10, params, prefKeyOauthTokenT);
            if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                if (new JSONObject(response).has("users")) {
                    response = new JSONArray(new JSONObject(response).getJSONArray("users").toString()).toString();
                } else if (new JSONObject(response).has("reports")) {
                    response = new JSONArray(new JSONObject(response).getJSONArray("reports").toString()).toString();
                }
            }
            switch (action) {
                case GET_ACCOUNTS:
                    List<AccountAdmin> accountAdmins = parseAccountAdminResponse(new JSONArray(response));
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
                case GET_ONE_ACCOUNT:

                    if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                        accountAdmins = parseAccountAdminResponse(new JSONArray(response));
                        if (accountAdmins.size() > 0) {
                            Account accountpleroma = getAccount(accountAdmins.get(0).getId());
                            if (accountpleroma != null) {
                                accountAdmins.get(0).setAccount(accountpleroma);
                            }
                        }
                    } else {
                        AccountAdmin accountAdmin = parseAccountAdminResponse(new JSONObject(response));
                        accountAdmins = new ArrayList<>();
                        accountAdmins.add(accountAdmin);
                    }
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
                case GET_REPORTS:
                    List<Report> reports = parseReportAdminResponse(new JSONArray(response));
                    apiResponse.setReports(reports);
                    break;
                case GET_ONE_REPORT:
                    reports = new ArrayList<>();
                    Report report = parseReportAdminResponse(context, new JSONObject(response));
                    reports.add(report);
                    apiResponse.setReports(reports);
                    break;
            }
        } catch (IOException | NoSuchAlgorithmException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Execute admin post actions
     *
     * @param action type of the action
     * @param id     String can for an account or a status
     * @return APIResponse
     */
    public APIResponse adminDo(adminAction action, String id, AdminAction adminAction) {
        apiResponse = new APIResponse();
        String http_action = "POST";
        String endpoint = null;
        String url_action = null;
        HashMap<String, String> params = null;
        switch (action) {
            case ENABLE:
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    endpoint = String.format("/admin/accounts/%s/enable", id);
                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    http_action = "PATCH";
                    endpoint = String.format("/admin/users/%s/toggle_activation", id);
                }
                break;
            case APPROVE:
                endpoint = String.format("/admin/accounts/%s/approve", id);
                break;
            case REJECT:
                endpoint = String.format("/admin/accounts/%s/reject", id);
                break;
            case UNSILENCE:
                endpoint = String.format("/admin/accounts/%s/unsilence", id);
                break;
            case UNSUSPEND:
                endpoint = String.format("/admin/accounts/%s/unsuspend", id);
                break;
            case ASSIGN_TO_SELF:
                endpoint = String.format("/admin/reports/%s/assign_to_self", id);
                break;
            case UNASSIGN:
                endpoint = String.format("/admin/reports/%s/unassign", id);
                break;
            case REOPEN:
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    endpoint = String.format("/admin/reports/%s/reopen", id);
                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    endpoint = String.format("/admin/reports/%s", id);
                    params = new HashMap<>();
                    params.put("state", "open");
                    http_action = "PUT";
                }
                break;
            case RESOLVE:
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    endpoint = String.format("/admin/reports/%s/resolve", id);
                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    endpoint = String.format("/admin/reports/%s", id);
                    params = new HashMap<>();
                    params.put("state", "resolved");
                    http_action = "PUT";
                }
                break;
            case NONE:
                params = new HashMap<>();
                params.put("type", "none");
                endpoint = String.format("/admin/accounts/%s/action", id);
                params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
                if (adminAction.getText() != null) {
                    params.put("text", adminAction.getText());
                }
                break;
            case DISABLE:

                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    params = new HashMap<>();
                    params.put("type", "disable");
                    endpoint = String.format("/admin/accounts/%s/action", id);
                    params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
                    if (adminAction.getText() != null) {
                        params.put("text", adminAction.getText());
                    }

                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    http_action = "PATCH";
                    endpoint = String.format("/admin/users/%s/toggle_activation", id);
                }
                break;
            case SILENCE:

                params = new HashMap<>();
                params.put("type", "silence");
                endpoint = String.format("/admin/accounts/%s/action", id);
                params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
                if (adminAction.getText() != null) {
                    params.put("text", adminAction.getText());
                }
                break;
            case SUSPEND:
                if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
                    params = new HashMap<>();
                    params.put("type", "suspend");
                    endpoint = String.format("/admin/accounts/%s/action", id);
                    params.put("send_email_notification", String.valueOf(adminAction.isSend_email_notification()));
                    if (adminAction.getText() != null) {
                        params.put("text", adminAction.getText());
                    }
                } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
                    http_action = "DELETE";
                    endpoint = "/admin/users";
                    params = new HashMap<>();
                    params.put("nickname", id);
                }
                break;
        }
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) {
            url_action = getAbsoluteUrl(endpoint);

        } else if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
            url_action = Helper.instanceWithProtocol(this.context, this.instance) + "/api/pleroma" + endpoint;
        }
        try {
            String response = null;
            switch (http_action) {
                case "POST":
                    assert url_action != null;
                    response = new HttpsConnection(context, this.instance).post(url_action, 10, params, prefKeyOauthTokenT);
                    break;
                case "PATCH":
                    assert url_action != null;
                    response = new HttpsConnection(context, this.instance).patch(url_action, 10, null, null, null, null, null, prefKeyOauthTokenT);
                    break;
                case "PUT":
                    assert url_action != null;
                    response = new HttpsConnection(context, this.instance).put(url_action, 10, params, prefKeyOauthTokenT);
                    break;
                case "DELETE":
                    assert url_action != null;
                    new HttpsConnection(context, this.instance).delete(url_action, 10, params, prefKeyOauthTokenT);
                    break;
            }
            switch (action) {
                case ENABLE:
                case APPROVE:
                case REJECT:
                case UNSILENCE:
                    // case UNDISABLE:
                case UNSUSPEND:
                    List<AccountAdmin> accountAdmins = null;
                    try {
                        AccountAdmin accountAdmin = parseAccountAdminResponse(new JSONObject(response));
                        accountAdmin.setAction(action);
                        accountAdmins = new ArrayList<>();
                        accountAdmins.add(accountAdmin);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
                case ASSIGN_TO_SELF:
                case UNASSIGN:
                case REOPEN:
                case RESOLVE:
                    List<Report> reports = null;
                    Report report;
                    try {
                        reports = new ArrayList<>();
                        report = parseReportAdminResponse(context, new JSONObject(response));
                        reports.add(report);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    apiResponse.setReports(reports);

                    break;
                case NONE:
                    accountAdmins = new ArrayList<>();
                    AccountAdmin accountAdmin = new AccountAdmin();
                    accountAdmin.setAction(action);
                    accountAdmins.add(accountAdmin);
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
                case DISABLE:
                    accountAdmins = new ArrayList<>();
                    accountAdmin = new AccountAdmin();
                    accountAdmin.setDisabled(true);
                    accountAdmin.setAction(action);
                    accountAdmins.add(accountAdmin);
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
                case SILENCE:
                    accountAdmins = new ArrayList<>();
                    accountAdmin = new AccountAdmin();
                    accountAdmin.setSilenced(true);
                    accountAdmin.setAction(action);
                    accountAdmins.add(accountAdmin);
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
                case SUSPEND:
                    accountAdmins = new ArrayList<>();
                    accountAdmin = new AccountAdmin();
                    accountAdmin.setSuspended(true);
                    accountAdmin.setAction(action);
                    accountAdmins.add(accountAdmin);
                    apiResponse.setAccountAdmins(accountAdmins);
                    break;
            }
        } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        }
        return apiResponse;
    }

    public InstanceNodeInfo displayNodeInfo(String domain) {

        if (domain == null) {
            return null;
        }
        String response;
        InstanceNodeInfo instanceNodeInfo = new InstanceNodeInfo();
        if (domain.startsWith("http://")) {
            domain = domain.replace("http://", "");
        }
        if (domain.startsWith("https://")) {
            domain = domain.replace("https://", "");
        }
        try {
            response = new HttpsConnection(context, domain).get("https://" + domain + "/.well-known/nodeinfo", 30, null, null);
            JSONArray jsonArray = new JSONObject(response).getJSONArray("links");
            ArrayList<NodeInfo> nodeInfos = new ArrayList<>();
            try {
                int i = 0;
                while (i < jsonArray.length()) {

                    JSONObject resobj = jsonArray.getJSONObject(i);
                    NodeInfo nodeInfo = new NodeInfo();
                    nodeInfo.setHref(resobj.getString("href"));
                    nodeInfo.setRel(resobj.getString("rel"));
                    i++;
                    nodeInfos.add(nodeInfo);
                }
                if (nodeInfos.size() > 0) {
                    NodeInfo nodeInfo = nodeInfos.get(nodeInfos.size() - 1);
                    response = new HttpsConnection(context, this.instance).get(nodeInfo.getHref(), 30, null, null);
                    JSONObject resobj = new JSONObject(response);
                    JSONObject jsonObject = resobj.getJSONObject("software");
                    String name = jsonObject.getString("name").toUpperCase();
                    if (name.compareTo("CORGIDON") == 0) {
                        name = "MASTODON";
                    }
                    instanceNodeInfo.setName(name);
                    instanceNodeInfo.setVersion(jsonObject.getString("version"));
                    instanceNodeInfo.setOpenRegistrations(resobj.getBoolean("openRegistrations"));
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        } catch (IOException | JSONException | NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
            try {
                response = new HttpsConnection(context, this.instance).get("https://" + domain + "/api/v1/instance", 30, null, null);
                JSONObject jsonObject = new JSONObject(response);
                instanceNodeInfo.setName("MASTODON");
                instanceNodeInfo.setVersion(jsonObject.getString("version"));
                instanceNodeInfo.setOpenRegistrations(true);
            } catch (IOException e1) {
                instanceNodeInfo.setConnectionError(true);
                e1.printStackTrace();
            } catch (NoSuchAlgorithmException | JSONException | KeyManagementException e1) {
                e1.printStackTrace();
            } catch (HttpsConnection.HttpsConnectionException e1) {
                if (e1.getStatusCode() == 404 || e1.getStatusCode() == 501) {
                    instanceNodeInfo.setName("GNU");
                    instanceNodeInfo.setVersion("unknown");
                    instanceNodeInfo.setOpenRegistrations(true);
                    e1.printStackTrace();
                } else {
                    instanceNodeInfo.setName("MASTODON");
                    instanceNodeInfo.setVersion("3.0");
                    instanceNodeInfo.setOpenRegistrations(false);
                }
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            try {
                response = new HttpsConnection(context, this.instance).get("https://" + domain + "/api/v1/instance", 30, null, null);
                JSONObject jsonObject = new JSONObject(response);
                instanceNodeInfo.setName("MASTODON");
                instanceNodeInfo.setVersion(jsonObject.getString("version"));
                instanceNodeInfo.setOpenRegistrations(true);
            } catch (IOException e1) {
                instanceNodeInfo.setConnectionError(true);
                e1.printStackTrace();
            } catch (NoSuchAlgorithmException | JSONException | KeyManagementException e1) {
                e1.printStackTrace();
            } catch (HttpsConnection.HttpsConnectionException e1) {
                if (e1.getStatusCode() == 404 || e1.getStatusCode() == 501) {
                    instanceNodeInfo.setName("GNU");
                    instanceNodeInfo.setVersion("unknown");
                    instanceNodeInfo.setOpenRegistrations(true);
                    e1.printStackTrace();
                } else {
                    instanceNodeInfo.setName("MASTODON");
                    instanceNodeInfo.setVersion("3.0");
                    instanceNodeInfo.setOpenRegistrations(false);
                }
            }
            e.printStackTrace();
        }
        return instanceNodeInfo;
    }

    public InstanceNodeInfo instanceInfo(String domain) {

        String response;
        InstanceNodeInfo instanceNodeInfo = null;
        try {
            response = new HttpsConnection(context, domain).get("https://" + domain + "/.well-known/nodeinfo", 30, null, null);
            JSONArray jsonArray = new JSONObject(response).getJSONArray("links");
            ArrayList<NodeInfo> nodeInfos = new ArrayList<>();
            try {
                instanceNodeInfo = new InstanceNodeInfo();
                int i = 0;
                while (i < jsonArray.length()) {

                    JSONObject resobj = jsonArray.getJSONObject(i);
                    NodeInfo nodeInfo = new NodeInfo();
                    nodeInfo.setHref(resobj.getString("href"));
                    nodeInfo.setRel(resobj.getString("rel"));
                    i++;
                    nodeInfos.add(nodeInfo);
                }
                if (nodeInfos.size() > 0) {
                    NodeInfo nodeInfo = nodeInfos.get(nodeInfos.size() - 1);
                    response = new HttpsConnection(context, this.instance).get(nodeInfo.getHref(), 30, null, null);
                    JSONObject resobj = new JSONObject(response);
                    JSONObject jsonObject = resobj.getJSONObject("software");
                    String name;
                    name = jsonObject.getString("name").toUpperCase();
                    instanceNodeInfo.setName(name);
                    instanceNodeInfo.setVersion(jsonObject.getString("version"));
                    instanceNodeInfo.setOpenRegistrations(resobj.getBoolean("openRegistrations"));
                    if (name.trim().toUpperCase().compareTo("MASTODON") == 0 || name.trim().toUpperCase().compareTo("PLEROMA") == 0 || name.trim().toUpperCase().compareTo("PIXELFED") == 0) {
                        APIResponse apiResponse = getInstance(domain);
                        Instance instanceNode = apiResponse.getInstance();
                        instanceNodeInfo.setNodeDescription(instanceNode.getDescription());
                        instanceNodeInfo.setNumberOfUsers(instanceNode.getUserCount());
                        instanceNodeInfo.setNumberOfPosts(instanceNode.getStatusCount());
                        instanceNodeInfo.setNumberOfInstance(instanceNode.getDomainCount());
                        instanceNodeInfo.setStaffAccount(instanceNode.getContactAccount());
                        instanceNodeInfo.setNodeName(instanceNode.getTitle());
                        instanceNodeInfo.setThumbnail(instanceNode.getThumbnail());
                        instanceNodeInfo.setVersion(instanceNode.getVersion());
                    }
                    if (resobj.has("metadata")) {
                        JSONObject metadata = resobj.getJSONObject("metadata");
                        if (metadata.has("staffAccounts")) {
                            instanceNodeInfo.setStaffAccountStr(metadata.getString("staffAccounts"));
                        }
                        if (metadata.has("nodeName")) {
                            instanceNodeInfo.setNodeName(metadata.getString("nodeName"));
                        }
                    }
                    if (resobj.has("usage")) {
                        JSONObject usage = resobj.getJSONObject("usage");
                        if (usage.has("users") && usage.getJSONObject("users").has("total")) {
                            instanceNodeInfo.setNumberOfUsers(usage.getJSONObject("users").getInt("total"));
                        }
                        if (usage.has("localPosts")) {
                            instanceNodeInfo.setNumberOfPosts(usage.getInt("localPosts"));
                        }
                    }
                    if (instanceNodeInfo.getStaffAccountStr() != null && instanceNodeInfo.getStaffAccount() == null) {
                        APIResponse search = searchAccounts(instanceNodeInfo.getStaffAccountStr(), 1);
                        if (search != null && search.getAccounts() != null && search.getAccounts().size() > 0) {
                            instanceNodeInfo.setStaffAccount(search.getAccounts().get(0));
                        }
                    }
                }
            } catch (JSONException e) {
                setDefaultError(e);
            }
        } catch (IOException | JSONException | NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        } catch (HttpsConnection.HttpsConnectionException e) {
            APIResponse apiResponse = getInstance(domain);
            instanceNodeInfo = new InstanceNodeInfo();
            instanceNodeInfo.setName("MASTODON");
            Instance instanceNode = apiResponse.getInstance();
            instanceNodeInfo.setNodeDescription(instanceNode.getDescription());
            instanceNodeInfo.setNumberOfUsers(instanceNode.getUserCount());
            instanceNodeInfo.setNumberOfPosts(instanceNode.getStatusCount());
            instanceNodeInfo.setNumberOfInstance(instanceNode.getDomainCount());
            instanceNodeInfo.setStaffAccount(instanceNode.getContactAccount());
            instanceNodeInfo.setNodeName(instanceNode.getTitle());
            instanceNodeInfo.setThumbnail(instanceNode.getThumbnail());
            instanceNodeInfo.setVersion(instanceNode.getVersion());
        }
        return instanceNodeInfo;
    }

    /***
     * Get info on the current Instance *synchronously*
     * @return APIResponse
     */
    public APIResponse getInstance(String instance) {
        try {
            String response = new HttpsConnection(context, this.instance).get("https://" + instance + "/api/v1/instance", 30, null, prefKeyOauthTokenT);
            Instance instanceEntity = parseInstance(new JSONObject(response));
            apiResponse.setInstance(instanceEntity);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /***
     * Get info on the current Instance *synchronously*
     * @return APIResponse
     */
    public APIResponse getInstance() {
        try {
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/instance"), 30, null, prefKeyOauthTokenT);
            Instance instanceEntity = parseInstance(new JSONObject(response));
            apiResponse.setInstance(instanceEntity);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /***
     * Get instance for registering an account *synchronously*
     * @return APIResponse
     */
    public APIResponse getInstanceReg(String category) {
        apiResponse = new APIResponse();
        try {
            String response = new HttpsConnection(context, null).get(String.format("https://api.joinmastodon.org/servers?category=%s", category));
            List<InstanceReg> instanceRegs = parseInstanceReg(new JSONArray(response));
            apiResponse.setInstanceRegs(instanceRegs);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /***
     * Update credential of the authenticated user *synchronously*
     * @return APIResponse
     */
    public APIResponse updateCredential(String display_name, String note, ByteArrayInputStream avatar, String avatarName, ByteArrayInputStream header, String headerName, accountPrivacy privacy, HashMap<String, String> customFields, boolean sensitive) {

        HashMap<String, String> requestParams = new HashMap<>();
        if (display_name != null)
            try {
                requestParams.put("display_name", URLEncoder.encode(display_name, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                requestParams.put("display_name", display_name);
            }
        if (note != null)
            try {
                requestParams.put("note", URLEncoder.encode(note, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                requestParams.put("note", note);
            }
        if (privacy != null)
            requestParams.put("locked", privacy == accountPrivacy.LOCKED ? "true" : "false");
        int i = 0;
        if (customFields != null && customFields.size() > 0) {
            Iterator<Map.Entry<String, String>> it = customFields.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, String> pair = it.next();
                requestParams.put("fields_attributes[" + i + "][name]", pair.getKey());
                requestParams.put("fields_attributes[" + i + "][value]", pair.getValue());
                it.remove();
                i++;
            }
        }
        requestParams.put("source[sensitive]", String.valueOf(sensitive));
        try {
            new HttpsConnection(context, this.instance).patch(getAbsoluteUrl("/accounts/update_credentials"), 10, requestParams, avatar, avatarName, header, headerName, prefKeyOauthTokenT);
        } catch (HttpsConnection.HttpsConnectionException e) {
            e.printStackTrace();
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /***
     * Verifiy credential of the authenticated user *synchronously*
     * @return Account
     */
    public Account verifyCredentials() {
        account = new Account();
        InstanceNodeInfo nodeinfo = displayNodeInfo(instance);
        String social = null;
        if (nodeinfo != null) {
            social = nodeinfo.getName();
        }
        try {
            if (context == null) {
                setError(500, new Throwable("An error occured!"));
                return null;
            }
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/accounts/verify_credentials"), 10, null, prefKeyOauthTokenT);
            account = parseAccountResponse(new JSONObject(response));
            if (social != null) {
                account.setSocial(social.toUpperCase());
            }
            if (account != null && account.getSocial() != null && account.getSocial().equals("PLEROMA")) {
                isPleromaAdmin(account.getAcct());
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            e.printStackTrace();
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return account;
    }


    /***
     * Verifiy credential of the authenticated user *synchronously*
     */
    public void refreshToken(Account account) {
        HashMap<String, String> params = new HashMap<>();
        params.put("grant_type", "refresh_token");
        params.put("client_id", account.getClient_id());
        params.put("client_secret", account.getClient_secret());
        params.put("refresh_token", account.getRefresh_token());
        SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
        try {
            String response;
            if (account.getSocial().compareTo("PEERTUBE") == 0) {

                response = new HttpsConnection(context, instance).post(getAbsoluteUrl("/users/token"), 60, params, null);
            } else {
                response = new HttpsConnection(context, instance).post("https://" + instance + "/oauth/token", 60, params, null);
            }
            JSONObject resobj = new JSONObject(response);
            String newToken = resobj.getString("access_token");
            String newRefreshToken = resobj.getString("refresh_token");
            final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedpreferences.edit();
            editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, newToken);
            editor.apply();
            if (account.getSocial().compareTo("PEERTUBE") == 0) {
                response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/users/me"), 60, null, newToken);
                JSONObject accountObject = new JSONObject(response).getJSONObject("account");
                account = parseAccountResponsePeertube(instance, accountObject);
            } else {
                response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/accounts/verify_credentials"), 10, null, newToken);
                account = parseAccountResponse(new JSONObject(response));
            }
            account.setRefresh_token(newRefreshToken);
            account.setToken(newToken);
            if (account.getHeader() == null) {
                account.setHeader("");
                account.setHeader_static("");
            }
            account.setInstance(instance);
            new AccountDAO(context, db).updateAccount(account);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        }
    }

    public APIResponse createAccount(AccountCreation accountCreation) {
        apiResponse = new APIResponse();

        try {
            HashMap<String, String> params = new HashMap<>();
            params.put(Helper.CLIENT_NAME, Helper.CLIENT_NAME_VALUE);
            params.put(Helper.REDIRECT_URIS, Helper.REDIRECT_CONTENT);
            params.put(Helper.SCOPES, Helper.OAUTH_SCOPES);
            params.put(Helper.WEBSITE, Helper.WEBSITE_VALUE);
            String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/apps"), 30, params, null);
            JSONObject resobj = new JSONObject(response);
            String client_id = resobj.getString("client_id");
            String client_secret = resobj.getString("client_secret");

            params = new HashMap<>();
            params.put("grant_type", "client_credentials");
            params.put("client_id", client_id);
            params.put("client_secret", client_secret);
            params.put("scope", "read write");
            response = new HttpsConnection(context, this.instance).post("https://" + this.instance + "/oauth/token", 30, params, null);
            JSONObject res = new JSONObject(response);
            String app_token = res.getString("access_token");
            params = new HashMap<>();
            params.put("username", accountCreation.getUsername());
            params.put("email", accountCreation.getEmail());
            params.put("password", accountCreation.getPassword());
            params.put("agreement", "true");
            params.put("locale", Locale.getDefault().getLanguage());
            new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/accounts"), 30, params, app_token);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Returns an account
     *
     * @param accountId String account fetched
     * @return Account entity
     */
    public Account getAccount(String accountId) {

        account = new Account();
        try {
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/accounts/%s", accountId)), 10, null, prefKeyOauthTokenT);
            account = parseAccountResponse(new JSONObject(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return account;
    }

    /**
     * Returns a relationship between the authenticated account and an account
     *
     * @param accountId String account fetched
     * @return Relationship entity
     */
    public Relationship getRelationship(String accountId) {

        List<Relationship> relationships;
        Relationship relationship = null;
        HashMap<String, String> params = new HashMap<>();
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED)
            params.put("id[]", accountId);
        else
            params.put("id", accountId);
        try {
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/accounts/relationships"), 10, params, prefKeyOauthTokenT);
            relationships = parseRelationshipResponse(new JSONArray(response));
            if (relationships.size() > 0)
                relationship = relationships.get(0);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return relationship;
    }

    /**
     * Returns a relationship between the authenticated account and an account
     *
     * @param accounts ArrayList<Account> accounts fetched
     * @return Relationship entity
     */
    public APIResponse getRelationship(List<Account> accounts) {
        HashMap<String, String> params = new HashMap<>();
        if (accounts != null && accounts.size() > 0) {
            StringBuilder parameters = new StringBuilder();
            for (Account account : accounts)
                parameters.append("id[]=").append(account.getId()).append("&");
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(5));
            params.put("id[]", parameters.toString());
            List<Relationship> relationships = new ArrayList<>();
            try {
                HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
                String response = httpsConnection.get(getAbsoluteUrl("/accounts/relationships"), 10, params, prefKeyOauthTokenT);
                relationships = parseRelationshipResponse(new JSONArray(response));
                apiResponse.setSince_id(httpsConnection.getSince_id());
                apiResponse.setMax_id(httpsConnection.getMax_id());
            } catch (HttpsConnection.HttpsConnectionException e) {
                setError(e.getStatusCode(), e);
            } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
                e.printStackTrace();
            }
            apiResponse.setRelationships(relationships);
        }

        return apiResponse;
    }

    /**
     * Retrieves status for the account *synchronously*
     *
     * @param accountId String Id of the account
     * @return APIResponse
     */
    public APIResponse getStatus(String accountId) {
        return getStatus(accountId, false, false, false, null, null, tootPerPage);
    }

    /**
     * Retrieves status for the account *synchronously*
     *
     * @param accountId String Id of the account
     * @param max_id    String id max
     * @return APIResponse
     */
    public APIResponse getStatus(String accountId, String max_id) {
        return getStatus(accountId, false, false, false, max_id, null, tootPerPage);
    }

    /**
     * Retrieves status with media for the account *synchronously*
     *
     * @param accountId String Id of the account
     * @param max_id    String id max
     * @return APIResponse
     */
    public APIResponse getStatusWithMedia(String accountId, String max_id) {
        return getStatus(accountId, true, false, false, max_id, null, tootPerPage);
    }

    /**
     * Retrieves pinned status(es) *synchronously*
     *
     * @param accountId String Id of the account
     * @param max_id    String id max
     * @return APIResponse
     */
    public APIResponse getPinnedStatuses(String accountId, String max_id) {
        return getStatus(accountId, false, true, false, max_id, null, tootPerPage);
    }

    /**
     * Retrieves replies status(es) *synchronously*
     *
     * @param accountId String Id of the account
     * @param max_id    String id max
     * @return APIResponse
     */
    public APIResponse getAccountTLStatuses(String accountId, String max_id, boolean exclude_replies) {
        return getStatus(accountId, false, false, exclude_replies, max_id, null, tootPerPage);
    }

    /**
     * Retrieves status for the account *synchronously*
     *
     * @param accountId       String Id of the account
     * @param onlyMedia       boolean only with media
     * @param exclude_replies boolean excludes replies
     * @param max_id          String id max
     * @param since_id        String since the id
     * @param limit           int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getStatus(String accountId, boolean onlyMedia, boolean pinned,
                                  boolean exclude_replies, String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        //noinspection ConstantConditions
        if (0 < limit || limit > 40)
            limit = 40;
        if (onlyMedia)
            params.put("only_media", Boolean.toString(true));
        if (pinned)
            params.put("pinned", Boolean.toString(true));
        params.put("exclude_replies", Boolean.toString(exclude_replies));
        params.put("limit", String.valueOf(limit));
        final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        String tag = sharedpreferences.getString(Helper.SET_FEATURED_TAG_ACTION, null);
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON && tag != null) {
            params.put("tagged", tag.toLowerCase());
        }
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/accounts/%s/statuses", accountId)), 10, params, prefKeyOauthTokenT);
            statuses = parseStatuses(context, new JSONArray(response));
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
            setDefaultError(e);
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves accounts that reblogged the status *synchronously*
     *
     * @param statusId String Id of the status
     * @param max_id   String id max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getRebloggedBy(String statusId, String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        params.put("limit", "80");
        accounts = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/statuses/%s/reblogged_by", statusId)), 10, params, prefKeyOauthTokenT);
            accounts = parseAccountResponse(new JSONArray(response));
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Retrieves accounts that favourited the status *synchronously*
     *
     * @param statusId String Id of the status
     * @param max_id   String id max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getFavouritedBy(String statusId, String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        params.put("limit", "80");
        accounts = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/statuses/%s/favourited_by", statusId)), 10, params, prefKeyOauthTokenT);
            accounts = parseAccountResponse(new JSONArray(response));
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Retrieves one status *synchronously*
     *
     * @param statusId String Id of the status
     * @return APIResponse
     */
    public APIResponse getStatusbyId(String statusId) {
        statuses = new ArrayList<>();

        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/statuses/%s", statusId)), 10, null, prefKeyOauthTokenT);
            Status status = parseStatuses(context, new JSONObject(response));
            statuses.add(status);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves one status *synchronously*
     *
     * @param statusId String Id of the status
     * @return APIResponse
     */
    public APIResponse getStatusbyIdAndCache(String statusId) {
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/statuses/%s", statusId)), 10, null, prefKeyOauthTokenT);
            Status status = parseStatuses(context, new JSONObject(response));
            SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
            Account account = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT);
            new TimelineCacheDAO(context, db).update(status.getId(), response, account.getId(), account.getInstance());
            statuses.add(status);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves the context of status with replies *synchronously*
     *
     * @param statusId Id of the status
     * @return List<Status>
     */
    public app.fedilab.android.client.Entities.Context getStatusContext(String statusId) {
        app.fedilab.android.client.Entities.Context statusContext = new app.fedilab.android.client.Entities.Context();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/statuses/%s/context", statusId)), 10, null, prefKeyOauthTokenT);
            statusContext = parseContext(new JSONObject(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return statusContext;
    }

    public APIResponse getReplies(String statusId, String max_id) {
        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        params.put("limit", String.valueOf(30));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUr2l(String.format("/status/%s/replies", statusId)), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves direct timeline for the account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getDirectTimeline(String max_id) {
        return getDirectTimeline(max_id, null, tootPerPage);
    }

    /**
     * Retrieves conversation timeline for the account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getConversationTimeline(String max_id) {
        return getConversationTimeline(max_id, null, tootPerPage);
    }

    /**
     * Retrieves direct timeline for the account since an Id value *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getConversationTimelineSinceId(String since_id) {
        return getConversationTimeline(null, since_id, tootPerPage);
    }

    /**
     * Retrieves conversation timeline for the account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    private APIResponse getConversationTimeline(String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 80)
            limit = 80;
        params.put("limit", String.valueOf(limit));
        List<Conversation> conversations = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/conversations"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            conversations = parseConversations(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setConversations(conversations);
        return apiResponse;
    }

    /**
     * Retrieves direct timeline for the account since an Id value *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getDirectTimelineSinceId(String since_id) {
        return getDirectTimeline(null, since_id, tootPerPage);
    }

    /**
     * Retrieves direct timeline for the account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    private APIResponse getDirectTimeline(String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 80)
            limit = 80;
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/timelines/direct"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves home timeline for the account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getHomeTimeline(String max_id) {
        return getHomeTimeline(max_id, null, null, tootPerPage);
    }

    /**
     * Retrieves home timeline for the account since an Id value *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getHomeTimelineSinceId(String since_id) {
        return getHomeTimeline(null, since_id, null, tootPerPage);
    }

    /**
     * Retrieves home timeline from cache the account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getHomeTimelineCache(String max_id) {

        SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
        statuses = new TimelineCacheDAO(context, db).get(max_id);

        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        boolean remember_position_home = sharedpreferences.getBoolean(Helper.SET_REMEMBER_POSITION_HOME, true);
        if (remember_position_home) {
            if (statuses != null) {
                Iterator<Status> i = statuses.iterator();
                List<String> ids = new ArrayList<>();
                while (i.hasNext()) {
                    Status s = i.next();
                    if (ids.contains(s.getId())) {
                        i.remove();
                        new TimelineCacheDAO(context, db).remove(s.getId());
                    } else {
                        ids.add(s.getId());
                    }
                }
            }
            if (statuses == null || statuses.size() <= 1) {
                return getHomeTimeline(max_id);
            } else {
                if (statuses.get(0).getId().matches("\\d+")) {
                    apiResponse.setSince_id(String.valueOf(statuses.get(0).getId()));
                } else {
                    apiResponse.setSince_id(statuses.get(0).getId());
                }
                apiResponse.setMax_id(statuses.get(statuses.size() - 1).getId());
                apiResponse.setStatuses(statuses);
                return apiResponse;
            }
        } else {
            return getHomeTimeline(max_id);
        }

    }

    /**
     * Retrieves home timeline for the account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getHomeTimeline(String max_id, String since_id, String min_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (min_id != null)
            params.put("min_id", min_id);
        if (0 > limit || limit > 80)
            limit = 80;
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/timelines/home"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            if (since_id == null) {
                statuses = parseStatusesForCache(context, new JSONArray(response));
            } else {
                statuses = parseStatuses(context, new JSONArray(response));
            }
        } catch (UnknownHostException e) {
            if (since_id == null) {
                getHomeTimelineCache(max_id);
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        if (apiResponse == null)
            apiResponse = new APIResponse();
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Get identy proof for an account *synchronously*
     *
     * @param userId user_id String
     * @return APIResponse
     */
    public APIResponse getIdentityProof(String userId) {
        List<IdentityProof> identityProofs = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/accounts/%s/identity_proofs", userId)), 10, null, prefKeyOauthTokenT);

            identityProofs = parseIdentityProof(new JSONArray(response));
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        }
        if (apiResponse == null)
            apiResponse = new APIResponse();
        apiResponse.setIdentityProofs(identityProofs);
        return apiResponse;
    }

    /**
     * Retrieves public GNU timeline for the account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getGNUTimeline(String remoteInstance, String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get("https://" + remoteInstance + "/api/statuses/public_timeline.json", 10, params, prefKeyOauthTokenT);
            statuses = GNUAPI.parseStatuses(context, new JSONArray(response));
            if (statuses.size() > 0) {
                if (statuses.get(0).getId() != null && statuses.get(0).getId().matches("-?\\d+(\\.\\d+)?")) {
                    apiResponse.setSince_id(String.valueOf(statuses.get(0).getId()));
                    apiResponse.setMax_id(String.valueOf(statuses.get(statuses.size() - 1).getId()));
                } else {
                    apiResponse.setSince_id(statuses.get(0).getId());
                    apiResponse.setMax_id(statuses.get(statuses.size() - 1).getId());
                }
            }
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException | HttpsConnection.HttpsConnectionException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves public pixelfed timeline for the account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getPixelfedTimeline(String remoteInstance, String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("page", max_id);
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrlRemote(remoteInstance, "/timelines/public/"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException | HttpsConnection.HttpsConnectionException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves Peertube videos from an instance *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getPeertubeChannel(String instance, String name) {

        List<Account> accounts = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(String.format("https://" + instance + "/api/v1/accounts/%s/video-channels", name), 10, null, null);
            JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
            accounts = parseAccountResponsePeertube(instance, jsonArray);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Retrieves Peertube videos from an instance *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getPeertubeChannelVideos(String instance, String name) {

        List<Peertube> peertubes = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(String.format("https://" + instance + "/api/v1/video-channels/%s/videos", name), 10, null, null);
            JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
            peertubes = parsePeertube(instance, jsonArray);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setPeertubes(peertubes);
        return apiResponse;
    }

    /**
     * Retrieves Peertube videos from an instance *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getPeertube(String instance, String max_id) {

        List<Peertube> peertubes = new ArrayList<>();
        HashMap<String, String> params = new HashMap<>();
        if (max_id == null)
            max_id = "0";
        params.put("start", max_id);
        params.put("filter", "local");
        params.put("sort", "-publishedAt");
        params.put("count", "20");

        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get("https://" + instance + "/api/v1/videos", 10, params, null);
            if (response == null) {
                apiResponse.setPeertubes(peertubes);
                return apiResponse;
            }
            JSONObject jsonObject = new JSONObject(response);
            if (jsonObject.has("data")) {
                JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
                peertubes = parsePeertube(instance, jsonArray);
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setPeertubes(peertubes);
        return apiResponse;
    }

    /**
     * Retrieves Peertube videos from an instance *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getSinglePeertube(String instance, String videoId) {


        Peertube peertube = null;
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(String.format("https://" + instance + "/api/v1/videos/%s", videoId), 10, null, null);
            JSONObject jsonObject = new JSONObject(response);
            peertube = parseSinglePeertube(instance, jsonObject);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        List<Peertube> peertubes = new ArrayList<>();
        peertubes.add(peertube);
        apiResponse.setPeertubes(peertubes);
        return apiResponse;
    }

    /**
     * Retrieves peertube search *synchronously*
     *
     * @param query String search
     * @return APIResponse
     */
    public APIResponse searchPeertube(String instance, String query) {
        HashMap<String, String> params = new HashMap<>();
        params.put("count", "50");
        if (query == null)
            return null;
        try {
            params.put("search", URLEncoder.encode(query, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            params.put("search", query);
        }

        List<Peertube> peertubes = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get("https://" + instance + "/api/v1/search/videos", 10, params, null);
            JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
            peertubes = parsePeertube(instance, jsonArray);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setPeertubes(peertubes);
        return apiResponse;
    }

    /**
     * Retrieves Peertube videos from an instance *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getSinglePeertubeComments(String instance, String videoId) {
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(String.format("https://" + instance + "/api/v1/videos/%s/comment-threads", videoId), 10, null, null);
            JSONObject jsonObject = new JSONObject(response);
            statuses = parseSinglePeertubeComments(context, instance, jsonObject);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves home timeline for the account *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getHowTo() {

        List<HowToVideo> howToVideos = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get("https://peertube.fedilab.app/api/v1/video-channels/fedilab_channel/videos", 10, null, null);
            JSONArray jsonArray = new JSONObject(response).getJSONArray("data");
            howToVideos = parseHowTos(jsonArray);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setHowToVideos(howToVideos);
        return apiResponse;
    }

    /**
     * Retrieves Nitter timeline from accounts *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getNitter(String instance, String max_id) {

        final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        String nitterHost = sharedpreferences.getString(Helper.SET_NITTER_HOST, Helper.DEFAULT_NITTER_HOST).toLowerCase();
        String[] usernames = instance.split(" ");
        if (usernames.length == 0) {
            Error error = new Error();
            error.setError(context.getString(R.string.toast_error));
            error.setStatusCode(404);
            apiResponse.setError(error);
            return apiResponse;
        }
        StringBuilder urlparams = new StringBuilder();

        {
            int i = 0;
            while (i < usernames.length) {
                String username = usernames[i].trim();
                urlparams.append(username);
                if (i != usernames.length - 1) urlparams.append(",");
                i++;
            }
        }

        String url = "https://" + nitterHost + "/" + urlparams + "/rss";
        if (max_id != null) {
            url += "?max_position=" + max_id;
        }
        try {
            statuses = new ArrayList<>();
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(url, 30, null, null);
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseNitter(response);

        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves Misskey timeline from an instance *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getMisskey(String instance, String max_id) {

        JSONObject params = new JSONObject();
        try {
            params.put("file", false);
            if (max_id != null)
                params.put("untilId", max_id);
            params.put("local", true);
            params.put("poll", false);
            params.put("renote", false);
            params.put("reply", false);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        try {
            statuses = new ArrayList<>();
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.postMisskey("https://" + instance + "/api/notes", 10, params, null);
            statuses = parseNotes(context, instance, new JSONArray(response));
            if (statuses.size() > 0) {
                apiResponse.setSince_id(statuses.get(0).getId());
                apiResponse.setMax_id(statuses.get(statuses.size() - 1).getId());
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves public timeline for the account *synchronously*
     *
     * @param local  boolean only local timeline
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getPublicTimeline(String instanceName, boolean local, String max_id) {
        return getPublicTimeline(local, instanceName, max_id, null, tootPerPage);
    }

    /**
     * Retrieves public timeline for the account *synchronously*
     *
     * @param local  boolean only local timeline
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getPublicTimeline(boolean local, String max_id) {
        return getPublicTimeline(local, null, max_id, null, tootPerPage);
    }

    /**
     * Retrieves public timeline for the account since an Id value *synchronously*
     *
     * @param local    boolean only local timeline
     * @param since_id String id since
     * @return APIResponse
     */
    public APIResponse getPublicTimelineSinceId(boolean local, String since_id) {
        return getPublicTimeline(local, null, null, since_id, tootPerPage);
    }

    /**
     * Retrieves instance timeline since an Id value *synchronously*
     *
     * @param instanceName String instance name
     * @param since_id     String id since
     * @return APIResponse
     */
    public APIResponse getInstanceTimelineSinceId(String instanceName, String since_id) {
        return getPublicTimeline(true, instanceName, null, since_id, tootPerPage);
    }

    /**
     * Retrieves public timeline for the account *synchronously*
     *
     * @param local    boolean only local timeline
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    private APIResponse getPublicTimeline(boolean local, String instanceName, String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (local)
            params.put("local", Boolean.toString(true));
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 40)
            limit = 40;
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String url;
            if (instanceName == null)
                url = getAbsoluteUrl("/timelines/public");
            else
                url = getAbsoluteUrlRemoteInstance(instanceName);
            String response = httpsConnection.get(url, 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves news coming from Fedilab's account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getNews(String max_id) {

        HashMap<String, String> params = null;
        if (max_id != null) {
            params = new HashMap<>();
            params.put("max_id", max_id);
        }

        statuses = new ArrayList<>();
        apiResponse = new APIResponse();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get("https://toot.fedilab.app/api/v1/timelines/tag/fedilab", 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            List<Status> tmp_status = parseStatuses(context, new JSONArray(response));
            if (tmp_status.size() > 0) {
                for (Status status : tmp_status) {
                    if (status.getAccount().getAcct().equals("fedilab")) {
                        statuses.add(status);
                    }
                }
            }
        } catch (IOException | NoSuchAlgorithmException | KeyManagementException | HttpsConnection.HttpsConnectionException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves discover timeline for the account *synchronously*
     *
     * @param local  boolean only local timeline
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getDiscoverTimeline(boolean local, String max_id) {
        return getDiscoverTimeline(local, max_id, null, tootPerPage);
    }

    /**
     * Retrieves discover timeline for the account *synchronously*
     *
     * @param local    boolean only local timeline
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getDiscoverTimeline(boolean local, String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (local)
            params.put("local", Boolean.toString(true));
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 40)
            limit = 40;
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String url;
            url = getAbsoluteUr2l("/discover/posts");
            String response = httpsConnection.get(url, 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    public APIResponse getCustomArtTimeline(boolean local, String tag, String max_id, List<String> any, List<String> all, List<String> none) {
        return getArtTimeline(local, tag, max_id, null, any, all, none);
    }

    public APIResponse getArtTimeline(boolean local, String max_id, List<String> any, List<String> all, List<String> none) {
        return getArtTimeline(local, null, max_id, null, any, all, none);
    }

    public APIResponse getCustomArtTimelineSinceId(boolean local, String tag, String since_id, List<String> any, List<String> all, List<String> none) {
        return getArtTimeline(local, tag, null, since_id, any, all, none);
    }

    public APIResponse getArtTimelineSinceId(boolean local, String since_id, List<String> any, List<String> all, List<String> none) {
        return getArtTimeline(local, null, null, since_id, any, all, none);
    }

    /**
     * Retrieves art timeline
     *
     * @param local  boolean only local timeline
     * @param max_id String id max
     * @return APIResponse
     */
    private APIResponse getArtTimeline(boolean local, String tag, String max_id, String since_id, List<String> any, List<String> all, List<String> none) {
        if (tag == null)
            tag = "mastoart";
        APIResponse apiResponse = getPublicTimelineTag(tag, local, true, max_id, since_id, tootPerPage, any, all, none, null);
        APIResponse apiResponseReply = new APIResponse();
        if (apiResponse != null) {
            apiResponseReply.setMax_id(apiResponse.getMax_id());
            apiResponseReply.setSince_id(apiResponse.getSince_id());
            apiResponseReply.setStatuses(new ArrayList<>());
            if (apiResponse.getStatuses() != null && apiResponse.getStatuses().size() > 0) {
                for (Status status : apiResponse.getStatuses()) {
                    if (status.getMedia_attachments() != null) {
                        try {
                            String statusSerialized = Helper.statusToStringStorage(status);
                            for (Attachment attachment : status.getMedia_attachments()) {
                                Status newStatus = Helper.restoreStatusFromString(statusSerialized);
                                if (newStatus == null)
                                    break;
                                newStatus.setArt_attachment(attachment);
                                apiResponseReply.getStatuses().add(newStatus);
                            }
                        } catch (Exception ignored) {
                        }
                    }
                }
            }
        }
        return apiResponseReply;
    }

    /**
     * Retrieves public tag timeline *synchronously*
     *
     * @param tag    String
     * @param local  boolean only local timeline
     * @param max_id String id max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getPublicTimelineTag(String tag, boolean local, String max_id, List<String> any, List<String> all, List<String> none) {
        return getPublicTimelineTag(tag, local, false, max_id, null, tootPerPage, any, all, none, null);
    }

    /**
     * Retrieves public tag timeline *synchronously*
     *
     * @param tag    String
     * @param local  boolean only local timeline
     * @param max_id String id max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getPublicTimelineTag(String tag, boolean local, String max_id, String instance) {
        return getPublicTimelineTag(tag, local, false, max_id, null, tootPerPage, null, null, null, instance);
    }

    /**
     * Retrieves public tag timeline *synchronously*
     *
     * @param tag      String
     * @param local    boolean only local timeline
     * @param since_id String since id
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getPublicTimelineTagSinceId(String tag, boolean local, String since_id, List<String> any, List<String> all, List<String> none) {
        return getPublicTimelineTag(tag, local, false, null, since_id, tootPerPage, any, all, none, null);
    }

    /**
     * Retrieves public tag timeline *synchronously*
     *
     * @param tag      String
     * @param local    boolean only local timeline
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getPublicTimelineTag(String tag, boolean local, boolean onlymedia, String max_id, String since_id, int limit, List<String> any, List<String> all, List<String> none, String instance) {

        HashMap<String, String> params = new HashMap<>();
        if (local)
            params.put("local", Boolean.toString(true));
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 40)
            limit = 40;
        if (onlymedia)
            params.put("only_media", Boolean.toString(true));


        if (any != null && any.size() > 0) {
            StringBuilder parameters = new StringBuilder();
            for (String a : any) {
                try {
                    a = URLEncoder.encode(a, "UTF-8");
                } catch (UnsupportedEncodingException ignored) {
                }
                parameters.append("any[]=").append(a).append("&");
            }
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(6));
            params.put("any[]", parameters.toString());
        }
        if (all != null && all.size() > 0) {
            StringBuilder parameters = new StringBuilder();
            for (String a : all) {
                try {
                    a = URLEncoder.encode(a, "UTF-8");
                } catch (UnsupportedEncodingException ignored) {
                }
                parameters.append("all[]=").append(a).append("&");
            }
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(6));
            params.put("all[]", parameters.toString());
        }
        if (none != null && none.size() > 0) {
            StringBuilder parameters = new StringBuilder();
            for (String a : none) {
                try {
                    a = URLEncoder.encode(a, "UTF-8");
                } catch (UnsupportedEncodingException ignored) {
                }
                parameters.append("none[]=").append(a).append("&");
            }
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(7));
            params.put("none[]", parameters.toString());
        }
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        if (tag == null)
            return null;
        try {
            String query = tag.trim();
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            if (MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE)
                try {
                    query = URLEncoder.encode(query, "UTF-8");
                } catch (UnsupportedEncodingException ignored) {
                }
            String response;
            if (instance == null)
                response = httpsConnection.get(getAbsoluteUrl(String.format("/timelines/tag/%s", query)), 10, params, prefKeyOauthTokenT);
            else
                response = httpsConnection.get(getAbsoluteUrlRemote(instance, String.format("/timelines/tag/%s", query)), 10, params, null);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves muted users by the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getMuted(String max_id) {
        return getAccounts("/mutes", max_id, null, accountPerPage);
    }

    /**
     * Retrieves blocked users by the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getBlocks(String max_id) {
        return getAccounts("/blocks", max_id, null, accountPerPage);
    }

    /**
     * Retrieves following for the account specified by targetedId  *synchronously*
     *
     * @param targetedId String targetedId
     * @param max_id     String id max
     * @return APIResponse
     */
    public APIResponse getFollowing(String targetedId, String max_id) {
        return getAccounts(String.format("/accounts/%s/following", targetedId), max_id, null, accountPerPage);
    }

    /**
     * Retrieves followers for the account specified by targetedId  *synchronously*
     *
     * @param targetedId String targetedId
     * @param max_id     String id max
     * @return APIResponse
     */
    public APIResponse getFollowers(String targetedId, String max_id) {
        return getAccounts(String.format("/accounts/%s/followers", targetedId), max_id, null, accountPerPage);
    }

    /**
     * Retrieves blocked users by the authenticated account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getAccounts(String action, String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 40)
            limit = 40;
        params.put("limit", String.valueOf(limit));
        accounts = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(action), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            accounts = parseAccountResponse(new JSONArray(response));
            if (accounts.size() == 1) {
                if (accounts.get(0).getAcct() == null) {
                    Throwable error = new Throwable(context.getString(R.string.toast_error));
                    setError(500, error);
                }
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Retrieves blocked domains for the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getBlockedDomain(String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        params.put("limit", "80");
        domains = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/domain_blocks"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            domains = parseDomains(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setDomains(domains);
        return apiResponse;
    }

    /**
     * Delete a blocked domains for the authenticated account *synchronously*
     *
     * @param domain String domain name
     */
    @SuppressWarnings("SameParameterValue")
    public int deleteBlockedDomain(String domain) {

        HashMap<String, String> params = new HashMap<>();
        params.put("domain", domain);
        domains = new ArrayList<>();
        HttpsConnection httpsConnection;
        try {
            httpsConnection = new HttpsConnection(context, this.instance);
            httpsConnection.delete(getAbsoluteUrl("/domain_blocks"), 10, params, prefKeyOauthTokenT);
            actionCode = httpsConnection.getActionCode();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return actionCode;
    }

    /**
     * Retrieves follow requests for the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getFollowRequest(String max_id) {
        return getFollowRequest(max_id, null, accountPerPage);
    }

    /**
     * Retrieves follow requests for the authenticated account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getFollowRequest(String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 40)
            limit = 40;
        params.put("limit", String.valueOf(limit));
        accounts = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/follow_requests"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            accounts = parseAccountResponse(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Retrieves favourited status for the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getFavourites(String max_id) {
        return getFavourites(max_id, null, tootPerPage);
    }

    /**
     * Retrieves favourited status for the authenticated account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    private APIResponse getFavourites(String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 40)
            limit = 40;
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/favourites"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Retrieves announcements for the authenticated account *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getAnnouncements() {

        List<Announcement> announcements = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/announcements"), 10, null, prefKeyOauthTokenT);
            announcements = parseAnnouncement(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAnnouncements(announcements);
        return apiResponse;
    }

    /**
     * Retrieves bookmarked status for the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getBookmarks(String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        params.put("limit", "40");
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/bookmarks"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Makes the post action for a status
     *
     * @param statusAction Enum
     * @param targetedId   String id of the targeted Id *can be this of a status or an account*
     * @return in status code - Should be equal to 200 when action is done
     */
    public int postAction(StatusAction statusAction, String targetedId) {
        return postAction(statusAction, targetedId, null, null);
    }

    /**
     * Makes the post action for a status
     *
     * @param statusAction Enum
     * @param targetedId   String id of the targeted Id *can be this of a status or an account*
     * @return in status code - Should be equal to 200 when action is done
     */
    public int postAction(StatusAction statusAction, String targetedId, String comment) {
        return postAction(statusAction, targetedId, null, comment);
    }

    /**
     * Makes the post action for a status
     *
     * @param targetedId        String id of the targeted Id *can be this of a status or an account*
     * @param muteNotifications - boolean - notifications should be also muted
     * @return in status code - Should be equal to 200 when action is done
     */
    public int muteNotifications(String targetedId, boolean muteNotifications) {

        HashMap<String, String> params = new HashMap<>();
        params.put("notifications", Boolean.toString(muteNotifications));
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            httpsConnection.post(getAbsoluteUrl(String.format("/accounts/%s/mute", targetedId)), 10, params, prefKeyOauthTokenT);
            actionCode = httpsConnection.getActionCode();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return actionCode;
    }

    //Pleroma admin calls

    /**
     * Makes the post action
     *
     * @param status  Status object related to the status
     * @param comment String comment for the report
     * @return in status code - Should be equal to 200 when action is done
     */
    public int reportAction(Status status, String comment) {
        return postAction(API.StatusAction.REPORT, null, status, comment);
    }

    /**
     * Makes the post action
     *
     * @param targetedId targeted account
     * @param comment    String comment for the report
     * @return in status code - Should be equal to 200 when action is done
     */
    public int reportAction(String targetedId, String comment) {
        return postAction(API.StatusAction.REPORT, targetedId, null, comment);
    }

    public int statusAction(Status status) {
        return postAction(StatusAction.CREATESTATUS, null, status, null);
    }

    /**
     * Makes the post action
     *
     * @param statusAction Enum
     * @param targetedId   String id of the targeted Id *can be this of a status or an account*
     * @param status       Status object related to the status
     * @param comment      String comment for the report
     * @return in status code - Should be equal to 200 when action is done
     */
    private int postAction(StatusAction statusAction, String targetedId, Status status, String comment) {

        String action;
        HashMap<String, String> params = null;
        switch (statusAction) {
            case FAVOURITE:
                action = String.format("/statuses/%s/favourite", targetedId);
                break;
            case UNFAVOURITE:
                action = String.format("/statuses/%s/unfavourite", targetedId);
                break;
            case BOOKMARK:
                action = String.format("/statuses/%s/bookmark", targetedId);
                break;
            case UNBOOKMARK:
                action = String.format("/statuses/%s/unbookmark", targetedId);
                break;
            case REBLOG:
                action = String.format("/statuses/%s/reblog", targetedId);
                break;
            case UNREBLOG:
                action = String.format("/statuses/%s/unreblog", targetedId);
                break;
            case FOLLOW:
                action = String.format("/accounts/%s/follow", targetedId);
                break;
            case REMOTE_FOLLOW:
                action = "/follows";
                params = new HashMap<>();
                params.put("uri", targetedId);
                break;
            case UNFOLLOW:
                action = String.format("/accounts/%s/unfollow", targetedId);
                break;
            case BLOCK:
                action = String.format("/accounts/%s/block", targetedId);
                break;
            case BLOCK_DOMAIN:
                action = "/domain_blocks";
                params = new HashMap<>();
                params.put("domain", targetedId);
                break;
            case UNBLOCK:
                action = String.format("/accounts/%s/unblock", targetedId);
                break;
            case MUTE:
                action = String.format("/accounts/%s/mute", targetedId);
                break;
            case UNMUTE:
                action = String.format("/accounts/%s/unmute", targetedId);
                break;
            case MUTE_CONVERSATION:
                action = String.format("/statuses/%s/mute", targetedId);
                break;
            case UNMUTE_CONVERSATION:
                action = String.format("/statuses/%s/unmute", targetedId);
                break;
            case PIN:
                action = String.format("/statuses/%s/pin", targetedId);
                break;
            case UNPIN:
                action = String.format("/statuses/%s/unpin", targetedId);
                break;
            case REACT:
                action = String.format("/pleroma/statuses/%s/react_with_emoji", targetedId);
                break;
            case UNREACT:
                action = String.format("/pleroma/statuses/%s/unreact_with_emoji", targetedId);
                break;
            case ENDORSE:
                action = String.format("/accounts/%s/pin", targetedId);
                break;
            case UNENDORSE:
                action = String.format("/accounts/%s/unpin", targetedId);
                break;
            case SHOW_BOOST:
                params = new HashMap<>();
                params.put("reblogs", "true");
                action = String.format("/accounts/%s/follow", targetedId);
                break;
            case HIDE_BOOST:
                params = new HashMap<>();
                params.put("reblogs", "false");
                action = String.format("/accounts/%s/follow", targetedId);
                break;
            case UNSTATUS:
                action = String.format("/statuses/%s", targetedId);
                break;
            case REMOVE_REACTION:
            case ADD_REACTION:
                action = String.format("/announcements/%s/reactions/%s", targetedId, comment);
                break;
            case REMOVE_PLEROMA_REACTION:
            case ADD_PLEROMA_REACTION:
                action = String.format("/pleroma/statuses/%s/reactions/%s", targetedId, comment);
                break;
            case DISMISS_ANNOUNCEMENT:
                action = String.format("/announcements/%s/dismiss", targetedId);
                break;
            case AUTHORIZE:
                action = String.format("/follow_requests/%s/authorize", targetedId);
                break;
            case REJECT:
                action = String.format("/follow_requests/%s/reject", targetedId);
                break;
            case REPORT:
                action = "/reports";
                params = new HashMap<>();
                if (status != null)
                    params.put("account_id", status.getAccount().getId());
                else
                    params.put("account_id", targetedId);
                params.put("comment", comment);
                if (status != null) {
                    params.put("status_ids[]", status.getId());
                }
                break;
            case CREATESTATUS:
                params = new HashMap<>();
                action = "/statuses";
                try {
                    params.put("status", URLEncoder.encode(status.getContent(), "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    params.put("status", status.getContent());
                }
                if (status.getScheduled_at() != null)
                    params.put("scheduled_at", status.getScheduled_at());
                if (status.getIn_reply_to_id() != null)
                    params.put("in_reply_to_id", status.getIn_reply_to_id());
                if (status.getMedia_attachments() != null && status.getMedia_attachments().size() > 0) {
                    StringBuilder parameters = new StringBuilder();
                    for (Attachment attachment : status.getMedia_attachments())
                        parameters.append("media_ids[]=").append(attachment.getId()).append("&");
                    parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(12));
                    params.put("media_ids[]", parameters.toString());
                }
                if (status.isSensitive())
                    params.put("sensitive", Boolean.toString(status.isSensitive()));
                if (status.getSpoiler_text() != null)
                    try {
                        params.put("spoiler_text", URLEncoder.encode(status.getSpoiler_text(), "UTF-8"));
                    } catch (UnsupportedEncodingException e) {
                        params.put("spoiler_text", status.getSpoiler_text());
                    }
                params.put("visibility", status.getVisibility());
                break;
            default:
                return -1;
        }
        if (statusAction != StatusAction.UNSTATUS
                && statusAction != StatusAction.ADD_REACTION && statusAction != StatusAction.REMOVE_REACTION
                && statusAction != StatusAction.ADD_PLEROMA_REACTION && statusAction != StatusAction.REMOVE_PLEROMA_REACTION) {
            try {
                HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
                String resp = httpsConnection.post(getAbsoluteUrl(action), 10, params, prefKeyOauthTokenT);
                actionCode = httpsConnection.getActionCode();
                if (statusAction == StatusAction.REBLOG || statusAction == StatusAction.UNREBLOG || statusAction == StatusAction.FAVOURITE || statusAction == StatusAction.UNFAVOURITE) {
                    Bundle b = new Bundle();
                    try {
                        Status status1 = parseStatuses(context, new JSONObject(resp));
                        b.putParcelable("status", status1);
                    } catch (JSONException ignored) {
                    }
                    Intent intentBC = new Intent(Helper.RECEIVE_ACTION);
                    intentBC.putExtras(b);
                    LocalBroadcastManager.getInstance(context).sendBroadcast(intentBC);
                    SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
                    Account account = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT);
                    Status indb = new TimelineCacheDAO(context, db).getSingle(targetedId);
                    if (indb != null) {
                        String response = httpsConnection.get(getAbsoluteUrl(String.format("/statuses/%s", targetedId)), 10, null, prefKeyOauthTokenT);
                        new TimelineCacheDAO(context, db).update(targetedId, response, account.getId(), account.getInstance());
                    }
                }
            } catch (HttpsConnection.HttpsConnectionException e) {
                setError(e.getStatusCode(), e);
                e.printStackTrace();
            } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
                e.printStackTrace();
            }
        } else if (statusAction == StatusAction.ADD_REACTION || statusAction == StatusAction.ADD_PLEROMA_REACTION) {
            try {
                HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
                httpsConnection.put(getAbsoluteUrl(action), 10, null, prefKeyOauthTokenT);
                actionCode = httpsConnection.getActionCode();
            } catch (HttpsConnection.HttpsConnectionException e) {
                setError(e.getStatusCode(), e);
            } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
                e.printStackTrace();
            }
        } else {
            try {
                HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
                httpsConnection.delete(getAbsoluteUrl(action), 10, null, prefKeyOauthTokenT);
                actionCode = httpsConnection.getActionCode();
                if (statusAction != StatusAction.REMOVE_REACTION && statusAction != StatusAction.REMOVE_PLEROMA_REACTION) {
                    SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
                    new TimelineCacheDAO(context, db).remove(targetedId);
                }
            } catch (HttpsConnection.HttpsConnectionException e) {
                setError(e.getStatusCode(), e);
            } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
                e.printStackTrace();
            }
        }
        return actionCode;
    }

    @SuppressWarnings("unused")
    public int reportStatus(List<Status> statuses, String comment, boolean forward) {
        String action;
        HashMap<String, String> params;
        action = "/reports";
        params = new HashMap<>();
        params.put("account_id", statuses.get(0).getAccount().getId());
        params.put("comment", comment);
        StringBuilder parameters = new StringBuilder();
        for (Status val : statuses)
            parameters.append("status_ids[]=").append(val).append("&");
        if (parameters.length() > 0) {
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(13));
            params.put("status_ids[]", parameters.toString());
        }
        params.put("forward", String.valueOf(forward));
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            httpsConnection.post(getAbsoluteUrl(action), 10, params, prefKeyOauthTokenT);
            actionCode = httpsConnection.getActionCode();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return actionCode;
    }

    /**
     * scheduled action for a status
     *
     * @param status Status object related to the status
     * @return APIResponse
     */
    public APIResponse scheduledAction(String call, Status status, String max_id, String targetedId) {

        HashMap<String, String> params = new HashMap<>();
        if (call.equals("PUT")) {
            if (status.getScheduled_at() != null)
                params.put("scheduled_at", status.getScheduled_at());
        } else if (call.equals("GET")) {
            if (max_id != null)
                params.put("max_id", max_id);
        }
        List<StoredStatus> storedStatus = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = null;

            switch (call) {
                case "GET":
                    response = httpsConnection.get(getAbsoluteUrl("/scheduled_statuses/"), 10, null, prefKeyOauthTokenT);
                    break;
                case "PUT":
                    response = httpsConnection.put(getAbsoluteUrl(String.format("/scheduled_statuses/%s", targetedId)), 10, params, prefKeyOauthTokenT);
                    break;
                case "DELETE":
                    httpsConnection.delete(getAbsoluteUrl(String.format("/scheduled_statuses/%s", targetedId)), 10, null, prefKeyOauthTokenT);
                    break;
            }
            if (call.equals("GET")) {
                apiResponse.setSince_id(httpsConnection.getSince_id());
                apiResponse.setMax_id(httpsConnection.getMax_id());
            }
            if (response != null && call.equals("PUT")) {
                Schedule schedule = parseSimpleSchedule(context, new JSONObject(response));
                StoredStatus st = new StoredStatus();
                st.setCreation_date(status.getCreated_at());
                st.setId(-1);
                st.setJobId(-1);
                st.setScheduled_date(schedule.getScheduled_at());
                st.setStatusReply(null);
                st.setSent_date(null);
                final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
                String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
                st.setUserId(userId);
                st.setStatus(schedule.getStatus());
                storedStatus.add(st);
            } else if (response != null) {
                List<Schedule> scheduleList = parseSchedule(context, new JSONArray(response));
                SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
                String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
                for (Schedule schedule : scheduleList) {
                    StoredStatus st = new StoredStatus();
                    st.setCreation_date(null);
                    st.setScheduledServerdId(schedule.getId());
                    st.setJobId(-1);
                    st.setScheduled_date(schedule.getScheduled_at());
                    st.setStatusReply(null);
                    st.setSent_date(null);
                    st.setUserId(userId);
                    st.setStatus(schedule.getStatus());
                    storedStatus.add(st);
                }
            }

        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStoredStatuses(storedStatus);
        return apiResponse;
    }

    /**
     * Public api call to submit a vote
     *
     * @param pollId  String
     * @param choices int[]
     * @return Poll
     */
    public Poll submiteVote(String pollId, int[] choices) {
        JsonObject jsonObject = new JsonObject();
        JsonArray jchoices = new JsonArray();
        for (int choice : choices) {
            jchoices.add(choice);
        }
        jsonObject.add("choices", jchoices);
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.postJson(getAbsoluteUrl(String.format("/polls/%s/votes", pollId)), 10, jsonObject, prefKeyOauthTokenT);
            return parsePoll(new JSONObject(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Public api call to refresh a poll
     *
     * @param status Status
     * @return Poll
     */
    public Poll getPoll(Status status) {
        try {
            Poll _p = (status.getReblog() != null) ? status.getReblog().getPoll() : status.getPoll();
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/polls/%s", _p.getId())), 10, null, prefKeyOauthTokenT);
            Poll poll = parsePoll(new JSONObject(response));
            Bundle b = new Bundle();
            status.setPoll(poll);
            b.putParcelable("status", status);
            Intent intentBC = new Intent(Helper.RECEIVE_ACTION);
            intentBC.putExtras(b);
            SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
            Status alreadyCached = new TimelineCacheDAO(context, db).getSingle(status.getId());
            Account account = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT);
            if (alreadyCached != null) {
                poll = parsePoll(new JSONObject(response));
                status.setPoll(poll);
                new TimelineCacheDAO(context, db).update(status.getId(), Helper.statusToStringStorage(status), account.getId(), account.getInstance());
            }
            LocalBroadcastManager.getInstance(context).sendBroadcast(intentBC);
            return poll;
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Posts a status
     *
     * @param status Status object related to the status
     * @return APIResponse
     */
    public APIResponse postStatusAction(Status status) {

        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("status", status.getContent());
        if (status.getContentType() != null)
            jsonObject.addProperty("content_type", status.getContentType());
        if (status.getIn_reply_to_id() != null)
            jsonObject.addProperty("in_reply_to_id", status.getIn_reply_to_id());
        if (status.getMedia_attachments() != null && status.getMedia_attachments().size() > 0) {
            JsonArray mediaArray = new JsonArray();
            for (Attachment attachment : status.getMedia_attachments())
                mediaArray.add(attachment.getId());
            jsonObject.add("media_ids", mediaArray);
        }
        if (status.getScheduled_at() != null)
            jsonObject.addProperty("scheduled_at", status.getScheduled_at());
        if (status.isSensitive())
            jsonObject.addProperty("sensitive", status.isSensitive());
        if (status.getSpoiler_text() != null)
            jsonObject.addProperty("spoiler_text", status.getSpoiler_text());

        if (status.getPoll() != null) {
            JsonObject poll = new JsonObject();
            JsonArray options = new JsonArray();
            for (PollOptions option : status.getPoll().getOptionsList()) {
                if (!option.getTitle().isEmpty())
                    options.add(option.getTitle());
            }
            poll.add("options", options);
            poll.addProperty("expires_in", status.getPoll().getExpires_in());
            poll.addProperty("multiple", status.getPoll().isMultiple());
            jsonObject.add("poll", poll);
        }
        jsonObject.addProperty("visibility", status.getVisibility());
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.postJson(getAbsoluteUrl("/statuses"), 10, jsonObject, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            Status statusreturned = parseStatuses(context, new JSONObject(response));
            statuses.add(statusreturned);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
            setDefaultError(e);
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Posts a status
     *
     * @param notificationId String, the current notification id, if null all notifications are deleted
     * @return APIResponse
     */
    public APIResponse postNoticationAction(String notificationId) {

        String action;
        HashMap<String, String> params = new HashMap<>();
        if (notificationId == null)
            action = "/notifications/clear";
        else {
            action = "/notifications/" + notificationId + "/dismiss";
        }
        try {
            new HttpsConnection(context, this.instance).post(getAbsoluteUrl(action), 10, params, prefKeyOauthTokenT);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves notifications for the authenticated account since an id*synchronously*
     *
     * @param since_id String since max
     * @return APIResponse
     */
    public APIResponse getNotificationsSince(DisplayNotificationsFragment.Type type, String since_id, boolean display) {
        return getNotifications(type, null, since_id, notificationPerPage, display);
    }

    /**
     * Retrieves notifications for the authenticated account since an id*synchronously*
     *
     * @param since_id String since max
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getNotificationsSince(DisplayNotificationsFragment.Type type, String since_id, int notificationPerPage, boolean display) {
        return getNotifications(type, null, since_id, notificationPerPage, display);
    }

    /**
     * Retrieves notifications for the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getNotifications(DisplayNotificationsFragment.Type type, String max_id, boolean display) {
        return getNotifications(type, max_id, null, notificationPerPage, display);
    }

    /**
     * Retrieves notifications for the authenticated account *synchronously*
     *
     * @param max_id String id max
     * @return APIResponse
     */
    public APIResponse getNotifications(String max_id) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        params.put("limit", "30");
        if (context == null) {
            apiResponse = new APIResponse();
            Error error = new Error();
            apiResponse.setError(error);
            return apiResponse;
        }
        List<Notification> notifications = new ArrayList<>();

        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/notifications"), 15, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            notifications = parseNotificationResponse(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setNotifications(notifications);
        return apiResponse;
    }

    /**
     * Retrieves notifications for the authenticated account *synchronously*
     *
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    private APIResponse getNotifications(DisplayNotificationsFragment.Type type, String max_id, String since_id, int limit, boolean display) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 30)
            limit = 30;
        params.put("limit", String.valueOf(limit));

        if (context == null) {
            apiResponse = new APIResponse();
            Error error = new Error();
            apiResponse.setError(error);
            return apiResponse;
        }
        final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        boolean notif_follow, notif_add, notif_mention, notif_share, notif_poll;
        StringBuilder parameters = new StringBuilder();
        //TODO: If pixelfed supports exclude_types this condition needs to be changed
        if (type == DisplayNotificationsFragment.Type.ALL && MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED) {
            if (display) {
                notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW_FILTER, true);
                notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD_FILTER, true);
                notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION_FILTER, true);
                notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE_FILTER, true);
                notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL_FILTER, true);
            } else {
                notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
                notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
                notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
                notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
                notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
            }


            if (!notif_follow) {
                parameters.append("exclude_types[]=").append("follow").append("&");
                parameters.append("exclude_types[]=").append("follow_request").append("&");
            }
            if (!notif_add)
                parameters.append("exclude_types[]=").append("favourite").append("&");
            if (!notif_share)
                parameters.append("exclude_types[]=").append("reblog").append("&");
            if (!notif_mention)
                parameters.append("exclude_types[]=").append("mention").append("&");
            if (!notif_poll)
                parameters.append("exclude_types[]=").append("poll").append("&");
            if (parameters.length() > 0) {
                parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(16));
                params.put("exclude_types[]", parameters.toString());
            }
        } else if (type == DisplayNotificationsFragment.Type.MENTION) {
            parameters.append("exclude_types[]=").append("follow").append("&");
            parameters.append("exclude_types[]=").append("follow_request").append("&");
            parameters.append("exclude_types[]=").append("favourite").append("&");
            parameters.append("exclude_types[]=").append("reblog").append("&");
            parameters.append("exclude_types[]=").append("poll").append("&");
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(16));
            params.put("exclude_types[]", parameters.toString());
        } else if (type == DisplayNotificationsFragment.Type.FAVORITE) {
            parameters.append("exclude_types[]=").append("follow").append("&");
            parameters.append("exclude_types[]=").append("follow_request").append("&");
            parameters.append("exclude_types[]=").append("mention").append("&");
            parameters.append("exclude_types[]=").append("reblog").append("&");
            parameters.append("exclude_types[]=").append("poll").append("&");
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(16));
            params.put("exclude_types[]", parameters.toString());
        } else if (type == DisplayNotificationsFragment.Type.BOOST) {
            parameters.append("exclude_types[]=").append("follow").append("&");
            parameters.append("exclude_types[]=").append("follow_request").append("&");
            parameters.append("exclude_types[]=").append("mention").append("&");
            parameters.append("exclude_types[]=").append("favourite").append("&");
            parameters.append("exclude_types[]=").append("poll").append("&");
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(16));
            params.put("exclude_types[]", parameters.toString());
        } else if (type == DisplayNotificationsFragment.Type.POLL) {
            parameters.append("exclude_types[]=").append("reblog").append("&");
            parameters.append("exclude_types[]=").append("follow").append("&");
            parameters.append("exclude_types[]=").append("follow_request").append("&");
            parameters.append("exclude_types[]=").append("mention").append("&");
            parameters.append("exclude_types[]=").append("favourite").append("&");
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(16));
            params.put("exclude_types[]", parameters.toString());
        } else if (type == DisplayNotificationsFragment.Type.FOLLOW) {
            parameters.append("exclude_types[]=").append("reblog").append("&");
            parameters.append("exclude_types[]=").append("mention").append("&");
            parameters.append("exclude_types[]=").append("favourite").append("&");
            parameters.append("exclude_types[]=").append("poll").append("&");
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(16));
            params.put("exclude_types[]", parameters.toString());
        }

        List<Notification> notifications = new ArrayList<>();

        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/notifications"), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            notifications = parseNotificationResponse(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        if (apiResponse != null) {
            apiResponse.setNotifications(notifications);
        }
        return apiResponse;
    }

    /**
     * Changes media description
     *
     * @param mediaId     String
     * @param description String
     * @return Attachment
     */
    public Attachment updateDescription(String mediaId, String description) {

        HashMap<String, String> params = new HashMap<>();
        try {
            params.put("description", URLEncoder.encode(description, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            params.put("description", description);
        }
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.put(getAbsoluteUrl(String.format("/media/%s", mediaId)), 240, params, prefKeyOauthTokenT);
            attachment = parseAttachmentResponse(new JSONObject(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return attachment;
    }

    /**
     * Retrieves Accounts and feeds when searching *synchronously*
     *
     * @param query String search
     * @return Results
     */
    public APIResponse search(String query) {

        HashMap<String, String> params = new HashMap<>();
        apiResponse = new APIResponse();
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE)
            params.put("q", query);
        else
            try {
                params.put("q", URLEncoder.encode(query, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                params.put("q", query);
            }
        params.put("resolve", "true");
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUr2l("/search"), 10, params, prefKeyOauthTokenT);
            results = parseResultsResponse(new JSONObject(response));
            apiResponse.setResults(results);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves trends for Mastodon *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getTrends() {

        List<Trends> trends;
        apiResponse = new APIResponse();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/trends"), 10, null, null);
            trends = parseTrends(new JSONArray(response));
            apiResponse.setTrends(trends);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves Accounts and feeds when searching *synchronously*
     *
     * @param query String search
     * @return Results
     */
    public APIResponse search2(String query, searchType type, String offset) {
        apiResponse = new APIResponse();
        HashMap<String, String> params = new HashMap<>();
        if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE)
            params.put("q", query);
        else
            try {
                params.put("q", URLEncoder.encode(query, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                params.put("q", query);
            }
        if (offset != null)
            params.put("offset", offset);
        if (type != null) {
            switch (type) {
                case TAGS:
                    params.put("type", "hashtags");
                    break;
                case ACCOUNTS:
                    params.put("type", "accounts");
                    break;
                case STATUSES:
                    params.put("type", "statuses");
                    break;
            }
        }
        params.put("limit", "20");
        params.put("resolve", "true");
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUr2l("/search"), 10, params, prefKeyOauthTokenT);
            results = parseResultsResponse(new JSONObject(response));
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            apiResponse.setResults(results);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Retrieves Accounts when searching (ie: via @...) *synchronously*
     * Not limited to following
     *
     * @param query String search
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse searchAccounts(String query, int count) {
        return searchAccounts(query, count, false);
    }

    /**
     * Retrieves Accounts when searching (ie: via @...) *synchronously*
     *
     * @param query     String search
     * @param count     int limit
     * @param following boolean following only
     * @return APIResponse
     */
    public APIResponse searchAccounts(String query, int count, boolean following) {

        HashMap<String, String> params = new HashMap<>();
        params.put("q", query);
        if (count < 5)
            count = 5;
        if (count > 40)
            count = 40;
        if (following)
            params.put("following", Boolean.toString(true));
        params.put("limit", String.valueOf(count));

        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/accounts/search"), 10, params, prefKeyOauthTokenT);
            accounts = parseAccountResponse(new JSONArray(response));
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Retrieves Accounts when searching (ie: via @...) *synchronously*
     *
     * @return APIResponse
     */
    public APIResponse getCustomEmoji() {
        List<Emojis> emojis = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl("/custom_emojis"), 30, null, prefKeyOauthTokenT);
            emojis = parseEmojis(new JSONArray(response));

        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        //Add custom emoji for Pleroma
        /*if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) {
            APIResponse apiResponsePleroma = getCustomPleromaEmoji();
            if (apiResponsePleroma != null && apiResponsePleroma.getEmojis() != null && apiResponsePleroma.getEmojis().size() > 0)
                emojis.addAll(apiResponsePleroma.getEmojis());
        }*/
        apiResponse.setEmojis(emojis);
        return apiResponse;
    }

    /**
     * Check if it's a Pleroma admin account and change in settings *synchronously*
     */
    private void isPleromaAdmin(String nickname) {
        SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
        boolean isAdmin;
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            httpsConnection.get(String.format(Helper.getLiveInstanceWithProtocol(context) + "/api/pleroma/admin/permission_group/%s/admin", nickname), 10, null, prefKeyOauthTokenT);
            //Call didn't return a 404, so the account is admin
            isAdmin = true;
        } catch (Exception e) {
            isAdmin = false;
        }
        SharedPreferences.Editor editor = sharedpreferences.edit();
        editor.putBoolean(Helper.PREF_IS_ADMINISTRATOR, isAdmin);
        editor.apply();
    }

    /**
     * Get filters for the user
     *
     * @return APIResponse
     */
    public APIResponse getFilters() {

        List<Filters> filters = null;
        try {
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/filters"), 10, null, prefKeyOauthTokenT);
            if (response == null) {
                apiResponse.setFilters(new ArrayList<>());
                return apiResponse;
            }
            filters = parseFilters(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setFilters(filters);
        return apiResponse;
    }

    /**
     * Get a Filter by its id
     *
     * @return APIResponse
     */
    public APIResponse getFilters(String filterId) {

        List<Filters> filters = new ArrayList<>();
        Filters filter;
        try {
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl(String.format("/filters/%s", filterId)), 10, null, prefKeyOauthTokenT);
            filter = parseFilter(new JSONObject(response));
            filters.add(filter);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setFilters(filters);
        return apiResponse;
    }

    /**
     * Create a filter
     *
     * @param filter Filter
     * @return APIResponse
     */
    public APIResponse addFilters(Filters filter) {
        HashMap<String, String> params = new HashMap<>();
        params.put("phrase", filter.getPhrase());
        StringBuilder parameters = new StringBuilder();
        for (String context : filter.getContext())
            parameters.append("context[]=").append(context).append("&");
        if (parameters.length() > 0) {
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(10));
            params.put("context[]", parameters.toString());
        }
        params.put("irreversible", String.valueOf(filter.isIrreversible()));
        params.put("whole_word", String.valueOf(filter.isWhole_word()));
        params.put("expires_in", String.valueOf(filter.getExpires_in()));
        ArrayList<Filters> filters = new ArrayList<>();
        try {
            String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/filters"), 10, params, prefKeyOauthTokenT);
            Filters resfilter = parseFilter(new JSONObject(response));
            filters.add(resfilter);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setFilters(filters);
        return apiResponse;
    }

    /**
     * Delete a filter
     *
     * @param filter Filter
     * @return APIResponse
     */
    public int deleteFilters(Filters filter) {

        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            httpsConnection.delete(getAbsoluteUrl(String.format("/filters/%s", filter.getId())), 10, null, prefKeyOauthTokenT);
            actionCode = httpsConnection.getActionCode();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return actionCode;
    }

    /**
     * Delete a filter
     *
     * @param filter Filter
     * @return APIResponse
     */
    public APIResponse updateFilters(Filters filter) {
        HashMap<String, String> params = new HashMap<>();
        params.put("phrase", filter.getPhrase());
        StringBuilder parameters = new StringBuilder();
        for (String context : filter.getContext())
            parameters.append("context[]=").append(context).append("&");
        if (parameters.length() > 0) {
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(10));
            params.put("context[]", parameters.toString());
        }
        params.put("irreversible", String.valueOf(filter.isIrreversible()));
        params.put("whole_word", String.valueOf(filter.isWhole_word()));
        params.put("expires_in", String.valueOf(filter.getExpires_in()));
        ArrayList<Filters> filters = new ArrayList<>();
        try {
            String response = new HttpsConnection(context, this.instance).put(getAbsoluteUrl(String.format("/filters/%s", filter.getId())), 10, params, prefKeyOauthTokenT);
            Filters resfilter = parseFilter(new JSONObject(response));
            filters.add(resfilter);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setFilters(filters);
        return apiResponse;
    }

    /**
     * Get lists for the user
     *
     * @return APIResponse
     */
    public APIResponse getLists() {
        apiResponse = new APIResponse();
        List<app.fedilab.android.client.Entities.List> lists = new ArrayList<>();
        try {
            String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/lists"), 5, null, prefKeyOauthTokenT);
            lists = parseLists(new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setLists(lists);
        return apiResponse;
    }

    /**
     * Retrieves list timeline  *synchronously*
     *
     * @param list_id  String id of the list
     * @param max_id   String id max
     * @param since_id String since the id
     * @param limit    int limit  - max value 40
     * @return APIResponse
     */
    public APIResponse getListTimeline(String list_id, String max_id, String since_id, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (max_id != null)
            params.put("max_id", max_id);
        if (since_id != null)
            params.put("since_id", since_id);
        if (0 > limit || limit > 80)
            limit = 80;
        params.put("limit", String.valueOf(limit));
        statuses = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/timelines/list/%s", list_id)), 10, params, prefKeyOauthTokenT);
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
            statuses = parseStatuses(context, new JSONArray(response));
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return apiResponse;
    }

    /**
     * Get accounts in a list for a user
     *
     * @param listId String, id of the list
     * @param limit  int, limit of results
     * @return APIResponse
     */
    @SuppressWarnings("SameParameterValue")
    public APIResponse getAccountsInList(String listId, int limit) {

        HashMap<String, String> params = new HashMap<>();
        if (limit < 0)
            limit = 0;
        if (limit > 50)
            limit = 50;
        params.put("limit", String.valueOf(limit));
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrl(String.format("/lists/%s/accounts", listId)), 10, params, prefKeyOauthTokenT);
            accounts = parseAccountResponse(new JSONArray(response));
            apiResponse.setSince_id(httpsConnection.getSince_id());
            apiResponse.setMax_id(httpsConnection.getMax_id());
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setAccounts(accounts);
        return apiResponse;
    }

    /**
     * Add an account in a list
     *
     * @param id          String, id of the list
     * @param account_ids String, account to add
     * @return APIResponse
     */
    public APIResponse addAccountToList(String id, String[] account_ids) {

        HashMap<String, String> params = new HashMap<>();
        StringBuilder parameters = new StringBuilder();
        for (String val : account_ids)
            parameters.append("account_ids[]=").append(val).append("&");
        if (parameters.length() > 0) {
            parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(14));
            params.put("account_ids[]", parameters.toString());
        }
        try {
            new HttpsConnection(context, this.instance).post(getAbsoluteUrl(String.format("/lists/%s/accounts", id)), 10, params, prefKeyOauthTokenT);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) {
            e.printStackTrace();
        }
        return apiResponse;
    }

    /**
     * Delete an account from a list
     *
     * @param id String, the id of the list
     * @return APIResponse
     */
    public int deleteAccountFromList(String id, String[] account_ids) {
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            StringBuilder parameters = new StringBuilder();
            HashMap<String, String> params = new HashMap<>();
            for (String val : account_ids)
                parameters.append("account_ids[]=").append(val).append("&");
            if (parameters.length() > 0) {
                parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(14));
                params.put("account_ids[]", parameters.toString());
            }
            httpsConnection.delete(getAbsoluteUrl(String.format("/lists/%s/accounts", id)), 10, params, prefKeyOauthTokenT);
            actionCode = httpsConnection.getActionCode();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException | KeyManagementException | IOException e) {
            e.printStackTrace();
        }
        return actionCode;
    }

    /**
     * Posts a list
     *
     * @param title String, the title of the list
     * @return APIResponse
     */
    public APIResponse createList(String title) {

        HashMap<String, String> params = new HashMap<>();
        params.put("title", title);
        List<app.fedilab.android.client.Entities.List> lists = new ArrayList<>();
        app.fedilab.android.client.Entities.List list;
        try {
            String response = new HttpsConnection(context, this.instance).post(getAbsoluteUrl("/lists"), 10, params, prefKeyOauthTokenT);
            list = parseList(new JSONObject(response));
            lists.add(list);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setLists(lists);
        return apiResponse;
    }

    /**
     * Update a list by its id
     *
     * @param id    String, the id of the list
     * @param title String, the title of the list
     * @return APIResponse
     */
    public APIResponse updateList(String id, String title) {

        HashMap<String, String> params = new HashMap<>();
        params.put("title", title);
        List<app.fedilab.android.client.Entities.List> lists = new ArrayList<>();
        app.fedilab.android.client.Entities.List list;
        try {
            String response = new HttpsConnection(context, this.instance).put(getAbsoluteUrl(String.format("/lists/%s", id)), 10, params, prefKeyOauthTokenT);
            list = parseList(new JSONObject(response));
            lists.add(list);
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setLists(lists);
        return apiResponse;
    }

    /**
     * Delete a list by its id
     *
     * @param id String, the id of the list
     * @return APIResponse
     */
    public int deleteList(String id) {
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            httpsConnection.delete(getAbsoluteUrl(String.format("/lists/%s", id)), 10, null, prefKeyOauthTokenT);
            actionCode = httpsConnection.getActionCode();
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (Exception e) {
            setDefaultError(e);
        }
        return actionCode;
    }

    /**
     * Retrieves list from Communitywiki *synchronously*
     *
     * @return APIResponse
     */
    public ArrayList<String> getCommunitywikiList() {
        ArrayList<String> list = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrlCommunitywiki("/list"), 10, null, prefKeyOauthTokenT);

            JSONArray jsonArray = new JSONArray(response);
            int len = jsonArray.length();
            for (int i = 0; i < len; i++) {
                list.add(jsonArray.get(i).toString());
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return list;
    }

    /**
     * Retrieves list from Communitywiki *synchronously*
     *
     * @return APIResponse
     */
    public ArrayList<String> getCommunitywikiList(String name) {
        ArrayList<String> list = new ArrayList<>();
        try {
            HttpsConnection httpsConnection = new HttpsConnection(context, this.instance);
            String response = httpsConnection.get(getAbsoluteUrlCommunitywiki(String.format("/list/%s", name)), 10, null, prefKeyOauthTokenT);

            JSONArray jsonArray = new JSONArray(response);
            for (int i = 0; i < jsonArray.length(); i++) {
                try {
                    list.add(jsonArray.getJSONObject(i).getString("acct"));
                } catch (JSONException ignored) {
                }
            }
        } catch (HttpsConnection.HttpsConnectionException e) {
            setError(e.getStatusCode(), e);
        } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
            e.printStackTrace();
        }
        apiResponse.setStatuses(statuses);
        return list;
    }

    /**
     * Parse json response an unique account
     *
     * @param resobj JSONObject
     * @return Account
     */
    private Results parseResultsResponse(JSONObject resobj) {

        Results results = new Results();
        try {
            results.setAccounts(parseAccountResponse(resobj.getJSONArray("accounts")));
            results.setStatuses(parseStatuses(context, resobj.getJSONArray("statuses")));
            results.setTrends(parseTrends(resobj.getJSONArray("hashtags")));
            results.setHashtags(parseTags(resobj.getJSONArray("hashtags")));
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return results;
    }

    /**
     * Parse Domains
     *
     * @param jsonArray JSONArray
     * @return List<String> of domains
     */
    private List<String> parseDomains(JSONArray jsonArray) {
        List<String> list_tmp = new ArrayList<>();
        for (int i = 0; i < jsonArray.length(); i++) {
            try {
                list_tmp.add(jsonArray.getString(i));
            } catch (JSONException ignored) {
            }
        }
        return list_tmp;
    }

    /**
     * Parse Tags
     *
     * @param jsonArray JSONArray
     * @return List<String> of tags
     */
    private List<String> parseTags(JSONArray jsonArray) {
        List<String> list_tmp = new ArrayList<>();
        for (int i = 0; i < jsonArray.length(); i++) {

            try {
                if (jsonArray.get(i) instanceof JSONObject) {
                    list_tmp.add(jsonArray.getJSONObject(i).getString("name"));
                } else {
                    list_tmp.add(jsonArray.getString(i));
                }
            } catch (JSONException ignored) {
            }
        }
        return list_tmp;
    }

    /**
     * Parse json response for several howto
     *
     * @param jsonArray JSONArray
     * @return List<HowToVideo>
     */
    private List<HowToVideo> parseHowTos(JSONArray jsonArray) {

        List<HowToVideo> howToVideos = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                HowToVideo howToVideo = parseHowTo(resobj);
                i++;
                howToVideos.add(howToVideo);
            }

        } catch (JSONException e) {
            setDefaultError(e);
        }
        return howToVideos;
    }

    /**
     * Parse json response for several howto
     *
     * @param jsonArray JSONArray
     * @return List<Peertube>
     */
    private List<Peertube> parsePeertube(String instance, JSONArray jsonArray) {

        List<Peertube> peertubes = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Peertube peertube = parsePeertube(instance, resobj);
                i++;
                peertubes.add(peertube);
            }

        } catch (JSONException e) {
            setDefaultError(e);
        }
        return peertubes;
    }

    /**
     * Parse json response for several trends
     *
     * @param jsonArray JSONArray
     * @return List<Trends>
     */
    private List<Trends> parseTrends(JSONArray jsonArray) {

        List<Trends> trends = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Trends trend = parseTrends(resobj);
                i++;
                trends.add(trend);
            }

        } catch (JSONException e) {
            setDefaultError(e);
        }
        return trends;
    }

    /**
     * Parse json response for unique trend
     *
     * @param resobj JSONObject
     * @return Trend
     */
    private Trends parseTrends(JSONObject resobj) {
        Trends trend = new Trends();
        try {
            trend.setName(resobj.getString("name"));
            trend.setUrl(resobj.getString("url"));
            List<TrendsHistory> historyList = new ArrayList<>();
            if (resobj.has("history")) {
                JSONArray histories = resobj.getJSONArray("history");
                for (int i = 0; i < histories.length(); i++) {
                    JSONObject hystory = histories.getJSONObject(i);
                    TrendsHistory trendsHistory = new TrendsHistory();
                    trendsHistory.setDays(hystory.getLong("day"));
                    trendsHistory.setUses(hystory.getInt("uses"));
                    trendsHistory.setAccounts(hystory.getInt("accounts"));
                    historyList.add(trendsHistory);
                }
            }
            trend.setTrendsHistory(historyList);
        } catch (JSONException ignored) {
        }
        return trend;
    }

    /**
     * Parse json response for several conversations
     *
     * @param jsonArray JSONArray
     * @return List<Conversation>
     */
    private List<Conversation> parseConversations(JSONArray jsonArray) {

        List<Conversation> conversations = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Conversation conversation = parseConversation(context, resobj);
                i++;
                conversations.add(conversation);
            }

        } catch (JSONException e) {
            setDefaultError(e);
        }
        return conversations;
    }

    /**
     * Parse json response for unique conversation
     *
     * @param resobj JSONObject
     * @return Conversation
     */
    private Conversation parseConversation(Context context, JSONObject resobj) {
        Conversation conversation = new Conversation();
        try {
            conversation.setId(resobj.getString("id"));
            conversation.setUnread(resobj.getBoolean("unread"));
            conversation.setAccounts(parseAccountResponse(resobj.getJSONArray("accounts")));
            conversation.setLast_status(parseStatuses(context, resobj.getJSONObject("last_status")));
        } catch (JSONException ignored) {
        }
        return conversation;
    }

    /**
     * Parse json response for several status
     *
     * @param jsonArray JSONArray
     * @return List<Status>
     */
    private List<Status> parseStatusesForCache(Context context, JSONArray jsonArray) {
        SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
        Account account = new AccountDAO(context, db).getAccountByToken(prefKeyOauthTokenT);
        List<Status> statuses = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Status status = parseStatuses(context, resobj);
                Status alreadyCached = new TimelineCacheDAO(context, db).getSingle(status.getId());
                if (alreadyCached == null && account != null && account.getId() != null && account.getInstance() != null) {
                    new TimelineCacheDAO(context, db).insert(status.getId(), resobj.toString(), account.getId(), account.getInstance());
                }
                i++;
                statuses.add(status);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
        return statuses;
    }

    /**
     * Parse json response an unique instance
     *
     * @param resobj JSONObject
     * @return Instance
     */
    private Instance parseInstance(JSONObject resobj) {

        Instance instance = new Instance();
        try {
            instance.setUri(resobj.getString("uri"));
            instance.setTitle(resobj.getString("title"));
            instance.setDescription(resobj.getString("description"));
            instance.setEmail(resobj.getString("email"));
            instance.setVersion(resobj.getString("version"));
            if (resobj.has("registrations")) {
                instance.setRegistration(resobj.getBoolean("registrations"));
            } else {
                instance.setRegistration(false);
            }
            if (resobj.has("approval_required")) {
                instance.setApproval_required(resobj.getBoolean("approval_required"));
            } else {
                instance.setApproval_required(false);
            }
            if (resobj.has("poll_limits")) {
                HashMap<String, Integer> poll_limits = new HashMap<>();
                JSONObject polllimits = resobj.getJSONObject("poll_limits");
                poll_limits.put("min_expiration", polllimits.getInt("min_expiration"));
                poll_limits.put("max_options", polllimits.getInt("max_options"));
                poll_limits.put("max_option_chars", polllimits.getInt("max_option_chars"));
                poll_limits.put("max_expiration", polllimits.getInt("max_expiration"));
                instance.setPoll_limits(poll_limits);
            }
            if (resobj.has("thumbnail")) {
                instance.setThumbnail(resobj.getString("thumbnail"));
            }
            if (resobj.has("stats")) {
                JSONObject stats = resobj.getJSONObject("stats");
                if (stats.has("user_count")) {
                    instance.setUserCount(stats.getInt("user_count"));
                }
                if (stats.has("status_count")) {
                    instance.setStatusCount(stats.getInt("status_count"));
                }
                if (stats.has("domain_count")) {
                    instance.setDomainCount(stats.getInt("domain_count"));
                }
            }
            if (resobj.has("contact_account")) {
                instance.setContactAccount(parseAccountResponse(resobj.getJSONObject("contact_account")));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * Parse json response for several instance reg
     *
     * @param jsonArray JSONArray
     * @return List<Status>
     */
    private List<InstanceReg> parseInstanceReg(JSONArray jsonArray) {

        List<InstanceReg> instanceRegs = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                InstanceReg instanceReg = parseInstanceReg(resobj);
                i++;
                instanceRegs.add(instanceReg);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return instanceRegs;
    }

    /**
     * Parse json response an unique instance for registering
     *
     * @param resobj JSONObject
     * @return InstanceReg
     */
    private InstanceReg parseInstanceReg(JSONObject resobj) {
        InstanceReg instanceReg = new InstanceReg();
        try {
            instanceReg.setDomain(resobj.getString("domain"));
            instanceReg.setVersion(resobj.getString("version"));
            instanceReg.setDescription(resobj.getString("description"));
            instanceReg.setLanguage(resobj.getString("language"));
            instanceReg.setCategory(resobj.getString("category"));
            instanceReg.setProxied_thumbnail(resobj.getString("proxied_thumbnail"));
            instanceReg.setTotal_users(resobj.getInt("total_users"));
            instanceReg.setLast_week_users(resobj.getInt("last_week_users"));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return instanceReg;
    }

    /**
     * Parse emojis
     *
     * @param jsonArray JSONArray
     * @return List<Emojis> of emojis
     */
    private List<Emojis> parseEmojis(JSONArray jsonArray) {
        List<Emojis> emojis = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Emojis emojis1 = parseEmojis(resobj);
                if (emojis1.isVisible_in_picker())
                    emojis.add(emojis1);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return emojis;
    }

    /**
     * Parse Filters
     *
     * @param jsonArray JSONArray
     * @return List<Filters> of filters
     */
    private List<Filters> parseFilters(JSONArray jsonArray) {
        List<Filters> filters = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Filters filter = parseFilter(resobj);
                if (filter != null)
                    filters.add(filter);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return filters;
    }

    /**
     * Parse json response for filter
     *
     * @param resobj JSONObject
     * @return Filter
     */
    private Filters parseFilter(JSONObject resobj) {
        Filters filter = new Filters();
        try {

            filter.setId(resobj.getString("id"));
            filter.setPhrase(resobj.getString("phrase"));
            resobj.get("expires_at");
            if (!resobj.getString("expires_at").equals("null"))
                filter.setSetExpires_at(Helper.mstStringToDate(resobj.getString("expires_at")));
            filter.setWhole_word(resobj.getBoolean("whole_word"));
            filter.setIrreversible(resobj.getBoolean("irreversible"));
            String contextString = resobj.getString("context");
            contextString = contextString.replaceAll("\\[", "");
            contextString = contextString.replaceAll("]", "");
            contextString = contextString.replaceAll("\"", "");
            String[] context = contextString.split(",");
            if (contextString.length() > 0) {
                ArrayList<String> finalContext = new ArrayList<>();
                for (String c : context)
                    finalContext.add(c.trim());
                filter.setContext(finalContext);
            }
            return filter;
        } catch (Exception ignored) {
            return null;
        }

    }

    /**
     * Parse Lists
     *
     * @param jsonArray JSONArray
     * @return List<List> of lists
     */
    private List<app.fedilab.android.client.Entities.List> parseLists(JSONArray jsonArray) {
        List<app.fedilab.android.client.Entities.List> lists = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                app.fedilab.android.client.Entities.List list = parseList(resobj);
                lists.add(list);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return lists;
    }

    private List<Account> parseAccountResponsePeertube(String instance, JSONArray jsonArray) {
        List<Account> accounts = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Account account = parseAccountResponsePeertube(instance, resobj);
                accounts.add(account);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return accounts;
    }

    /**
     * Parse json response for list of reports for admins
     *
     * @param jsonArray JSONArray
     * @return List<Report>
     */
    private List<Report> parseReportAdminResponse(JSONArray jsonArray) {

        List<Report> reports = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Report report = parseReportAdminResponse(context, resobj);
                reports.add(report);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return reports;
    }

    /**
     * Parse json response for list of accounts for admins
     *
     * @param jsonArray JSONArray
     * @return List<AccountAdmin>
     */
    private List<AccountAdmin> parseAccountAdminResponse(JSONArray jsonArray) {

        List<AccountAdmin> accountAdmins = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                AccountAdmin accountAdmin = parseAccountAdminResponse(resobj);
                accountAdmins.add(accountAdmin);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return accountAdmins;
    }

    /**
     * Parse json response for list of accounts
     *
     * @param jsonArray JSONArray
     * @return List<Account>
     */
    private List<Account> parseAccountResponse(JSONArray jsonArray) {

        List<Account> accounts = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Account account = parseAccountResponse(resobj);
                accounts.add(account);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return accounts;
    }

    /**
     * Parse json response an unique relationship
     *
     * @param resobj JSONObject
     * @return Relationship
     */
    private Relationship parseRelationshipResponse(JSONObject resobj) {

        Relationship relationship = new Relationship();
        try {
            relationship.setId(resobj.getString("id"));
            relationship.setFollowing(resobj.getBoolean("following"));
            relationship.setFollowed_by(resobj.getBoolean("followed_by"));
            relationship.setBlocking(resobj.getBoolean("blocking"));
            relationship.setMuting(resobj.getBoolean("muting"));
            try {
                relationship.setMuting_notifications(resobj.getBoolean("muting_notifications"));
            } catch (Exception ignored) {
                relationship.setMuting_notifications(true);
            }
            try {
                relationship.setEndorsed(resobj.getBoolean("endorsed"));
            } catch (Exception ignored) {
                relationship.setMuting_notifications(false);
            }
            try {
                relationship.setShowing_reblogs(resobj.getBoolean("showing_reblogs"));
            } catch (Exception ignored) {
                relationship.setMuting_notifications(false);
            }
            try {
                relationship.setBlocked_by(resobj.getBoolean("blocked_by"));
            } catch (Exception ignored) {
                relationship.setBlocked_by(false);
            }
            relationship.setRequested(resobj.getBoolean("requested"));
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return relationship;
    }

    /**
     * Parse json response for list of relationship
     *
     * @param jsonArray JSONArray
     * @return List<Relationship>
     */
    private List<Relationship> parseRelationshipResponse(JSONArray jsonArray) {

        List<Relationship> relationships = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {
                JSONObject resobj = jsonArray.getJSONObject(i);
                Relationship relationship = parseRelationshipResponse(resobj);
                relationships.add(relationship);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return relationships;
    }

    /**
     * Parse json response for the context
     *
     * @param jsonObject JSONObject
     * @return Context
     */
    private app.fedilab.android.client.Entities.Context parseContext(JSONObject jsonObject) {

        app.fedilab.android.client.Entities.Context context = new app.fedilab.android.client.Entities.Context();
        try {
            context.setAncestors(parseStatuses(this.context, jsonObject.getJSONArray("ancestors")));
            context.setDescendants(parseStatuses(this.context, jsonObject.getJSONArray("descendants")));
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return context;
    }

    /**
     * Parse json response for list of notifications
     *
     * @param jsonArray JSONArray
     * @return List<Notification>
     */
    private List<Notification> parseNotificationResponse(JSONArray jsonArray) {

        List<Notification> notifications = new ArrayList<>();
        try {
            int i = 0;
            while (i < jsonArray.length()) {

                JSONObject resobj = jsonArray.getJSONObject(i);
                Notification notification = parseNotificationResponse(context, resobj);
                notifications.add(notification);
                i++;
            }
        } catch (JSONException e) {
            setDefaultError(e);
        }
        return notifications;
    }

    /**
     * Set the error message
     *
     * @param statusCode int code
     * @param error      Throwable error
     */
    private void setError(int statusCode, Throwable error) {
        APIError = new Error();
        APIError.setStatusCode(statusCode);
        String message = statusCode + " - " + error.getMessage();
        try {
            JSONObject jsonObject = new JSONObject(Objects.requireNonNull(error.getMessage()));
            String errorM = jsonObject.getString("error");
            message = "Error " + statusCode + " : " + errorM;
        } catch (JSONException e) {
            if (error.getMessage().split(".").length > 0) {
                String errorM = error.getMessage().split(".")[0];
                message = "Error " + statusCode + " : " + errorM;
            }
        }
        APIError.setError(message);
        apiResponse.setError(APIError);
    }

    private void setDefaultError(Exception e) {
        APIError = new Error();
        if (apiResponse == null) {
            apiResponse = new APIResponse();
        }
        if (e.getLocalizedMessage() != null && e.getLocalizedMessage().trim().length() > 0)
            APIError.setError(e.getLocalizedMessage());
        else if (e.getMessage() != null && e.getMessage().trim().length() > 0)
            APIError.setError(e.getMessage());
        else
            APIError.setError(context.getString(R.string.toast_error));
        apiResponse.setError(APIError);
    }

    public Error getError() {
        return APIError;
    }

    private String getAbsoluteUrl(String action) {
        return Helper.instanceWithProtocol(this.context, this.instance) + "/api/v1" + action;
    }

    private String getAbsoluteUr2l(String action) {
        return Helper.instanceWithProtocol(this.context, this.instance) + "/api/v2" + action;
    }

    private String getAbsoluteUrlRemote(String remote, String action) {
        return "https://" + remote + "/api/v1" + action;
    }

    private String getAbsoluteUrlRemoteInstance(String instanceName) {
        return "https://" + instanceName + "/api/v1/timelines/public?local=true";
    }

    private String getAbsoluteUrlCommunitywiki(String action) {
        return "https://communitywiki.org/trunk/api/v1" + action;
    }

    public enum searchType {
        TAGS,
        STATUSES,
        ACCOUNTS
    }

    public enum adminAction {
        ENABLE,
        APPROVE,
        REJECT,
        NONE,
        SILENCE,
        DISABLE,
        UNSILENCE,
        SUSPEND,
        UNSUSPEND,
        ASSIGN_TO_SELF,
        UNASSIGN,
        REOPEN,
        RESOLVE,
        GET_ACCOUNTS,
        GET_ONE_ACCOUNT,
        GET_REPORTS,
        GET_ONE_REPORT
    }

    public enum StatusAction {
        FAVOURITE,
        UNFAVOURITE,
        BOOKMARK,
        UNBOOKMARK,
        REBLOG,
        UNREBLOG,
        MUTE,
        MUTE_NOTIFICATIONS,
        UNMUTE,
        MUTE_CONVERSATION,
        UNMUTE_CONVERSATION,
        BLOCK,
        UNBLOCK,
        FOLLOW,
        UNFOLLOW,
        CREATESTATUS,
        UNSTATUS,
        AUTHORIZE,
        REJECT,
        REPORT,
        REMOTE_FOLLOW,
        PIN,
        UNPIN,
        REACT,
        UNREACT,
        ENDORSE,
        UNENDORSE,
        SHOW_BOOST,
        HIDE_BOOST,
        BLOCK_DOMAIN,
        RATEVIDEO,
        PEERTUBECOMMENT,
        PEERTUBEREPLY,
        PEERTUBEDELETECOMMENT,
        PEERTUBEDELETEVIDEO,
        UPDATESERVERSCHEDULE,
        DELETESCHEDULED,
        REFRESHPOLL,
        ADD_REACTION,
        REMOVE_REACTION,
        ADD_PLEROMA_REACTION,
        REMOVE_PLEROMA_REACTION,
        DISMISS_ANNOUNCEMENT
    }

    public enum accountPrivacy {
        PUBLIC,
        LOCKED
    }

}
