/*
This file is part of ATM.
ATM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ATM 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with ATM. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jflicks.restlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.jflicks.nms.NMS;
import org.jflicks.nms.NMSUtil;
import org.jflicks.tv.Channel;
import org.jflicks.tv.Listing;
import org.jflicks.tv.LiveTV;
import org.jflicks.tv.ShowAiring;
import org.jflicks.tv.live.Live;
import org.jflicks.tv.programdata.ProgramData;
import org.jflicks.tv.recorder.Recorder;
import org.jflicks.tv.scheduler.Scheduler;
import org.jflicks.util.LogUtil;
import org.jflicks.util.Util;
/**
* This class is a singleton to handle state of LiveTV support
* vis REST.
*
* @author Doug Barnum
* @version 1.0
*/
public final class LiveTVSupport extends BaseSupport {
private static LiveTVSupport instance = new LiveTVSupport();
private HashMap<String, LiveTV> map;
/**
* Default empty constructor.
*/
private LiveTVSupport() {
setMap(new HashMap<String, LiveTV>());
}
/**
* We are a singleton, so users need access to it.
*
* @return A LiveTVSupport instance.
*/
public static LiveTVSupport getInstance() {
return (instance);
}
private HashMap<String, LiveTV> getMap() {
return (map);
}
private void setMap(HashMap<String, LiveTV> m) {
map = m;
}
private LiveTV getLiveTVById(String id) {
LiveTV result = null;
if (id != null) {
result = map.get(id);
}
return (result);
}
private void addLiveTV(String id, LiveTV ltv) {
if ((id != null) && (ltv != null)) {
map.put(id, ltv);
}
}
private LiveTV removeLiveTV(String id) {
LiveTV result = null;
if (id != null) {
result = map.get(id);
map.remove(result);
}
return (result);
}
public LiveTVBean openSession(String channelId) {
LiveTVBean result = null;
LiveTVItem item = getLiveTVItemByChannelId(channelId);
LogUtil.log(LogUtil.DEBUG, "item: <" + item + ">");
if (item != null) {
Channel c = item.getChannel();
LogUtil.log(LogUtil.DEBUG, "channel: <" + c + ">");
LogUtil.log(LogUtil.DEBUG, "hostPort: <" + item.getHostPort() + ">");
NMS n = NMSUtil.select(getNMS(), item.getHostPort());
LogUtil.log(LogUtil.DEBUG, "nms: <" + n + ">");
if ((c != null) && (n != null)) {
LiveTV ltv = n.openSession(c.getNumber());
if (ltv != null) {
addLiveTV(ltv.getId(), ltv);
result = new LiveTVBean(ltv);
} else {
result = new LiveTVBean("Could not open session!");
}
} else {
if (c == null) {
result = new LiveTVBean("Channel not available!");
} else {
result = new LiveTVBean("Server not available!");
}
}
} else {
result = new LiveTVBean("Channel not available!");
}
return (result);
}
public void closeSession(String liveId) {
LogUtil.log(LogUtil.DEBUG, "closeSession liveId: <" + liveId + ">");
LiveTV ltv = getLiveTVById(liveId);
LogUtil.log(LogUtil.DEBUG, "closeSession liveTV: <" + ltv + ">");
if (ltv != null) {
NMS n = NMSUtil.select(getNMS(), ltv.getHostPort());
if (n != null) {
n.closeSession(ltv);
}
removeLiveTV(liveId);
}
}
/**
* In any moment in time get the LiveTVItem instances available.
*
* @return An array of LiveTVItem instances.
*/
public LiveTVItem[] getLiveTVItems() {
LiveTVItem[] result = null;
NMS[] narray = getNMS();
if (narray != null) {
// First get all unique channels.
HashMap<NMS, Channel[]> map = new HashMap<NMS, Channel[]>();
for (int i = 0; i < narray.length; i++) {
//for (int i = 0; i < 1; i++) {
if (supportsLive(narray[i])) {
// Sweet something that can be played live.
Channel[] array = getAvailableChannels(narray[i]);
if ((array != null) && (array.length > 0)) {
map.put(narray[i], array);
}
}
}
if (map.size() > 0) {
// We are going to keep these unique by channel and keep
// only the first one found.
ArrayList<Channel> clist = new ArrayList<Channel>();
ArrayList<LiveTVItem> llist = new ArrayList<LiveTVItem>();
Set<Map.Entry<NMS, Channel[]>> set = map.entrySet();
Iterator<Map.Entry<NMS, Channel[]>> iter = set.iterator();
while (iter.hasNext()) {
Map.Entry<NMS, Channel[]> entry = iter.next();
NMS key = entry.getKey();
Channel[] value = entry.getValue();
for (int i = 0; i < value.length; i++) {
if (!clist.contains(value[i])) {
LiveTVItem item = new LiveTVItem();
item.setHostPort(key.getHost() + ":" + key.getPort());
item.setChannel(value[i]);
ShowAiring[] sas = key.getShowAiringsByChannel(value[i]);
if ((sas != null) && (sas.length > 0)) {
item.setShowAiring(sas[0]);
clist.add(value[i]);
llist.add(item);
}
}
}
}
if (llist.size() > 0) {
result = llist.toArray(new LiveTVItem[llist.size()]);
// We want to set the directUrl property if it exists.
for (int i = 0; i < result.length; i++) {
applyDirectUrl(result[i]);
}
}
}
}
if (result == null) {
// We will return an empty array.
result = new LiveTVItem[0];
}
return (result);
}
private boolean supportsLive(NMS n) {
boolean result = false;
if (n != null) {
Live l = n.getLive();
if (l != null) {
Recorder[] array = n.getConfiguredRecorders();
if ((array != null) && (array.length > 0)) {
result = true;
}
}
}
return (result);
}
private void applyDirectUrl(LiveTVItem item) {
NMS[] narray = getNMS();
if ((narray != null) && (item != null)) {
Channel c = item.getChannel();
LogUtil.log(LogUtil.DEBUG, "applyDirectUrl: c " + c);
if (c != null) {
// Ok we have at least one NMS and a LiveTVItem.
for (int i = 0; i < narray.length; i++) {
// The Recorders in this moment of time ready to record.
Recorder[] recs = getRecorders(narray[i]);
LogUtil.log(LogUtil.DEBUG, "applyDirectUrl: recs " + recs);
if ((recs != null) && (recs.length > 0)) {
for (int j = 0; j < recs.length; j++) {
LogUtil.log(LogUtil.DEBUG, "applyDirectUrl: before supports ");
if (supportsChannel(narray[i], c, recs[j])) {
LogUtil.log(LogUtil.DEBUG, "applyDirectUrl: after supports ");
// Ok lets get the directUrlPrefix
String directUrlPrefix = recs[j].getDirectUrlPrefix();
LogUtil.log(LogUtil.DEBUG, "applyDirectUrl: directUrlPrefix " + directUrlPrefix);
if (directUrlPrefix != null) {
StringBuilder sb = new StringBuilder(directUrlPrefix);
sb.append(c.getNumber());
String directUrlSuffix = recs[j].getDirectUrlSuffix();
if (directUrlSuffix != null) {
sb.append(directUrlSuffix);
}
item.setDirectUrl(sb.toString());
// We continue on because we want to use the last recorder.
}
}
}
}
}
}
}
}
private Channel[] getAvailableChannels(NMS n) {
Channel[] result = null;
if (n != null) {
Recorder[] recs = n.getRecorders();
Scheduler s = n.getScheduler();
if ((s != null) && (recs != null) && (recs.length > 0)) {
Channel[] all = s.getRecordableChannels();
if ((all != null) && (all.length > 0)) {
ArrayList<Channel> clist = new ArrayList<Channel>();
for (int i = 0; i < all.length; i++) {
String listId = all[i].getListingId();
for (int j = 0; j < recs.length; j++) {
if (!recs[j].isRecording()) {
// A free recorder. Can it do this channel?
if (supportsChannel(n, all[i], recs[j])) {
if (!clist.contains(all[i])) {
clist.add(all[i]);
}
}
}
}
}
if (clist.size() > 0) {
result = clist.toArray(new Channel[clist.size()]);
Arrays.sort(result);
}
}
}
}
return (result);
}
private boolean supportsChannel(NMS n, Channel c, Recorder r) {
boolean result = false;
if ((n != null) && (c != null) && (r != null)) {
Scheduler s = n.getScheduler();
if (s != null) {
String lname = s.getListingNameByRecorder(r);
String lid = c.getListingId();
if ((lname != null) && (lid != null)) {
ProgramData[] array = n.getProgramData();
if ((array != null) && (array.length > 0)) {
// Should have just one....
for (int i = 0; i < array.length; i++) {
ProgramData pd = array[i];
Listing listing = pd.getListingByName(lname);
if (listing != null) {
if (lid.equals(listing.getId())) {
result = true;
break;
}
}
}
}
}
}
}
return (result);
}
private LiveTVItem getLiveTVItemByChannelId(String channelId) {
LiveTVItem result = null;
if (channelId != null) {
int cid = Util.str2int(channelId, -1);
if (cid != -1) {
LiveTVItem[] all = getLiveTVItems();
if ((all != null) && (all.length > 0)) {
for (int i = 0; i < all.length; i++) {
Channel c = all[i].getChannel();
if (c != null) {
if (cid == c.getId()) {
result = all[i];
break;
}
}
}
} else {
LogUtil.log(LogUtil.DEBUG, "No channels available for recording now!");
}
} else {
LogUtil.log(LogUtil.DEBUG, "bad channel id: <" + channelId + ">");
}
}
LogUtil.log(LogUtil.DEBUG, "LiveTVItem to use null = " + (result == null));
return (result);
}
private Recorder[] getRecorders(NMS n) {
Recorder[] result = null;
if (n != null) {
Scheduler s = n.getScheduler();
if (s != null) {
Recorder[] array = s.getConfiguredRecorders();
if ((array != null) && (array.length > 0)) {
ArrayList<Recorder> rlist = new ArrayList<Recorder>();
for (int i = 0; i < array.length; i++) {
if (!array[i].isRecording()) {
rlist.add(array[i]);
}
}
if (rlist.size() > 0) {
result = rlist.toArray(new Recorder[rlist.size()]);
}
}
}
}
return (result);
}
private String[] getListingNames(NMS n) {
String[] result = null;
if (n != null) {
Scheduler s = n.getScheduler();
if (s != null) {
result = s.getConfiguredListingNames();
}
}
return (result);
}
}