/*## (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 VodECMReader extends ECMProvider {
 private boolean enabled;

 private static LinkedList queue;
 private static Hashtable ecms;
 private static Hashtable ecm_lookup;

 static {
  ecms=new Hashtable();
  queue=new LinkedList();
 }

 public VodECMReader() {
  super(-1);
 }

 public void enable() {
  enabled=true;
 }

 public void disable() {
  enabled=false;
 }

 public boolean enabled() {
  return enabled;
 }

 public static void set_ecms(Hashtable tab) {
  synchronized(ecms) {
   ecms=tab;
  }
 }

 public static boolean known_ecm(int stamp) {
  VOD.ECMData ecmdata=(VOD.ECMData)ecms.get(new Integer(stamp)); 

  if (ecmdata!=null) return true;
   else return false;
 }

 public static void notify_ecm(int stamp,int cw) {
  try {
   ApiMonitor.log("notify_ecm_data");

   synchronized(queue) {
    queue.addLast(new Integer(stamp));
    queue.addLast(new Integer(cw));
    queue.notify();
   }
  } catch(Throwable t) {}
 }

 public VOD.ECMData get_ecm_data() throws Throwable {
  ApiMonitor.log("get_ecm_data "+queue);

  VOD.ECMData ecmdata=null;

  synchronized(queue) {
   while(queue.isEmpty()) queue.wait();
       
   Integer stamp=(Integer)queue.removeFirst();
   Integer cw=(Integer)queue.removeFirst();

   ecmdata=(VOD.ECMData)ecms.get(stamp);
   ecmdata.stamp(stamp.intValue());
   ecmdata.handler_cw(cw.intValue());
  }

  return ecmdata;
 }

 public void start() {
  super.start();
 }

 public void stop() {
  super.stop();

  synchronized(queue) {
   queue.notify();
  }
 }

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

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

   ApiMonitor.log("VodECMReader start");

   while(running) {
    VOD.ECMData ecmdata=get_ecm_data();

    if (ecmdata==null) continue;

    int cw[]=ecmdata.cw();

    cur_cw[0]=cw[0];
    cur_cw[1]=cw[1];

    next_cw[0]=cw[2];
    next_cw[1]=cw[3];

    int stamp=ecmdata.stamp();

    ApiMonitor.log("VodECMReader stamp "+Utils.hex_value(stamp,8));

    CW.wait_for_chunk(stamp,2000);

    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) {
     ApiMonitor.log("VodECMReader: cw "+Utils.hex_value(cur_cw[0],8)+" "+Utils.hex_value(cur_cw[1],8));
     ApiMonitor.log("VodECMReader: cur_idx "+Utils.hex_value(cur_idx,8)+" nxt_idx "+Utils.hex_value(nxt_idx,8));
     process_handlers(cur_idx,cur_cw,nxt_idx,next_cw);
    }
   }   

   ApiMonitor.log("VodECMReader stopped");
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }
 }

}
