/*
 *  Zandy
 *  Based in part on Mendroid, Copyright 2011 Martin Paul Eve <martin@martineve.com>
 *
 *  This file is part of Zandy.
 *
 *  Zandy is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *   
 *  Zandy 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 Zandy.  If not, see <http://www.gnu.org/licenses/>.
 *  
 */

package com.gimranov.zandy.app.task;

import java.util.ArrayList;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.gimranov.zandy.app.ServerCredentials;
import com.gimranov.zandy.app.XMLResponseParser;
import com.gimranov.zandy.app.data.Attachment;
import com.gimranov.zandy.app.data.Database;
import com.gimranov.zandy.app.data.Item;

/**
 * Executes one or more API requests asynchronously.
 * <p>
 * Steps in migration:
 * 1. Move the logic on what kind of request is handled how into the APIRequest itself
 * 2. Throw exceptions when we have errors
 * 3. Call the handlers when provided
 * 4. Move aggressive syncing logic out of ZoteroAPITask itself; it should be elsewhere.
 *
 * @author ajlyon
 */
public class ZoteroAPITask extends AsyncTask<APIRequest, Message, Message> {
    private static final String TAG = ZoteroAPITask.class.getSimpleName();

    public ArrayList<APIRequest> deletions;
    public ArrayList<APIRequest> queue;

    public int syncMode = -1;

    public static final int AUTO_SYNC_STALE_COLLECTIONS = 1;

    public boolean autoMode = false;

    private Database db;
    private ServerCredentials cred;

    private Handler handler;

    public ZoteroAPITask(Context c) {
        queue = new ArrayList<APIRequest>();
        cred = new ServerCredentials(c);
        /* TODO reenable in a working way
		if (settings.getBoolean("sync_aggressively", false))
			syncMode = AUTO_SYNC_STALE_COLLECTIONS;
		*/
        deletions = APIRequest.delete(c);
        db = new Database(c);
    }

    public void setHandler(Handler h) {
        handler = h;
    }

    private Handler getHandler() {
        if (handler == null) {
            handler = new Handler() {
            };
        }
        return handler;
    }

    @Override
    protected Message doInBackground(APIRequest... params) {
        return doFetch(params);
    }

    @SuppressWarnings("unused")
    public Message doFetch(APIRequest... reqs) {
        int count = reqs.length;

        for (int i = 0; i < count; i++) {
            if (reqs[i] == null) {
                Log.d(TAG, "Skipping null request");
                continue;
            }

            // Just in case we missed something, we fix the user ID right here too,
            // and we set the key as well.
            reqs[i] = cred.prep(reqs[i]);

            try {
                Log.i(TAG, "Executing API call: " + reqs[i].query);
                reqs[i].issue(db, cred);
                Log.i(TAG, "Successfully retrieved API call: " + reqs[i].query);
                reqs[i].succeeded(db);

            } catch (APIException e) {
                Log.e(TAG, "Failed to execute API call: " + e.request.query, e);
                e.request.status = APIRequest.REQ_FAILING + e.request.getHttpStatus();
                e.request.save(db);
                Message msg = Message.obtain();
                msg.arg1 = APIRequest.ERROR_UNKNOWN + e.request.getHttpStatus();
                return msg;
            }

            // The XML parser's queue is simply from following continuations in the paged
            // feed. We shouldn't split out its requests...
            if (XMLResponseParser.queue != null && !XMLResponseParser.queue.isEmpty()) {
                Log.i(TAG, "Finished call, but adding " +
                        XMLResponseParser.queue.size() +
                        " items to queue.");
                queue.addAll(XMLResponseParser.queue);
                XMLResponseParser.queue.clear();
            } else {
                Log.i(TAG, "Finished call, and parser's request queue is empty");
            }
        }

        // 
        if (queue.size() > 0) {
            // If the last batch saw unchanged items, don't follow the Atom
            // continuations; just run the child requests
            // XXX This is disabled for now
            if (false && !XMLResponseParser.followNext) {
                ArrayList<APIRequest> toRemove = new ArrayList<APIRequest>();
                for (APIRequest r : queue) {
                    if (r.type != APIRequest.ITEMS_CHILDREN) {
                        Log.d(TAG, "Removing request from queue since last page had old items: " + r.query);
                        toRemove.add(r);
                    }
                }
                queue.removeAll(toRemove);
            }
            Log.i(TAG, "Starting queued requests: " + queue.size() + " requests");
            APIRequest[] templ = {};
            APIRequest[] requests = queue.toArray(templ);
            queue.clear();
            Log.i(TAG, "Queue size now: " + queue.size());
            // XXX I suspect that this calling of doFetch from doFetch might be the cause of our
            // out-of-memory situations. We may be able to accomplish the same thing by expecting
            // the code listening to our handler to fetch again if QUEUED_MORE is received. In that
            // case, we could just save our queue here and really return.

            // XXX Test: Here, we try to use doInBackground instead
            doInBackground(requests);

            // Return a message with the number of requests added to the queue
            Message msg = Message.obtain();
            msg.arg1 = APIRequest.QUEUED_MORE;
            msg.arg2 = requests.length;
            return msg;
        }


        // Here's where we tie in to periodic housekeeping syncs
        // If we're already in auto mode (that is, here), just move on
        if (autoMode) {
            Message msg = Message.obtain();
            msg.arg1 = APIRequest.UPDATED_DATA;
            return msg;
        }

        Log.d(TAG, "Sending local changes");
        Item.queue(db);
        Attachment.queue(db);

        APIRequest[] templ = {};

        ArrayList<APIRequest> list = new ArrayList<APIRequest>();
        for (Item i : Item.queue) {
            list.add(cred.prep(APIRequest.update(i)));
        }

        for (Attachment a : Attachment.queue) {
            list.add(cred.prep(APIRequest.update(a, db)));
        }

        // This queue has deletions, collection memberships, and failing requests
        // We may want to filter it in the future
        list.addAll(APIRequest.queue(db));

        // We're in auto mode...
        autoMode = true;
        doInBackground(list.toArray(templ));

        // Return a message noting that we've queued more requests
        Message msg = Message.obtain();
        msg.arg1 = APIRequest.QUEUED_MORE;
        msg.arg2 = list.size();
        return msg;
    }


    @Override
    protected void onPostExecute(Message result) {
        getHandler().sendMessage(result);
    }
}
