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

package com.android.mms.util;

import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.database.sqlite.SqliteWrapper;
import android.provider.Telephony.Mms.Rate;
import timber.log.Timber;

public class RateController {
    private static final boolean LOCAL_LOGV = false;

    private static final int RATE_LIMIT = 100;
    private static final long ONE_HOUR = 1000 * 60 * 60;

    private static final int NO_ANSWER  = 0;
    private static final int ANSWER_YES = 1;
    private static final int ANSWER_NO  = 2;

    public static final int ANSWER_TIMEOUT = 20000;
    public static final String RATE_LIMIT_SURPASSED_ACTION =
        "com.android.mms.RATE_LIMIT_SURPASSED";
    public static final String RATE_LIMIT_CONFIRMED_ACTION =
        "com.android.mms.RATE_LIMIT_CONFIRMED";

    private static RateController sInstance;
    private static boolean sMutexLock;

    private final Context mContext;
    private int mAnswer;

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (LOCAL_LOGV) {
                Timber.v("Intent received: " + intent);
            }

            if (RATE_LIMIT_CONFIRMED_ACTION.equals(intent.getAction())) {
                synchronized (this) {
                    mAnswer = intent.getBooleanExtra("answer", false)
                                            ? ANSWER_YES : ANSWER_NO;
                    notifyAll();
                }
            }
        }
    };

    private RateController(Context context) {
        mContext = context;
    }

    public static void init(Context context) {
        if (LOCAL_LOGV) {
            Timber.v("RateController.init()");
        }

        if (sInstance != null) {
            Timber.w("Already initialized.");
            return;
        }
        sInstance = new RateController(context);
    }

    public static RateController getInstance() {
        if (sInstance == null) {
            throw new IllegalStateException("Uninitialized.");
        }
        return sInstance;
    }

    public final void update() {
        ContentValues values = new ContentValues(1);
        values.put(Rate.SENT_TIME, System.currentTimeMillis());
        SqliteWrapper.insert(mContext, mContext.getContentResolver(),
                             Rate.CONTENT_URI, values);
    }

    public final boolean isLimitSurpassed() {
        long oneHourAgo = System.currentTimeMillis() - ONE_HOUR;
        Cursor c = SqliteWrapper.query(mContext, mContext.getContentResolver(),
                Rate.CONTENT_URI, new String[] { "COUNT(*) AS rate" },
                Rate.SENT_TIME + ">" + oneHourAgo, null, null);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    boolean limit = c.getInt(0) >= RATE_LIMIT;
                    c.close();
                    return limit;
                }
            } finally {
                c.close();
            }
        }
        return false;
    }

    synchronized public boolean isAllowedByUser() {
        while (sMutexLock) {
            try {
                wait();
            } catch (InterruptedException e) {
                 // Ignore it.
            }
        }
        sMutexLock = true;

        mContext.registerReceiver(mBroadcastReceiver,
                new IntentFilter(RATE_LIMIT_CONFIRMED_ACTION));

        mAnswer = NO_ANSWER;
        try {
            Intent intent = new Intent(RATE_LIMIT_SURPASSED_ACTION);
            // Using NEW_TASK here is necessary because we're calling
            // startActivity from outside an activity.
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mContext.startActivity(intent);
            return waitForAnswer() == ANSWER_YES;
        } finally {
            mContext.unregisterReceiver(mBroadcastReceiver);
            sMutexLock = false;
            notifyAll();
        }
    }

    synchronized private int waitForAnswer() {
        for (int t = 0; (mAnswer == NO_ANSWER) && (t < ANSWER_TIMEOUT); t += 1000) {
            try {
                if (LOCAL_LOGV) {
                    Timber.v("Waiting for answer..." + t / 1000);
                }
                wait(1000L);
            } catch (InterruptedException e) {
                 // Ignore it.
            }
        }
        return mAnswer;
    }
}
