package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

public class VideoCacheServiceConnection {
    private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

    private HashSet<String> mBindCount = new HashSet<String>();

    private IVideoCacheService mVideoCacheService;
    private final ServiceConnection mVideoCacheServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, Log.buf().append("onServiceConnected")
                        .append(name.toString()).append(' ')
                        .append(service.toString()).toString());
            }
            mVideoCacheService = IVideoCacheService.Stub.asInterface(service);

            try {
                mVideoCacheService.addListener(mVideoCacheServiceCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, e.toString(), e);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, Log.buf().append("onServiceDisconnected")
                        .append(name.toString()).toString());
            }
            try {
                mVideoCacheService.removeListener(mVideoCacheServiceCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, e.toString(), e);
            }

            mVideoCacheService = null;

//            Intent intent = new Intent(IVideoCacheService.class.getName());
//            mContext.stopService(intent);
        }

    };

    private static class VideoCacheServiceCallbackStub extends IVideoCacheServiceCallback.Stub {
        private WeakReference<VideoCacheServiceConnection> mRef;

        public VideoCacheServiceCallbackStub(VideoCacheServiceConnection connection) {
            mRef = new WeakReference<VideoCacheServiceConnection>(connection);
        }

        @Override
        public void onNotifyProgress(String videoV, int ecoType, int seekOffsetWrite,
                int contentLength) throws RemoteException {
            VideoCacheServiceConnection thiz = mRef.get();
            if (thiz == null) {
                return;
            }
            Iterator<WeakReference<Callback>> it = thiz.mCallbacks.iterator();
            while (it.hasNext()) {
                Callback callback = it.next().get();
                if (callback == null) {
                    it.remove();
                } else {
                    callback.onNotifyProgress(videoV, ecoType, seekOffsetWrite, contentLength);
                }
            }
        }

        @Override
        public void onStart(String videoV, int ecoType) throws RemoteException {
            VideoCacheServiceConnection thiz = mRef.get();
            if (thiz == null) {
                return;
            }
            Iterator<WeakReference<Callback>> it = thiz.mCallbacks.iterator();
            while (it.hasNext()) {
                Callback callback = it.next().get();
                if (callback == null) {
                    it.remove();
                } else {
                    callback.onStart(videoV, ecoType);
                }
            }
        }

        @Override
        public void onFinished(String videoV, int ecoType) throws RemoteException {
            VideoCacheServiceConnection thiz = mRef.get();
            if (thiz == null) {
                return;
            }
            Iterator<WeakReference<Callback>> it = thiz.mCallbacks.iterator();
            while (it.hasNext()) {
                Callback callback = it.next().get();
                if (callback == null) {
                    it.remove();
                } else {
                    callback.onFinished(videoV, ecoType);
                }
            }
        }

        @Override
        public void onAllFinished() throws RemoteException {
            VideoCacheServiceConnection thiz = mRef.get();
            if (thiz == null) {
                return;
            }
            Iterator<WeakReference<Callback>> it = thiz.mCallbacks.iterator();
            while (it.hasNext()) {
                Callback callback = it.next().get();
                if (callback == null) {
                    it.remove();
                } else {
                    callback.onAllFinished();
                }
            }
        }

        @Override
        public void onDeleted(String videoV) throws RemoteException {
            VideoCacheServiceConnection thiz = mRef.get();
            if (thiz == null) {
                return;
            }
            Iterator<WeakReference<Callback>> it = thiz.mCallbacks.iterator();
            while (it.hasNext()) {
                Callback callback = it.next().get();
                if (callback == null) {
                    it.remove();
                } else {
                    callback.onDeleted(videoV);
                }
            }
        }

        @Override
        public void onAllDeleted() throws RemoteException {
            VideoCacheServiceConnection thiz = mRef.get();
            if (thiz == null) {
                return;
            }
            Iterator<WeakReference<Callback>> it = thiz.mCallbacks.iterator();
            while (it.hasNext()) {
                Callback callback = it.next().get();
                if (callback == null) {
                    it.remove();
                } else {
                    callback.onAllDeleted();
                }
            }
        }
    }
    private final VideoCacheServiceCallbackStub mVideoCacheServiceCallback = new VideoCacheServiceCallbackStub(this);

    public interface Callback {
        void onNotifyProgress(String videoV, int ecoType, int seekOffsetWrite,
                int contentLength) throws RemoteException;

        void onStart(String videoV, int ecoType) throws RemoteException;

        void onFinished(String videoV, int ecoType) throws RemoteException;

        void onAllFinished() throws RemoteException;

        void onDeleted(String videoV) throws RemoteException;

        void onAllDeleted() throws RemoteException;
    }

    ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<WeakReference<Callback>>();

    public void addCallback(Callback callback) {
        mCallbacks.add(new WeakReference<Callback>(callback));
    }

    public IVideoCacheService getIVideoCacheService() {
        return mVideoCacheService;
    }

    public synchronized void bindVideoCacheService(Intent intentBind,
                Context context, String tag) {
        if (tag == null) {
            throw new IllegalArgumentException();
        }
        if (mBindCount.size() == 0) {
            Intent intent = new Intent(context, VideoCacheService.class);
            ComponentName resultStartService = context.startService(intent);
            if (DEBUG_LOGD) {
                if (resultStartService != null) {
                    Log.d(LOG_TAG, Log.buf().append("IVideoCacheService starts: ")
                            .append(resultStartService.toString()).toString());
                }
            }

            intentBind.setComponent(intent.getComponent());
            boolean bindResult = context.bindService(intentBind,
                    mVideoCacheServiceConnection, Context.BIND_AUTO_CREATE);
//            if (DEBUG_LOGD) {
//                Log.d(LOG_TAG, "bindResult=" + bindResult);
//            }
            if (bindResult) {
                mBindCount.add(tag);
            } else {
                Util.showErrorToast(context, R.string.toast_cache_fail_bind);
            }
        } else {
            mBindCount.add(tag);
        }
    }

    public synchronized void unbindVideoCacheService(Context context, String tag) {
        if (tag == null) {
            throw new IllegalArgumentException();
        }

        mBindCount.remove(tag);

        if (mBindCount.size() == 0) {
            if (mVideoCacheService != null) {
                try {
                    context.unbindService(mVideoCacheServiceConnection);
                } catch (IllegalArgumentException e) {
                    // XXX 非bind状態で呼ばれるケースがあるようなのでcatch
                    Log.e(LOG_TAG, e.toString(), e);
                }
            }
        }
    }

    /**
     * {@link VideoCacheService} が起動済みなら再bindする
     * @param context
     */
    public synchronized void rebindVideoCacheServiceIsRunning(Context context,
            String tag) {
        if (mVideoCacheService == null && Util.isServiceRunning(context,
                VideoCacheService.class.getName())) {
            bindVideoCacheService(new Intent(), context, tag);
        }
    }
}
