/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.util.*;
import javax.media.*;
import javax.tv.service.*;
import javax.tv.service.selection.*;
import org.ocap.storage.*;
import org.ocap.shared.dvr.*;
import org.ocap.shared.dvr.navigation.*;
import tv.osmosys.tci.*;
import org.dvb.pvr.*;
import sun.misc.CVM;

public class RecMan {
 public static final int PAD = 25;

 private static Player player;
 private static RecordingRequest played_recording;
 private static Service last_service;

 private static String pvod_props[]={
  "asset.name",
  "asset.id",
  "asset.ver",
  "is_complete",
  "completion",
  "chunk_cnt",
  "completion.prop_size",
  "is_pvod"
 };

 private static String recording_time(long time) {
  String res="";

  time/=1000;

  int hours=(int)(time/3600);
  int seconds=(int)(time-(hours*3600));
  int minutes=seconds/60;
  seconds=seconds-(minutes*60);

  res+=Utils.padded_int(hours,2)+"h "+Utils.padded_int(minutes,2)+"m "+Utils.padded_int(seconds,2)+"s ";

  return res;
 }

 private static String recording_state(int state) {
  String res="";

  switch(state) {
   case LeafRecordingRequest.PENDING_NO_CONFLICT_STATE:
    res="PENDING_NO_CONFLICT_STATE";
    break;
   case LeafRecordingRequest.PENDING_WITH_CONFLICT_STATE:
    res="PENDING_WITH_CONFLICT_STATE";
    break;
   case LeafRecordingRequest.IN_PROGRESS_STATE:
    res="IN_PROGRESS_STATE";
    break;
   case LeafRecordingRequest.IN_PROGRESS_INSUFFICIENT_SPACE_STATE:
    res="IN_PROGRESS_INSUFFICIENT_SPACE_STATE";
    break;
   case LeafRecordingRequest.INCOMPLETE_STATE:
    res="INCOMPLETE_STATE";
    break;
   case LeafRecordingRequest.COMPLETED_STATE:
    res="COMPLETED_STATE";
    break;
   case LeafRecordingRequest.FAILED_STATE:
    res="FAILED_STATE";
    break;
   case LeafRecordingRequest.DELETED_STATE:
    res="DELETED_STATE";
    break;
   case LeafRecordingRequest.IN_PROGRESS_WITH_ERROR_STATE:
    res="IN_PROGRESS_WITH_ERROR_STATE";
    break;
   case LeafRecordingRequest.IN_PROGRESS_INCOMPLETE_STATE:
    res="IN_PROGRESS_INCOMPLETE_STATE";
    break;
   default:
    res=state+"_STATE";
    break;
  }

  return res;
 }

 private static void print_program_time_locator(RecordingRequest rr) throws Throwable {
  if (rr instanceof LeafRecordingRequest) {
   LeafRecordingRequest lrr=(LeafRecordingRequest)rr;
   try {
    RecordedService recsrv=lrr.getService();
    MediaLocator ml=recsrv.getMediaLocator();

    long length=recsrv.getRecordedDuration();

    String s="- "+Utils.padded_string("program",PAD)+recsrv.getName();
    Output.text(s);

    s="- "+Utils.padded_string("time",PAD)+recording_time(length);
    Output.text(s);

    s="- "+Utils.padded_string("locator",PAD)+ml;
    Output.text(s);
   } catch(Throwable t) {}
  }

  String s="- "+Utils.padded_string("state",PAD)+recording_state(rr.getState());
  Output.text(s);
 }

 public static void print_recinfo(RecordingRequest rr) throws Throwable {
  RecordingSpec rs=rr.getRecordingSpec();

  if (rs instanceof PVODRecordingSpec) {
   PVODRecordingSpec pvodrs=(PVODRecordingSpec)rs;

   int id=rr.getId();
   String s="- "+Utils.padded_string("id",PAD)+"0x"+Utils.hex_value(id,8);
   Output.text(s);

   for(int j=0;j<pvod_props.length;j++) {
    Object val=pvodrs.getProperty(pvod_props[j]);
   
    if (val!=null) {
     s="- "+Utils.padded_string(pvod_props[j],PAD)+val;
     Output.text(s);
    }
   }

   print_program_time_locator(rr);
  } else

  if (rs instanceof ServiceRecordingSpec) {
   int id=rr.getId();
   String s="- "+Utils.padded_string("id",PAD)+"0x"+Utils.hex_value(id,8);
   Output.text(s);

   String name=(String)rr.getAppData("RECORDING_NAME");
   
   if (name==null) {       
    if (rr.getParent()!=null) {
     name=(String)rr.getParent().getAppData("RECORDING_NAME");
    }
     
    if (name==null) {
     name=(String)rr.getAppData("EVENT_NAME"); 
    }
   }
  
   if (name!=null) name="\""+name+"\"";
    else name="(empty)";

   s="- "+Utils.padded_string("name",PAD)+name;
   Output.text(s);

   print_program_time_locator(rr);

   Long t=(Long)rr.getAppData("CREATION_TIME");

   String time="";

   if (t!=null) {
    Date date=new Date((long)t.longValue());
    time=date.toString();
   } else time="(unknown)";

   s="- "+Utils.padded_string("creation time",PAD)+time;
   Output.text(s);

   s="- "+Utils.padded_string("can continue watching",PAD);
   t=(Long)rr.getAppData("VIEWED_BK");

   if ((t!=null)&&(t.longValue()>0)) s+="yes";
    else s+="no";

   Output.text(s);

   s="- "+Utils.padded_string("was movie watched",PAD);
   Integer flags=(Integer)rr.getAppData("FLAGS");

   if ((flags!=null)&&((flags.intValue()&0x08)!=0)) s+="yes";
    else s+="no";

   Output.text(s);
  }
 }

 public static void print_pvod() throws Throwable {
  RecordingManager rm=RecordingManager.getInstance();

  RecordingList rl=rm.getEntries();

  int idx=0;

  Output.text("[PVOD info]");

  for(int i=0;i<rl.size();i++) {
   RecordingRequest rr=rl.getRecordingRequest(i);

   RecordingSpec rs=rr.getRecordingSpec();

   if (!(rs instanceof PVODRecordingSpec)) continue;

   Output.text("RECORDING "+Utils.padded_int(idx,3));

   print_recinfo(rr);

   idx++;
  }
 }

 public static void print_userlist() throws Throwable {
  RecordingManager rm=RecordingManager.getInstance();

  RecordingList rl=rm.getEntries();

  int idx=0;

  Output.text("[recordings info]");

  for(int i=0;i<rl.size();i++) {
   RecordingRequest rr=rl.getRecordingRequest(i);

   RecordingSpec rs=rr.getRecordingSpec();

   if (!(rs instanceof ServiceRecordingSpec)) continue;
    
   Output.text("RECORDING "+Utils.padded_int(idx,3));

   print_recinfo(rr);

   idx++;
  }
 }

 public static RecordingRequest find_recording(int id) {
  RecordingRequest res=null;

  RecordingManager rm=RecordingManager.getInstance();

  RecordingList rl=rm.getEntries();

  for(int i=0;i<rl.size();i++) {
   RecordingRequest rr=rl.getRecordingRequest(i);

   if (rr.getId()==id) {
    res=rr;
    break;
   }
  }

  return res;  
 }

 public static int recording_id2asset_id(int id) {
  int assetid=-1;

  RecordingRequest rr=find_recording(id);

  if (rr!=null) {
   RecordingSpec rs=rr.getRecordingSpec();

   if (rs instanceof PVODRecordingSpec) {
    PVODRecordingSpec pvodrs=(PVODRecordingSpec)rs;
    Object val=pvodrs.getProperty("asset.id");
    assetid=Integer.parseInt(val.toString());
   }
  }

  return assetid;
 }

 public static void print_pvod_props(int id) throws Throwable {
  Output.text("[PVOD properties]");
  VOD.print_props(id);
 }

 public static void print_all_pvod_props() throws Throwable {
  VOD.print_all_props();
 }

 public static void start_playing(int id) {
  RecordingRequest rr=find_recording(id);

  if (rr!=null) {
   if (start_playing(rr)) {
    try {
     Output.text("[playing recording]");
     print_recinfo(rr);
    } catch(Throwable t) {}
   }
  }
 }

 public static boolean start_playing(RecordingRequest rr) {
  boolean res=false;

  if (rr instanceof LeafRecordingRequest) {
   LeafRecordingRequest lrr=(LeafRecordingRequest)rr;

   try {
    RecordedService recsrv=lrr.getService();
    MediaLocator ml=recsrv.getMediaLocator();

    String url=ml.toString();

    Media.JTVplay(url);

    played_recording=rr;

    res=true;
   } catch(Throwable t) {
    ApiMonitor.log_stacktrace(t);
   }
  }

  return res;
 }

 public static void stop_playing() throws Throwable {
  Media.JTVstop();

  Output.text("[stopped playing recording]");
  print_recinfo(played_recording);

  played_recording=null;
 }

 public static int start_recording(int channel,long start_time,long end_time) throws Throwable {
  int res=RecManDefs.RES_OK;

  try {
   Service srv=SrvUtils.srv_by_channel(channel);

   if (srv!=null) {
    long duration=end_time-start_time;

    Date start=new Date(start_time);

    RecordingManager rm=RecordingManager.getInstance();

    DVBRecordingProperties rp=new DVBRecordingProperties(0x7fffffffffffffffL,0x7fffffffffffffffL,true,true,true);
    ServiceRecordingSpec rspec=new ServiceRecordingSpec(srv,start,duration,rp);

    rm.record(rspec);
   } else res=RecManDefs.INVALID_CHANNEL;   

  } catch(Throwable t) {
   ApiMonitor.log("start_recording exc: "+t);
  }

  return res;
 }

 public static int stop_recording(int id) throws Throwable {
  int res=RecManDefs.RES_OK;

  try {
   RecordingRequest rr=find_recording(id);

   if (rr!=null) {
    res=RecManDefs.NOT_IN_RECORD_STATE;

    ((LeafRecordingRequest)rr).stop();

    res=RecManDefs.RES_OK;
   } else res=RecManDefs.INVALID_RECID;   

  } catch(Throwable t) {
   ApiMonitor.log("stop_recording exc: "+t);
  }

  return res;
 }

 public static int delete_recording(int id) throws Throwable {
  int res=RecManDefs.RES_OK;

  try {
   RecordingRequest rr=find_recording(id);

   if (rr!=null) {
    res=RecManDefs.CANNOT_DELETE;

    rr.delete();

    res=RecManDefs.RES_OK;
   } else res=RecManDefs.INVALID_RECID;   

  } catch(Throwable t) {
   ApiMonitor.log("delete_recording exc: "+t);
  }

  return res;
 }

 public static int delete_content(int id) throws Throwable {
  int res=RecManDefs.RES_OK;

  try {
   RecordingRequest rr=find_recording(id);

   if (rr!=null) {
    res=RecManDefs.CANNOT_DELETE;

    SegmentedRecordedService srs=((LeafRecordingRequest)rr).getService();

    if (srs!=null) {
     srs.delete();

     res=RecManDefs.RES_OK;
    }

   } else res=RecManDefs.INVALID_RECID;   

  } catch(Throwable t) {
   ApiMonitor.log("delete_content exc: "+t);
  }

  return res;
 }

}
