/*
 * Copyright (c) 2015, Motorola Mobility LLC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     - Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     - Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     - Neither the name of Motorola Mobility nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

package com.android.service.ims.presence;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import android.text.format.Time;

import com.android.ims.internal.Logger;

import java.util.ArrayList;
import java.util.List;

public class PollingTask {
    private Logger logger = Logger.getLogger(this.getClass().getName());
    private Context mContext = null;

    private static long sMaxId = 0;
    public static final String ACTION_POLLING_RETRY_ALARM =
            "com.android.service.ims.presence.capability_polling_retry";
    private PendingIntent mRetryAlarmIntent = null;

    public long mId;
    public int mType;
    public List<Contacts.Item> mContacts = new ArrayList<Contacts.Item>();

    private long mTimeUnit;
    private int mTotalRetry;
    private int mCurrentRetry;
    private long mLastUpdateTime;

    private boolean mCancelled = false;
    private boolean mCompleted = false;

    public PollingTask(int type, List<Contacts.Item> list) {
        mId = sMaxId++;
        mType = type;

        mContacts.clear();
        for(int i = 0; i < list.size(); i++) {
            Contacts.Item item = list.get(i);
            mContacts.add(item);
        }

        mCurrentRetry = 0;
        mTotalRetry = 5;
        mTimeUnit = 1800; // 1800s = 30 minutes
        if (CapabilityPolling.ACTION_POLLING_NEW_CONTACTS == mType) {
            mTotalRetry = 4;
            mTimeUnit = 60; // 60s = 1 minute
        }
        mLastUpdateTime = 0;
    }

    public void execute() {
        PollingsQueue queue = PollingsQueue.getInstance(null);
        if (queue == null) {
            return;
        }

        PollingAction action = new PollingAction(queue.getContext(), this);
        action.execute();
    }

    public void finish(boolean fullUpdated) {
        cancelRetryAlarm();

        PollingsQueue queue = PollingsQueue.getInstance(null);
        if (queue == null) {
            return;
        }

        mCompleted = fullUpdated ? true : false;
        queue.remove(this);
    }

    public void retry() {
        mCurrentRetry += 1;
        logger.print("retry mCurrentRetry=" + mCurrentRetry);
        if (mCurrentRetry > mTotalRetry) {
            logger.print("Retry finished for task: " + this);
            updateTimeStampToCurrent();
            finish(false);
            return;
        }

        if (mCancelled) {
            logger.print("Task is cancelled: " + this);
            finish(false);
            return;
        }

        long delay = mTimeUnit;
        for (int i = 0; i < mCurrentRetry - 1; i++) {
            delay *= 2;
        }

        logger.print("retry delay=" + delay);

        scheduleRetry(delay * 1000);
    }

    public void cancel() {
        mCancelled = true;
        logger.print("Cancel this task: " + this);

        if (mRetryAlarmIntent != null) {
            cancelRetryAlarm();
            finish(false);
        }
    }

    public void onPreExecute() {
        logger.print("onPreExecute(), id = " + mId);
        cancelRetryAlarm();
    }

    public void onPostExecute(int result) {
        logger.print("onPostExecute(), result = " + result);
        mLastUpdateTime = System.currentTimeMillis();
    }

    private void updateTimeStampToCurrent() {
        if (mContext == null) {
            return;
        }

        EABContactManager contactManager = new EABContactManager(
                mContext.getContentResolver(), mContext.getPackageName());
        for (int i = 0; i < mContacts.size(); i++) {
            Contacts.Item item = mContacts.get(i);

            String number = item.number();
            long current = System.currentTimeMillis();
            EABContactManager.Request request = new EABContactManager.Request(number)
                    .setLastUpdatedTimeStamp(current);
            int result = contactManager.update(request);
            if (result <= 0) {
                logger.debug("failed to update timestamp for contact: ");
            }
        }
    }

    private void cancelRetryAlarm() {
        if (mRetryAlarmIntent != null) {
            if (mContext != null) {
                AlarmManager alarmManager = (AlarmManager)
                        mContext.getSystemService(Context.ALARM_SERVICE);
                alarmManager.cancel(mRetryAlarmIntent);
            }
            mRetryAlarmIntent = null;
        }
    }

    private void scheduleRetry(long msec) {
        logger.print("scheduleRetry msec=" + msec);

        cancelRetryAlarm();

        if (mContext == null) {
            PollingsQueue queue = PollingsQueue.getInstance(null);
            if (queue != null) {
                mContext = queue.getContext();
            }
        }

        if (mContext != null) {
            Intent intent = new Intent(ACTION_POLLING_RETRY_ALARM);
            intent.setClass(mContext, AlarmBroadcastReceiver.class);
            intent.putExtra("pollingTaskId", mId);

            mRetryAlarmIntent = PendingIntent.getBroadcast(mContext, 0, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

            AlarmManager alarmManager = (AlarmManager)
                    mContext.getSystemService(Context.ALARM_SERVICE);
            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    SystemClock.elapsedRealtime() + msec, mRetryAlarmIntent);
        }
    }

    private String getTimeString(long time) {
        if (time <= 0) {
            time = System.currentTimeMillis();
        }

        Time tobj = new Time();
        tobj.set(time);
        return String.format("%s.%s", tobj.format("%m-%d %H:%M:%S"), time % 1000);
    }

    public boolean isCompleted() {
        return mCompleted;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PollingTask)) return false;

        PollingTask that = (PollingTask)o;
        return (this.mId == that.mId) && (this.mType == that.mType);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("PollingTask { ");
        sb.append("\nId: " + mId);
        sb.append("\nType: " + mType);
        sb.append("\nCurrentRetry: " + mCurrentRetry);
        sb.append("\nTotalRetry: " + mTotalRetry);
        sb.append("\nTimeUnit: " + mTimeUnit);
        sb.append("\nLast update time: " + mLastUpdateTime + "(" +
                getTimeString(mLastUpdateTime) + ")");
        sb.append("\nContacts: " + mContacts.size());
        for (int i = 0; i < mContacts.size(); i++) {
            sb.append("\n");
            Contacts.Item item = mContacts.get(i);
            sb.append("Number " + i + ": " + item.number());
        }
        sb.append("\nCompleted: " + mCompleted);
        sb.append(" }");
        return sb.toString();
    }
}

