/*## (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.tv.service.*;

public class RemoteECMReader extends ECMProvider {
 public static final int TIME_DIFF = 2*1000;
 
 private StreamInput in;
 StreamOutput out;
 String srvname;
 String srvlocator;
 private boolean enabled;

 private static int nxt_cw = -1;

 public static void update_nxt_cw(int cw) {
  nxt_cw=cw;
 }

 public RemoteECMReader() {
  super(-1);

  try {
   SrvConnection conn=SrvConnection.get(Proto.CLT_BOX);
   in=conn.input();
   in.set_data_type(Proto.CMD_ANNOUNCEMENT);  
   out=conn.output();
   out.set_data_type(Proto.CMD_ANNOUNCEMENT);
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

 public void enable(int channel) {
  try {
   this.channel=channel;

   Service srv=SrvUtils.srv_by_channel(channel);
   srvname=srv.getName();
   srvlocator=srv.getLocator().toString();

   out.write_byte(Proto.AN_ECM_RECEIVER_STARTED);
   out.write_string(srvname);
   out.write_string(srvlocator);
   out.flush();
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  enabled=true;
 }

 public void disable() {
  enabled=false;

  try {
   out.write_byte(Proto.AN_ECM_RECEIVER_STOPPED);
   out.write_string(srvname);
   out.write_string(srvlocator);
   out.flush();
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

 public boolean enabled() {
  return enabled;
 }

 public static void wait_for_handler() {
  ApiMonitor.log("wait_for_handler()");

  while(true) {
   long time=System.currentTimeMillis();
   long diff=time-CW.decrypt_time();

   if (diff<TIME_DIFF) {
    break;
   }

   Utils.sleep(100);
  }

  ApiMonitor.log("wait end");
 }

 public void run(Object args[]) {
  try {
   running=true;

   int cur_cw[]=new int[2];
   int next_cw[]=new int[2];

   while(running) {
    int cmd=in.get_byte();

    if (cmd!=Proto.AN_ECM_DATA) continue;

    int stamp=in.read_int();

    cur_cw[0]=in.read_int();
    cur_cw[1]=in.read_int();

    next_cw[0]=in.read_int();
    next_cw[1]=in.read_int();

    CW.wait_for_chunk(stamp,2000);

    ApiMonitor.log("RemoteECMReader: cw "+Utils.hex_value(cur_cw[0],8)+" "+Utils.hex_value(cur_cw[1],8)+" stamp "+Utils.hex_value(stamp,8));

    CW.Chunk chunk1=CW.get_cw_data(stamp);
    CW.Chunk chunk2=CW.get_next_cw_data(chunk1);

    ApiMonitor.log("chunk1 "+chunk1);
    ApiMonitor.log("chunk2 "+chunk2);

    int cur_idx=-1;
    int nxt_idx=-1;

    if (chunk1!=null) {
     if (chunk1.current()) {
      cur_idx=chunk1.key_num();
      ApiMonitor.log("chunk1 cur_idx "+cur_idx);
      CW.del_chunk(chunk1);
     } else {
      nxt_idx=chunk1.key_num();
      ApiMonitor.log("chunk1 nxt_idx "+nxt_idx);
     }
    }

    if (chunk2!=null) {
     if (chunk2.current()) {
      cur_idx=chunk2.key_num();
      ApiMonitor.log("chunk2 cur_idx "+cur_idx);
      CW.del_chunk(chunk2);
     } else {
      nxt_idx=chunk2.key_num();
      ApiMonitor.log("chunk2 nxt_idx "+nxt_idx);
     }
    }

    if (enabled) {
     process_handlers(cur_idx,cur_cw,nxt_idx,next_cw);
    }
   }   

  } catch(Throwable t) {}
 }

}
