package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;
import static jp.sourceforge.nicoro.NicoroAPIManager.ECO_TYPE_LOW;
import static jp.sourceforge.nicoro.NicoroAPIManager.ECO_TYPE_MID;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.widget.RemoteViews;

import java.lang.ref.WeakReference;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedList;

import jp.sourceforge.nicoro.VideoCacheService.CacheRequest;
import jp.sourceforge.nicoro.VideoCacheService.NotifyProgressData;

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

    /** プレイヤー実行中のNotification ID */
    public static final int NOTIFICATION_ID_RUNNING_PLAYER = 1;
    /** キャッシュ開始／実行中のNotification ID */
    public static final int NOTIFICATION_ID_RUNNING_CACHE = 2;
    /** 完了のNotification ID */
    public static final int NOTIFICATION_ID_FINISHED_CACHE = 3;
    /** エラー通知のNotification ID */
    public static final int NOTIFICATION_ID_ERROR = 4;

    /**
     * 実行中／完了してNotificationに表示しているキャッシュの情報
     */
    private static class NotificationCacheInfo {
        public String videoV;
        public int forceEco;
        public NotificationCacheInfo(String v, int e) {
            videoV = v;
            forceEco = e;
        }
    }

    private static final NotificationController sIntance = new NotificationController();

    private Context mContext;
    private WeakReference<NotificationManager> mNotificationManager = new WeakReference<NotificationManager>(null);

    private DecimalFormat mDecimalFormatMB = Util.createRunCachingProgressMessageFormat();

    private StringBuffer mStringBufferProgress = new StringBuffer(64);

    /**
     * 実行中でNotificationに表示しているキャッシュの情報
     */
    private LinkedList<NotificationCacheInfo> mNotificationRunningCacheInfo =
        new LinkedList<NotificationController.NotificationCacheInfo>();

    /**
     * キャッシュ実行中のNotification
     */
    private Notification mNotificationRunningCache;

    /**
     * 完了してNotificationに表示しているキャッシュの情報
     */
    private LinkedList<NotificationCacheInfo> mNotificationFinishedCacheInfo =
        new LinkedList<NotificationController.NotificationCacheInfo>();

    Notification mNotificationRunningPlayer;
    NotificationCompat.Builder mNotificationRunningPlayerBuilder;

    private NotificationController() {
    }

    public static NotificationController getInstance(Context context) {
        sIntance.setContext(context);
        return sIntance;
    }

    private void setContext(Context context) {
        mContext = context.getApplicationContext();
    }

    private void setRunningCacheProgressSize(RemoteViews contentView,
            int seekOffsetWrite, int contentLength) {
        mStringBufferProgress.setLength(0);
        Util.getRunCachingProgressMessage(mDecimalFormatMB,
                mStringBufferProgress,
                seekOffsetWrite, contentLength);
        contentView.setTextViewText(R.id.size, mStringBufferProgress);

        if (seekOffsetWrite >= 0) {
            assert contentLength >= 0;
            contentView.setProgressBar(R.id.progress,
                    contentLength, seekOffsetWrite, false);
        } else {
            assert contentLength < 0;
            contentView.setProgressBar(R.id.progress, 100, 0, true);
        }
    }

    NotificationManager getNotificationManager() {
        NotificationManager nm = mNotificationManager.get();
        if (nm == null) {
            nm = (NotificationManager) mContext.getSystemService(
                    Context.NOTIFICATION_SERVICE);
            mNotificationManager = new WeakReference<NotificationManager>(nm);
        }
        return nm;
    }

    /**
     * キャッシュ開始／実行中のNotificationをすべてキャンセル
     */
    public void cancelAllRunningCache() {
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            notificationManager.cancel(NOTIFICATION_ID_RUNNING_CACHE);
        }
        mNotificationRunningCache = null;
        synchronized (mNotificationRunningCacheInfo) {
            mNotificationRunningCacheInfo.clear();
        }
    }

    public CacheRequest createCacheRequest(String v, String cus,
            int fe) {
        if (v == null || cus == null) {
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, "createCacheRequest: invalid VideoV or Cookie, ignore");
            }
            return null;
        }
        CacheRequest cacheRequest = new CacheRequest();
        cacheRequest.videoV = v;
        cacheRequest.cookieUserSession = cus;
        cacheRequest.forceEco = fe;
        return cacheRequest;
    }

    /**
     * 指定したキャッシュ開始／実行中のNotificationをキャンセル
     * @param cacheRequest
     */
    public void cancelRunningCache(CacheRequest cacheRequest) {
        int infoSize;
        synchronized (mNotificationRunningCacheInfo) {
            Iterator<NotificationCacheInfo> it = mNotificationRunningCacheInfo.iterator();
            while (it.hasNext()) {
                NotificationCacheInfo info = it.next();
                if (info.videoV.equals(cacheRequest.videoV)
                        && info.forceEco == cacheRequest.forceEco) {
                    it.remove();
                    break;
                }
            }
            infoSize = mNotificationRunningCacheInfo.size();
        }

        if (infoSize == 0) {
            NotificationManager notificationManager = getNotificationManager();
            if (notificationManager != null) {
                notificationManager.cancel(NOTIFICATION_ID_RUNNING_CACHE);
            }
            mNotificationRunningCache = null;
        }
    }

    /**
     * 指定したキャッシュ完了のNotificationをキャンセル
     * @param videoV
     */
    public void cancelFinishedCache(String videoV) {
        NotificationManager notificationManager = getNotificationManager();

        int infoSize;
        synchronized (mNotificationFinishedCacheInfo) {
            Iterator<NotificationCacheInfo> it =
                mNotificationFinishedCacheInfo.iterator();
            while (it.hasNext()) {
                NotificationCacheInfo info = it.next();
                if (videoV.equals(info.videoV)) {
                    it.remove();
                }
            }
            infoSize = mNotificationFinishedCacheInfo.size();
        }
        if (infoSize == 0) {
            if (notificationManager != null) {
                notificationManager.cancel(NOTIFICATION_ID_FINISHED_CACHE);
            }
        }
    }

    /**
     * キャッシュ完了のNotificationをすべてキャンセル
     */
    public void cancelAllFinishedCache() {
        synchronized (mNotificationFinishedCacheInfo) {
            mNotificationFinishedCacheInfo.clear();
        }
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            notificationManager.cancel(NOTIFICATION_ID_FINISHED_CACHE);
        }
    }

    /**
     * キャッシュ完了のNotificationを追加
     * @param cacheRequest
     * @param notification
     */
    public void addFinishedCache(CacheRequest cacheRequest,
            Notification notification) {
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            notificationManager.notify(
                    NOTIFICATION_ID_FINISHED_CACHE,
                    notification);
        }

        synchronized (mNotificationFinishedCacheInfo) {
            mNotificationFinishedCacheInfo.add(new NotificationCacheInfo(
                    cacheRequest.videoV, cacheRequest.forceEco));
        }
    }

    /**
     * キャッシュ開始／実行中のNotificationを追加
     * @param cacheRequest
     * @param notification
     */
    public void addRunningCache(CacheRequest cacheRequest,
            Notification notification) {
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            notificationManager.notify(
                    NOTIFICATION_ID_RUNNING_CACHE,
                    notification);
        }
        mNotificationRunningCache = notification;

        synchronized (mNotificationRunningCacheInfo) {
            mNotificationRunningCacheInfo.add(
                    new NotificationCacheInfo(
                            cacheRequest.videoV, cacheRequest.forceEco));
        }
    }

    /**
     * キャッシュ開始／実行中のNotificationを更新
     * @param notifyProgressData
     * @param cacheRequestQueueSize
     * @param realert tickerを再表示するか否か
     */
    public void updateRunningCache(NotifyProgressData notifyProgressData,
            int cacheRequestQueueSize, boolean realert) {
        NotificationManager notificationManager = getNotificationManager();
        Notification notification = mNotificationRunningCache;
        if (notificationManager != null &&
                notification != null) {
            RemoteViews contentView = notification.contentView;

            String text = createTextProgressRunningCache(
                    notifyProgressData.ecoType, notifyProgressData.videoV,
                    cacheRequestQueueSize);
            contentView.setTextViewText(R.id.title, text);

            setRunningCacheProgressSize(
                    contentView, notifyProgressData.seekOffsetWrite,
                    notifyProgressData.contentLength);

            if (realert) {
                notification.tickerText = text;
                notification.flags &= ~Notification.FLAG_ONLY_ALERT_ONCE;
            } else {
                // 二回目以降の表示はtickerText削除（Android 3.0でちらつくため）
                notification.tickerText = null;
                notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
            }

            notificationManager.notify(
                    NOTIFICATION_ID_RUNNING_CACHE,
                    notification);
        } else {
            Log.d(LOG_TAG, Log.buf()
                    .append("notifyProgressData.notificationStartVideoCache")
                    .append(notification)
                    .toString());
        }
    }

    /**
     * キャッシュ開始のNotificationを作成
     * @param cacheRequest
     * @param cacheRequestQueueSize 開始待ちのキャッシュリクエストの数
     * @return
     */
    public Notification createNotificationRunningCache(CacheRequest cacheRequest,
            int cacheRequestQueueSize) {
        Context context = mContext;
        // キャッシュ履歴表示
        // android:launchMode="singleTask" で複数起動防止
        Intent intent = new Intent(context, NotificationWrapperActivity.class)
            .setAction(DashBoardActivity.ACTION_SHOW_LIST_MENU)
            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .putExtra(ListMenuActivity.INTENT_EXTRA_DEFAULT_LIST_RADIO,
                    R.id.radio_access_history);

        String title = createTextStartRunningCache(cacheRequest.forceEco,
                cacheRequest.videoV, cacheRequestQueueSize);

        // iconを必ず設定しないとNotification出ない？
//        int icon = android.R.drawable.stat_notify_sync;
//        int icon = R.drawable.icon;
        int icon = R.drawable.status_downloading;

        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                0,
                intent,
//              PendingIntent.FLAG_NO_CREATE);
                PendingIntent.FLAG_UPDATE_CURRENT);

        RemoteViews contentView = new RemoteViews(context.getPackageName(),
                R.layout.notification_cache_progress);
        contentView.setTextViewText(R.id.title, title);
        setRunningCacheProgressSize(contentView,
                -1, -1);

        Notification notification = new NotificationCompat.Builder(context)
            .setSmallIcon(icon)
            .setTicker(title)
            .setWhen(0)
            .setOngoing(true)
            .setOnlyAlertOnce(true)
            .setContent(contentView)
            .setContentIntent(pendingIntent)
            .getNotification();
        // XXX NotificationCompatのバグで2.3以前ではcontentViewを上書きしてしまうので、再設定
        notification.contentView = contentView;
        return notification;
    }

    /**
     * キャッシュ開始の文言作成
     * @param forceEco
     * @param videoV
     * @param cacheRequestQueueSize
     * @return
     */
    private String createTextStartRunningCache(int forceEco, String videoV,
            int cacheRequestQueueSize) {
        Context context = mContext;
        int resTitle;
        if (forceEco == ECO_TYPE_LOW) {
            resTitle = R.string.notification_title_start_cache_force_low;
        } else if (forceEco == ECO_TYPE_MID) {
            resTitle = R.string.notification_title_start_cache_force_mid;
        } else {
            resTitle = R.string.notification_title_start_cache;
        }
        String title = context.getString(resTitle, videoV);
        if (cacheRequestQueueSize > 0) {
            title += " " + context.getString(R.string.notification_title_postfix_cache_queue_size,
                    cacheRequestQueueSize);
        }
        return title;
    }

    /**
     * キャッシュ実行中の文言作成
     * @param ecoType
     * @param videoV
     * @param cacheRequestQueueSize
     * @return
     */
    private String createTextProgressRunningCache(int ecoType, String videoV,
            int cacheRequestQueueSize) {
        Context context = mContext;
        int resTitle;
        if (ecoType == ECO_TYPE_LOW) {
            resTitle = R.string.notification_title_progress_cache_low;
        } else if (ecoType == ECO_TYPE_MID) {
            resTitle = R.string.notification_title_progress_cache_mid;
        } else {
            resTitle = R.string.notification_title_progress_cache;
        }
        String title = context.getString(resTitle, videoV);
        if (cacheRequestQueueSize > 0) {
            title += " " + context.getString(R.string.notification_title_postfix_cache_queue_size,
                    cacheRequestQueueSize);
        }
        return title;
    }

    /**
     * キャッシュ終了のNotificationを作成
     * @param cacheRequest
     * @return
     */
    public Notification createNotificationFinishedCache(CacheRequest cacheRequest) {
        Context context = mContext;

        int infoSize;
        synchronized (mNotificationFinishedCacheInfo) {
            infoSize = mNotificationFinishedCacheInfo.size();
        }

        String title;
        String contentText;
        if (infoSize == 0) {
            // Notification 新規作成
            int resTitle;
            if (cacheRequest.forceEco == ECO_TYPE_LOW) {
                resTitle = R.string.notification_title_finish_cache_force_low;
            } else if (cacheRequest.forceEco == ECO_TYPE_MID) {
                resTitle = R.string.notification_title_finish_cache_force_mid;
            } else {
                resTitle = R.string.notification_title_finish_cache;
            }
            title = context.getString(resTitle, cacheRequest.videoV);

            contentText = context.getString(R.string.notification_text_finish_cache,
                    cacheRequest.videoV);
        } else {
            // 複数Notificationを一つに纏め
            title = context.getString(R.string.notification_title_finish_cache,
                    cacheRequest.videoV);
            String postfix = context.getString(
                    R.string.notification_title_postfix_finish_cache_multi);
            title += " " + postfix;

            String param = cacheRequest.videoV;
            param += " " + postfix;
            contentText = context.getString(R.string.notification_text_finish_cache,
                    param);
        }

//        int icon = R.drawable.icon;
        int icon = R.drawable.status_complete;

        // キャッシュ履歴表示
        // android:launchMode="singleTask" で複数起動防止
        Intent intent = new Intent(context, NotificationWrapperActivity.class)
            .setAction(DashBoardActivity.ACTION_SHOW_LIST_MENU)
            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            .putExtra(ListMenuActivity.INTENT_EXTRA_DEFAULT_LIST_RADIO,
                    R.id.radio_access_history);

        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        Intent deleteIntent = VideoCacheNotifyService.createIntentDeleteNotificationFinishCache(mContext);
        PendingIntent deletePendingIntent = PendingIntent.getService(
                context, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        Notification notification = new NotificationCompat.Builder(context)
            .setSmallIcon(icon)
            .setTicker(title)
            .setAutoCancel(true)
            .setContentTitle(title)
            .setContentText(contentText)
            .setContentIntent(pendingIntent)
            .setDeleteIntent(deletePendingIntent)
            .getNotification();
//        notification.flags |= Notification.FLAG_NO_CLEAR;
        return notification;
    }

    /**
     * プレイヤー再生中のNotificationを表示
     * @param activityClass
     * @param playerFragment
     */
    public void addRunningPlayer(Class<? extends Activity> activityClass,
            AbstractPlayerFragment playerFragment) {
        Context context = mContext;
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            NotificationCompat.Builder builder;
            if (mNotificationRunningPlayerBuilder == null) {
//                int icon = R.drawable.icon;
                int icon = R.drawable.status_playing;
                String title = context.getString(
                        R.string.notification_running_player);
                Intent intent = new Intent(context, activityClass).setFlags(
                        Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                PendingIntent pendingIntent = PendingIntent.getActivity(
                        context, 0, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);

                // TODO setOnlyAlertOnceしないと3.2ではNotificationがちらつくが、2.1ではNotificationの表示が更新されない
                builder = new NotificationCompat.Builder(context)
                    .setSmallIcon(icon)
                    .setWhen(0)
                    .setOngoing(true)
                    .setOnlyAlertOnce(true)
                    .setContentTitle(title)
                    .setContentIntent(pendingIntent);

                mNotificationRunningPlayerBuilder = builder;
            } else {
                builder = mNotificationRunningPlayerBuilder;
            }

            StringBuilder contentBuilder = new StringBuilder();
            if (playerFragment != null) {
                contentBuilder.append(playerFragment.getTitle()).append(' ');
                playerFragment.appendCurrentPlayTime(contentBuilder);
            }
            String contentText = contentBuilder.toString();

            builder.setContentText(contentText);

            mNotificationRunningPlayer = builder.getNotification();
            notificationManager.notify(NOTIFICATION_ID_RUNNING_PLAYER,
                    mNotificationRunningPlayer);
        }
    }

    /**
     * プレイヤー再生中のNotificationを解除
     */
    public void cancelRunningPlayer() {
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            notificationManager.cancel(NOTIFICATION_ID_RUNNING_PLAYER);
        }
        mNotificationRunningPlayer = null;
        mNotificationRunningPlayerBuilder = null;
    }

    public void addErrorBeforeCrash(String title, String text) {
        Context context = mContext;
        NotificationManager notificationManager = getNotificationManager();
        if (notificationManager != null) {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                .setContentTitle(title)
                .setContentText(text)
                .setTicker(title)
                .setAutoCancel(true)
                .setSmallIcon(android.R.drawable.stat_notify_error);

            notificationManager.notify(NOTIFICATION_ID_ERROR,
                    builder.getNotification());
        }
    }
}
