/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * 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.server;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.UserHandle;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AbstractFuture;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * {@link ContextWrapper} that can attach listeners for upcoming
 * {@link Context#sendBroadcast(Intent)}.
 */
public class BroadcastInterceptingContext extends ContextWrapper {
    private static final String TAG = "WatchingContext";

    private final List<BroadcastInterceptor> mInterceptors = Lists.newArrayList();

    public class BroadcastInterceptor extends AbstractFuture<Intent> {
        private final BroadcastReceiver mReceiver;
        private final IntentFilter mFilter;

        public BroadcastInterceptor(BroadcastReceiver receiver, IntentFilter filter) {
            mReceiver = receiver;
            mFilter = filter;
        }

        public boolean dispatchBroadcast(Intent intent) {
            if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
                if (mReceiver != null) {
                    final Context context = BroadcastInterceptingContext.this;
                    mReceiver.onReceive(context, intent);
                    return false;
                } else {
                    set(intent);
                    return true;
                }
            } else {
                return false;
            }
        }

        @Override
        public Intent get() throws InterruptedException, ExecutionException {
            try {
                return get(5, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public BroadcastInterceptingContext(Context base) {
        super(base);
    }

    public Future<Intent> nextBroadcastIntent(String action) {
        return nextBroadcastIntent(new IntentFilter(action));
    }

    public Future<Intent> nextBroadcastIntent(IntentFilter filter) {
        final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter);
        synchronized (mInterceptors) {
            mInterceptors.add(interceptor);
        }
        return interceptor;
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mInterceptors) {
            mInterceptors.add(new BroadcastInterceptor(receiver, filter));
        }
        return null;
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiver(receiver, filter);
    }

    @Override
    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mInterceptors) {
            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
            while (i.hasNext()) {
                final BroadcastInterceptor interceptor = i.next();
                if (receiver.equals(interceptor.mReceiver)) {
                    i.remove();
                }
            }
        }
    }

    @Override
    public void sendBroadcast(Intent intent) {
        synchronized (mInterceptors) {
            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
            while (i.hasNext()) {
                final BroadcastInterceptor interceptor = i.next();
                if (interceptor.dispatchBroadcast(intent)) {
                    i.remove();
                }
            }
        }
    }

    @Override
    public void sendBroadcast(Intent intent, String receiverPermission) {
        sendBroadcast(intent);
    }

    @Override
    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
        sendBroadcast(intent);
    }

    @Override
    public void sendBroadcastAsUser(Intent intent, UserHandle user,
            String receiverPermission) {
        sendBroadcast(intent);
    }

    @Override
    public void sendStickyBroadcast(Intent intent) {
        sendBroadcast(intent);
    }

    @Override
    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
        sendBroadcast(intent);
    }

    @Override
    public void removeStickyBroadcast(Intent intent) {
        // ignored
    }
}
