package org.iotivity.bridge.homeserver.utils;

import android.content.Context;
import android.util.Log;
import android.util.Pair;
import android.webkit.DownloadListener;
import android.widget.Switch;

import org.hamcrest.core.StringEndsWith;
import org.iotivity.OCEndpoint;
import org.iotivity.OCInterfaceMask;
import org.iotivity.OCResourcePropertiesMask;
import org.iotivity.bridge.homeserver.handler.FactoryPresetsHandler;
import org.iotivity.oc.OcRemoteDevice;
import org.iotivity.oc.OcRemoteResource;
import org.iotivity.oc.OcResource;
import org.iotivity.oc.OcUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

public class Utils {

    private static final String TAG = Utils.class.getSimpleName();

    public static long generateRandomID() {
        return UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE;
    }
    public static boolean containsList(String[] arr, String targetValue) {
        return Arrays.asList(arr).contains(targetValue);
    }

    public static boolean containsSet(String[] arr, String targetValue) {
        Set<String> set = new HashSet<String>(Arrays.asList(arr));
        return set.contains(targetValue);
    }

    public static boolean containsLoop(String[] arr, String targetValue) {
        for(String s: arr){
            if(s.equals(targetValue))
                return true;
        }
        return false;
    }

    public static boolean containsArraysBinarySearch(String[] arr, String targetValue) {
        Arrays.sort(arr);
        int a =  Arrays.binarySearch(arr, targetValue);
        if(a > 0)
            return true;
        else
            return false;
    }

    public static OCEndpoint containsEndPointList(OCEndpoint[] arr, String startValue) {
        for(OCEndpoint ep: arr){
            if(OcUtils.endpointToString(ep).contains(startValue))
                return ep;
        }
        return null;
    }

    public static OCEndpoint getCoapsEndpoint (OCEndpoint[] arr) {
        OCEndpoint endpoint = Utils.containsEndPointList(arr, "coaps");
        if (endpoint != null) {
            return endpoint ;
        } else {
            return arr[0];
        }
    }

    public static OCEndpoint getCoapEndpoint (OCEndpoint[] arr) {
        OCEndpoint endpoint = Utils.containsEndPointList(arr, "coap");
        if (endpoint != null) {
            return endpoint ;
        } else {
            return arr[0];
        }
    }

    public static String interfacesMaskToString(int interfaceMask) {
        StringBuilder interfaces = new StringBuilder();
        if ((interfaceMask & OCInterfaceMask.BASELINE) > 0) {
            interfaces.append("BASELINE");
        }
        if ((interfaceMask & OCInterfaceMask.LL) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("LL");
        }
        if ((interfaceMask & OCInterfaceMask.B) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("B");
        }
        if ((interfaceMask & OCInterfaceMask.R) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("R");
        }
        if ((interfaceMask & OCInterfaceMask.RW) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("RW");
        }
        if ((interfaceMask & OCInterfaceMask.A) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("A");
        }
        if ((interfaceMask & OCInterfaceMask.S) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("S");
        }
        if ((interfaceMask & OCInterfaceMask.CREATE) > 0) {
            if (interfaces.length() > 0) {
                interfaces.append(" | ");
            }
            interfaces.append("CREATE");
        }
        return interfaces.toString();
    }

    public static int interfacesStringToMask(String interfaceMask) {

        switch (interfaceMask) {
            case "BASELINE":
                return OCInterfaceMask.BASELINE ;
            case "LL":
                return OCInterfaceMask.LL ;
            case "B":
                return OCInterfaceMask.B ;
            case "R":
                return OCInterfaceMask.R ;
            case "RW":
                return OCInterfaceMask.RW ;
            case "S":
                return OCInterfaceMask.S ;
            case "A":
                return OCInterfaceMask.A ;
            case "CREATE":
                return OCInterfaceMask.CREATE ;
            default:
                return OCInterfaceMask.BASELINE ;
        }
    }

    public static String propertiesMaskToString(int propertiesMask) {
        StringBuilder resourceProperties = new StringBuilder();
        if ((propertiesMask & OCResourcePropertiesMask.OC_DISCOVERABLE) > 0) {
            resourceProperties.append("DISCOVERABLE");
        }
        if ((propertiesMask & OCResourcePropertiesMask.OC_OBSERVABLE) > 0) {
            if (resourceProperties.length() > 0) {
                resourceProperties.append(" | ");
            }
            resourceProperties.append("OBSERVABLE");
        }
        if ((propertiesMask & OCResourcePropertiesMask.OC_SECURE) > 0) {
            if (resourceProperties.length() > 0) {
                resourceProperties.append(" | ");
            }
            resourceProperties.append("SECURE");
        }
        if ((propertiesMask & OCResourcePropertiesMask.OC_PERIODIC) > 0) {
            if (resourceProperties.length() > 0) {
                resourceProperties.append(" | ");
            }
            resourceProperties.append("PERIODIC");
        }
        return resourceProperties.toString();
    }

    public static boolean isRemoteDCRs(OcRemoteResource resource) {
        return isDCRs(resource.getTypes());
    }

    public static boolean isOwnDCRs(OcResource resource) {
        return isDCRs(resource.getResourceTypes());
    }

    /* This will return true if Device Configuration Resource such as Security Virtual Resource,
     * Core Resources such as /oic/res(oic.wk.res), /oic/d(oic.wk.d), /oic/p(oic.wk.p),
     * and Management Resources, Monitoring Resource, SoftUpdate Resource, Cloud Conf Resources
     */
    public static boolean isDCRs(String[] resource_types) {   // Device Configuration Resource

        // Discovery Core Resources
        if(isCoreResource(resource_types)) {
            return true ;
        }
        // Optional Core Resource
        else if(isOptionalCoreResource(resource_types)) {
            return false ;
        }
        // Security Virtual Resources
        else if (isSecurityResource(resource_types)){
            return true ;
        }
        // Management Resource and so on ...
        else if (isManagementResourceAndEtc(resource_types)) {
            return true ;
        }

        return false ;
    }

    /* This will return true if Non-Configuration Resource with auth-crypt wildcard(*) permission
    * and Core Resource with anon-clear /oic/res(oic.wk.res), /oic/d(oic.wk.d), /oic/p(oic.wk.p) permission
    * in ACL2 (/oic/sec/acl2)
    */
    public static boolean isAccessableByPairwisedClient(String[] resource_types) {
        // Discovery Core Resources
        if(isCoreResource(resource_types)) {
            return true ;
        }
        // Optional Core Resource
        else if(isOptionalCoreResource(resource_types)) {
            return true ;
        }
        // Security Virtual Resources
        else if (isSecurityResource(resource_types)){
            return false ;
        }
        // Management Resource and so on ...
        else if (isManagementResourceAndEtc(resource_types)) {
            return false ;
        }
        // The Other Device specific Resources are accessable
        return true ;
    }

    public static boolean isDeviceSpecificResource(String[] resource_types) {
        // Discovery Core Resources
        if(isCoreResource(resource_types)) {
            return false ;
        }
        // Optional Core Resource
        else if(isOptionalCoreResource(resource_types)) {
            return false ;
        }
        // Security Virtual Resources
        else if (isSecurityResource(resource_types)){
            return false ;
        }
        // Management Resource and so on ...
        else if (isManagementResourceAndEtc(resource_types)) {
            return false ;
        }
        return true ;
    }

    public static boolean isCoreResource(String[] resource_types) {
        // Discovery Core Resources
        if(containsList(resource_types, "oic.wk.res")          // Implicit Invisible Discovery Resource in client discovery
                || containsList(resource_types, "oic.wk.d")    // Device Resource
                || containsList(resource_types, "oic.wk.p")) { // Platform Resource
            return true ;
        }
        return false ;
    }

    public static boolean isOptionalCoreResource(String[] resource_types) {
        // Optional Core Resources
        if(containsList(resource_types, "oic.wk.con")     // Configuration Resource
                || containsList(resource_types, "oic.wk.introspection") // Device Introspection
                || containsList(resource_types, "x.org.openconnectivity.oic.introspection.data")) {   // Device Introspection Data
            return true ;
        }
        return false ;
    }

    public static boolean isSecurityResource(String[] resource_types) {
        // Security Virtual Resources
        if (containsList(resource_types, "oic.r.doxm")            // Device Ownership Transfer Method Resource
                || containsList(resource_types, "oic.r.pstat")         // Provision State Resource
                || containsList(resource_types, "oic.r.acl2")          // Access Control List Resource
                || containsList(resource_types, "oic.r.cred")          // Credentials Resource
                || containsList(resource_types, "oic.r.sp")            // Security Profile Resource
                || containsList(resource_types, "oic.r.csr")           // Certificate Signing Request Resource
                || containsList(resource_types, "oic.r.roles")         // Role Maintain Resource with Role certificate
                || containsList(resource_types, "oic.r.ael")           // Auditable Events List Resource that have been logged on the device.
                || containsList(resource_types, "oic.r.sdi")           // Security Domain Information Resource that idendifies OCF Security Domain to which the device belongs.
        ) {
            return true ;
        }
        return false ;
    }

    public static boolean isManagementResourceAndEtc(String[] resource_types) {
        if (containsList(resource_types, "oic.wk.mnt")                // Management Resource
                || containsList(resource_types, "oic.wk.nmon")             // Network Monitoring Resource
                || containsList(resource_types, "oic.r.softwareupdate")    // Software Update Resource
                || containsList(resource_types, "oic.r.coapcloudconf")) {  // Cloud Resource for CoapCloudConf
            return true ;
        }
        return false ;
    }

    public static boolean isObt(OcRemoteDevice device) {

        return containsDeviceType(device, "oic.d.dots") ;
    }
    public static boolean isBridge(OcRemoteDevice device) {

        return containsDeviceType(device, "oic.d.bridge") ;
    }

    public static boolean isVod(OcRemoteDevice device) {

        return containsDeviceType(device, "oic.d.virtual") ;
    }

    public static boolean containsDeviceType(OcRemoteDevice device, String device_type) {

        for(OcRemoteResource resource : device.getResources()) {
            if(containsList(resource.getTypes(), device_type)) {
                return true ;
            }
        }
        return false ;
    }

    public static String propertiesToString(ArrayList<Pair<String, Object>> props) {

        StringBuffer sb = new StringBuffer();
        for(Pair prop : props) {
            sb.append(prop.first + " = " + prop.second.toString() + "\n");
        }
        return sb.toString();
    }

    public static String toString(String[] arr) {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        for(int index = 0; index < arr.length; index++) {
            String obj = arr[index];
            sb.append(obj + ( index < arr.length -1 ? ", " : ""));
        }
        sb.append("]");
        return sb.toString();
    }

    public static String toString(long[] arr) {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        for(int index = 0; index < arr.length; index++) {
            Long obj = arr[index];
            sb.append(obj + ( index < arr.length -1 ? ", " : ""));
        }
        sb.append("]");
        return sb.toString();
    }

    public static String toString(double[] arr) {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        for(int index = 0; index < arr.length; index++) {
            Double obj = arr[index];
            sb.append(obj + ( index < arr.length -1 ? ", " : ""));
        }
        sb.append("]");
        return sb.toString();
    }

    public static String toString(boolean[] arr) {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        for(int index = 0; index < arr.length; index++) {
            boolean obj = arr[index];
            sb.append(obj + ( index < arr.length -1 ? ", " : ""));
        }
        sb.append("]");
        return sb.toString();
    }

    public static String getDIFromAnchor(String anchor){

        if(anchor.contains("ocf:")) {
            return anchor.substring(anchor.lastIndexOf("/")+1);
        }
        return null;
    }
}
