/*
 * This file is part of libbluray
 * Copyright (C) 2013  VideoLAN
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.videolan;

import java.util.EventObject;
import java.util.Iterator;
import java.util.LinkedList;

import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.GainChangeEvent;
import javax.media.GainChangeListener;
import javax.tv.media.MediaSelectEvent;
import javax.tv.media.MediaSelectListener;
import javax.tv.service.selection.ServiceContextEvent;
import javax.tv.service.selection.ServiceContextListener;

import org.bluray.bdplus.StatusListener;

import org.bluray.media.AngleChangeEvent;
import org.bluray.media.AngleChangeListener;
import org.bluray.media.PanningChangeListener;
import org.bluray.media.PanningChangeEvent;
import org.bluray.media.PiPStatusEvent;
import org.bluray.media.PiPStatusListener;
import org.bluray.media.PlaybackListener;
import org.bluray.media.PlaybackMarkEvent;
import org.bluray.media.PlaybackPlayItemEvent;
import org.bluray.media.UOMaskTableListener;
import org.bluray.media.UOMaskTableChangedEvent;
import org.bluray.media.UOMaskedEvent;

import org.davic.resources.ResourceStatusEvent;
import org.davic.resources.ResourceStatusListener;

import org.dvb.application.AppsDatabaseEvent;
import org.dvb.application.AppsDatabaseEventListener;
import org.dvb.media.SubtitleAvailableEvent;
import org.dvb.media.SubtitleListener;
import org.dvb.media.SubtitleNotAvailableEvent;
import org.dvb.media.SubtitleNotSelectedEvent;
import org.dvb.media.SubtitleSelectedEvent;
import org.dvb.media.VideoFormatListener;
import org.dvb.media.VideoFormatEvent;

public class BDJListeners {
    private LinkedList listeners = new LinkedList();

    public void add(Object listener) {
        if (listener != null) {
            BDJXletContext ctx = BDJXletContext.getCurrentContext();
            if (ctx == null) {
                logger.error("Listener added from wrong thread: " + Logger.dumpStack());
                return;
            }
            synchronized (listeners) {
                remove(listener);
                listeners.add(new BDJListener(ctx, listener));
            }
        }
    }

    public void remove(Object listener) {
        synchronized (listeners) {
            for (Iterator it = listeners.iterator(); it.hasNext(); ) {
                BDJListener item = (BDJListener)it.next();
                if (item.listener == listener)
                    it.remove();
            }
        }
    }

    public void clear() {
        if (null != BDJXletContext.getCurrentContext()) {
            logger.error("clear() from wrong thread: " + Logger.dumpStack());
            return;
        }
        synchronized (listeners) {
            listeners.clear();
        }
    }

    public void putCallback(Object event) {
        boolean mediaQueue = true;
        /*
        if (event instanceof PlaybackMarkEvent) {
        } else if (event instanceof PlaybackPlayItemEvent) {
        } else if (event instanceof UOMaskTableChangedEvent) {
        } else if (event instanceof UOMaskedEvent) {
        } else if (event instanceof PiPStatusEvent) {
        } else if (event instanceof PanningChangeEvent) {
        } else if (event instanceof AngleChangeEvent) {
        } else if (event instanceof MediaSelectEvent) {
        } else if (event instanceof GainChangeEvent) {
        } else if (event instanceof ControllerEvent) {
        }
        */
        if (event instanceof ServiceContextEvent) {
            mediaQueue = false;
        } else if (event instanceof ResourceStatusEvent) {
            mediaQueue = false;
        } else if (event instanceof AppsDatabaseEvent) {
            mediaQueue = false;
        } else if (event instanceof PSR102Status) {
            mediaQueue = false;
        }
        putCallback(event, mediaQueue);
    }

    public void putCallback(Object event, boolean mediaQueue) {
        synchronized (listeners) {
            for (Iterator it = listeners.iterator(); it.hasNext(); ) {
                BDJListener item = (BDJListener)it.next();
                if (item.ctx == null || item.ctx.isReleased()) {
                    logger.info("Listener terminated: " + item.ctx);
                    it.remove();
                } else {
                    if (mediaQueue) {
                        item.ctx.putMediaCallback(new Callback(event, item.listener));
                    } else {
                        item.ctx.putCallback(new Callback(event, item.listener));
                    }
                }
            }
        }
    }

    private static class PSR102Status {
        public int value;

        private PSR102Status(int value) {
            this.value = value;
        }
    }

    public void putPSR102Callback(int value) {
        putCallback(new PSR102Status(value));
    }

    private static class BDJListener {
        public BDJXletContext ctx;
        public Object listener;

        BDJListener(BDJXletContext ctx, Object listener) {
            this.ctx = ctx;
            this.listener = listener;
        }
    }

    private static class Callback extends BDJAction {
        private Callback(Object event, Object listener) {
            this.event = event;
            this.listener = listener;
        }

        public String toString() {
            return this.getClass().getName() + "[event=" + event + ", listener=" + listener;
        }

        protected void doAction() {
            if (event instanceof PlaybackMarkEvent) {
                ((PlaybackListener)listener).markReached((PlaybackMarkEvent)event);
            } else if (event instanceof PlaybackPlayItemEvent) {
                ((PlaybackListener)listener).playItemReached((PlaybackPlayItemEvent)event);
            } else if (event instanceof ServiceContextEvent) {
                ((ServiceContextListener)listener).receiveServiceContextEvent((ServiceContextEvent)event);
            } else if (event instanceof UOMaskTableChangedEvent) {
                ((UOMaskTableListener)listener).receiveUOMaskTableChangedEvent((UOMaskTableChangedEvent)event);
            } else if (event instanceof UOMaskedEvent) {
                ((UOMaskTableListener)listener).receiveUOMaskedEvent((UOMaskedEvent)event);
            } else if (event instanceof PiPStatusEvent) {
                ((PiPStatusListener)listener).piPStatusChange((PiPStatusEvent)event);
            } else if (event instanceof PanningChangeEvent) {
                ((PanningChangeListener)listener).panningChange((PanningChangeEvent)event);
            } else if (event instanceof AngleChangeEvent) {
                ((AngleChangeListener)listener).angleChange((AngleChangeEvent)event);
            } else if (event instanceof MediaSelectEvent) {
                ((MediaSelectListener)listener).selectionComplete((MediaSelectEvent)event);

            } else if (event instanceof GainChangeEvent) {
                ((GainChangeListener)listener).gainChange((GainChangeEvent)event);
            } else if (event instanceof ControllerEvent) {
                ((ControllerListener)listener).controllerUpdate((ControllerEvent)event);

            } else if (event instanceof ResourceStatusEvent) {
                ((ResourceStatusListener)listener).statusChanged((ResourceStatusEvent)event);

            } else if (event instanceof AppsDatabaseEvent) {
                AppsDatabaseEvent dbevent = (AppsDatabaseEvent)event;
                AppsDatabaseEventListener dblistener = (AppsDatabaseEventListener)listener;
                switch (dbevent.getEventId()) {
                case AppsDatabaseEvent.APP_ADDED:
                    dblistener.entryAdded(dbevent);
                    break;
                case AppsDatabaseEvent.APP_CHANGED:
                    dblistener.entryChanged(dbevent);
                    break;
                case AppsDatabaseEvent.APP_DELETED:
                    dblistener.entryRemoved(dbevent);
                    break;
                case AppsDatabaseEvent.NEW_DATABASE:
                    dblistener.newDatabase(dbevent);
                    break;
                }

            } else if (event instanceof SubtitleAvailableEvent || event instanceof SubtitleNotAvailableEvent ||
                       event instanceof SubtitleNotSelectedEvent || event instanceof SubtitleSelectedEvent) {
                ((SubtitleListener)listener).subtitleStatusChanged((EventObject)event);

            } else if (event instanceof VideoFormatEvent) {
                ((VideoFormatListener)listener).receiveVideoFormatEvent((VideoFormatEvent)event);

            } else if (event instanceof PSR102Status) {
                ((StatusListener)listener).receive(((PSR102Status)event).value);

            } else {
                System.err.println("Unknown event type: " + event.getClass().getName());
            }
        }

        private Object listener;
        private Object event;
    }

    private static final Logger logger = Logger.getLogger(BDJListeners.class.getName());
}
