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

public class Entitlement {

 public static class SubsDiff {
  int value;
  Subscription stab[];

  public SubsDiff(Subscription s,int idx) {
   stab=new Subscription[2];
   stab[idx]=s;
   value=s.value();
  }
 }

 public static final int ADD = 0x80000000;

 String provname;
 Vector subscriptions;

 public Entitlement(String provider,Vector subs) {
  provname=provider;
  subscriptions=subs;
 }

 public String provider() {
  return provname;
 }

 public Vector subscriptions() {
  return subscriptions;
 }

 public void print() throws Throwable {
  String s="- \""+provname+"\"";
  Output.text(s);

  for(int i=0;i<subscriptions.size();i++) {
   Subscription subs=(Subscription)subscriptions.elementAt(i);
   subs.print(provname.equals(SrvUtilsDefs.DEFAULT_PROVIDER));
  }
 }

 public void log() {
  String s="- \""+provname+"\"";
  ApiMonitor.log(s);

  for(int i=0;i<subscriptions.size();i++) {
   Subscription subs=(Subscription)subscriptions.elementAt(i);
   subs.log();
  }
 }

 public static void log(Entitlement enttab[]) {
  for(int i=0;i<enttab.length;i++) {
   enttab[i].log();
  }
 }

 private static Subscription lookup_subscription(Entitlement[] etab,String provider,Subscription s) {
  for(int i=0;i<etab.length;i++) {
   Entitlement e=etab[i];

   if (!e.provider().equals(provider)) continue;

   Vector subs=e.subscriptions();

   for(int j=0;j<subs.size();j++) {
    Subscription s2=(Subscription)subs.elementAt(j);

    if (s2.equals(s)) return s2;      
   }
  }

  return null;
 }

 private static void store_sub(Hashtable diff,Entitlement etab[],int idx) {
  for(int i=0;i<etab.length;i++) {
   Entitlement e=etab[i];

   String provider=e.provider();

   Vector sentries=(Vector)diff.get(provider);

   if (sentries==null) {
    sentries=new Vector();
    diff.put(provider,sentries);
   }

   Vector subs=e.subscriptions();

   for(int j=0;j<subs.size();j++) {
    Subscription s=(Subscription)subs.elementAt(j);

    int value=s.value();

    SubsDiff subdiff=null;

    for(int k=0;k<sentries.size();k++) {
     SubsDiff tmp=(SubsDiff)sentries.elementAt(k);

     if (tmp.value==value) {
      subdiff=tmp;
      break;
     }
    }

    if (subdiff==null) {
     subdiff=new SubsDiff(s,idx);
     sentries.addElement(subdiff);
    } else {
     subdiff.stab[idx]=s;
    }
   }
  }
 }

 public static Entitlement[] diff(Entitlement[] oldtab,Entitlement[] newtab) {
  Hashtable diff=new Hashtable();

  store_sub(diff,oldtab,0);
  store_sub(diff,newtab,1);

  Vector ev=new Vector();

  for(Enumeration e=diff.keys();e.hasMoreElements();) {
   String provider=(String)e.nextElement();
   Vector sentries=(Vector)diff.get(provider);

   Vector subs=new Vector();

   for(int j=0;j<sentries.size();j++) {
    SubsDiff sdiff=(SubsDiff)sentries.elementAt(j);

    Subscription sold=sdiff.stab[0];
    Subscription snew=sdiff.stab[1];

    if ((sold!=null)&&(snew!=null)) {
     int diffmask=sold.bitmask()^snew.bitmask();

     if (diffmask!=0) {    
      if ((snew.bitmask()&diffmask)!=0) {
       diffmask|=ADD;
      }

      Subscription s=new Subscription(snew.subsperiod,diffmask);
      subs.addElement(s);
     }

    } else

    if (sold!=null) {
     int diffmask=sold.bitmask();

     Subscription s=new Subscription(sold.subsperiod,diffmask);
     subs.addElement(s);
    } else

    if (snew!=null) {
     int diffmask=snew.bitmask()|ADD;

     Subscription s=new Subscription(snew.subsperiod,diffmask);
     subs.addElement(s);
    }

   }

   if (subs.size()>0) {
    Entitlement ent=new Entitlement(provider,subs);
    ev.addElement(ent);
   }
  }   

  Entitlement[] etab=new Entitlement[ev.size()];

  for(int i=0;i<ev.size();i++) {
   etab[i]=(Entitlement)ev.elementAt(i);
  }

  return etab;
 }

 public static Entitlement create(String provider,int mask) {
  Entitlement ent=null;

  try {
   Subscription sub=Subscription.create(mask);

   Vector subs=new Vector();
   subs.addElement(sub);

   ent=new Entitlement(provider,subs);
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return ent;  
 }

 private static Entitlement lookup_ent(String provider,Entitlement etab[]) {
  Entitlement ent=null;

  for(int i=0;i<etab.length;i++) {
   Entitlement e=etab[i];

   if (e.provider().equals(provider)) {
    ent=e;
    break;
   }
  }

  return ent;
 }

 private static Subscription lookup_sub(Entitlement ent,Subscription sub) {
  Subscription res=null;

  try {
   Vector subs=ent.subscriptions();

   for(int j=0;j<subs.size();j++) {
    Subscription s2=(Subscription)subs.elementAt(j);

    if (s2.equals(sub)) {
     res=s2;
     break;
    }     
   }
  } catch(Throwable t) {
ApiMonitor.log_stacktrace(t);
  }

  return res;
 }

 public static Entitlement[] merge(Entitlement etab[],Entitlement newent) {
  Entitlement res[]=null;

  try {
   Entitlement ent=lookup_ent(newent.provider(),etab);

   if (ent!=null) {
    Vector newsubs=newent.subscriptions();

    if (newsubs.size()==1) {
     Subscription newsub=(Subscription)newsubs.elementAt(0);
     Subscription matchsub=lookup_sub(ent,newsub);

     if (matchsub!=null) {
      matchsub.bitmask(matchsub.bitmask()|newsub.bitmask());
     } else {
     //add new sub
      Vector subs=ent.subscriptions();
      subs.removeElementAt(subs.size()-1);
      subs.insertElementAt(newsub,0);
     }
    }

    res=etab;
   } else {
    //add new ent
    res=new Entitlement[etab.length+1];

    for(int i=0;i<etab.length;i++) {
     res[i]=etab[i];
    }
   
    res[etab.length]=newent;
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return res;
 }

 public static Entitlement[] remove(Entitlement etab[],Entitlement newent) {
  Entitlement res[]=null;

  try {
   Entitlement ent=lookup_ent(newent.provider(),etab);

   if (ent!=null) {
    Vector newsubs=newent.subscriptions();

    if (newsubs.size()==1) {
     Subscription newsub=(Subscription)newsubs.elementAt(0);
     Subscription matchsub=lookup_sub(ent,newsub);

     if (matchsub!=null) {
      matchsub.bitmask(matchsub.bitmask()&(~newsub.bitmask()));
     }
    }

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

  return res;
 }

}
