/**************************************************************************************************
 * Copyright 2018 Qualcomm Technologies International, Ltd.                                       *
 **************************************************************************************************/

package com.qualcomm.qti.iotcontrollersdk.controller;

import android.util.Log;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;


import com.qualcomm.qti.iotcontrollersdk.controller.interfaces.IoTCompletionCallback;
import com.qualcomm.qti.iotcontrollersdk.controller.interfaces.DiscoveryInterface;
import com.qualcomm.qti.iotcontrollersdk.iotsys.resource.attributes.*;
import com.qualcomm.qti.iotcontrollersdk.iotsys.resource.clients.*;
import com.qualcomm.qti.iotcontrollersdk.iotsys.listeners.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.iotivity.base.ObserveType;
import org.iotivity.base.OcConnectivityType;
import org.iotivity.base.OcPlatform;
import org.iotivity.base.OcPlatform.OnResourceFoundListener;
import org.iotivity.base.OcResource;
import org.iotivity.base.OcResource.OnObserveListener;
import org.iotivity.base.OcException;
import org.iotivity.base.OcRepresentation;
import org.iotivity.base.OcResource.OnPostListener;
import org.iotivity.base.OcResource.OnGetListener;
import org.iotivity.base.OcHeaderOption;


public class IoTSysClient extends ControllerInterface {

    private final static String TAG = "IoTSysClient";

    public enum IoTSysObserveOrigin {
{% for swagger_data in swagger_objects %}
{% if "iotsys" in swagger_data.type %}
{% if swagger_data.prop != 0 %}
        IoTSysObserveOrigin{{swagger_data.name|class_name_syntax}},
{% endif %}
{% endif %}
{% endfor %}
    }

    public interface IoTSysClientObserver {
      void onRegistration(IoTSysObserveOrigin type);
      void deRegistration(IoTSysObserveOrigin type);
      void onUpdate(IoTSysObserveOrigin type, OcRepresentation rep);
      void onFailed(IoTSysObserveOrigin type, String exception);
    }

    TaskExecutors mExecutor;
    IoTSysClientObserver mObserver;

    /** The followings are resource clients instance variables */
{% for swagger_data in swagger_objects %}
{% if "iotsys" in swagger_data.type %}
    private {{swagger_data.name|class_name_syntax}}ResourceClient {{swagger_data.name|java_instance_variable_syntax}};
{% endif %}
{% endfor %}

    /*private List<String> mDiscoveryResourceTypes;*/

    /**
     * Class constructor
     */

    public IoTSysClient(final String host, final String id, DiscoveryInterface discovery, final TaskExecutors executor) {

        mHost = host;
        mId = id;
        mDeviceDiscovery = discovery;
        mUriPrefix = "/qti/iotsys";
        mExecutor = executor;
/*        mDiscoveryResourceTypes = Arrays.asList(

{% for swagger_data in swagger_objects %}
{% if "iotsys" in swagger_data.type %}
                  "{{swagger_data.type}}",
{% endif %}
{% endfor %}

                  "qti.iotsys.r.system"
        );
*/
    }

    public void registerObserver(IoTSysClientObserver observer) {
          mObserver = observer;
    }

    public synchronized String getHostName() {
        return mHost;
    }

    public synchronized String getDeviceId() {
        return mId;
    }

    @Override
    public void discoverWithCompletion(final IoTCompletionCallback callback) throws OcException {
        if(mDeviceDiscovery != null) {

            mDeviceDiscovery.findResource(mHost,OcPlatform.WELL_KNOWN_QUERY,EnumSet.of(OcConnectivityType.CT_ADAPTER_IP),
                                 new OnResourceFoundListener() {

                                   private AtomicInteger mCounter = new AtomicInteger(0);

                                   @Override
                                   public void onResourceFound(OcResource resource) {
                                      TaskExecutors.getExecutor().executeOnIoTSysResourceExecutor(() -> {

                                          if (resource.getUri().contains(mUriPrefix)) {

                                              Log.d(TAG,String.format("IoTSys Resource Found URI:%s, %d",
                                                          resource.getUri(),resource.getResourceTypes().size()));

                                              if(mCounter.incrementAndGet()  == IoTConstants.IOTSYS_RESOURCE_NUMBER) {
                                                Log.d(TAG,"All required IoTSys resources found!");
                                                callback.onCompletion(true);
                                              }
                                          }
                                      });
                                   }

                                   @Override
                                   public synchronized void onFindResourceFailed(Throwable ex, String uri) {
                                        Log.e(TAG,String.format("Finding Resource failed:%s",uri));
                                        callback.onCompletion(false);
                                        ex.printStackTrace();
                                   }
                                 }
                             );
            }
    }

    public void createResourceClientsForHost(String host) throws OcException {

{% for swagger_data in swagger_objects %}
{% if "iotsys" in swagger_data.type %}
        {{swagger_data.name|java_instance_variable_syntax}} = new {{swagger_data.name|class_name_syntax}}ResourceClient(host);
{% if swagger_data.prop != 0 %}
        {{swagger_data.name|java_instance_variable_syntax}}.observe(ObserveType.OBSERVE.getValue(),new HashMap<>(),new OnObserveListener() {

            @Override
            public void onObserveCompleted(List<OcHeaderOption> headerOptionList,
                                       OcRepresentation ocRepresentation,
                                       int sequenceNumber) {
                TaskExecutors.getExecutor().executeIoTSysNotification(() -> {

                    if (OcResource.OnObserveListener.REGISTER == sequenceNumber) {
                      mObserver.onRegistration(IoTSysObserveOrigin.IoTSysObserveOrigin{{swagger_data.name|class_name_syntax}});
                    } else if (OcResource.OnObserveListener.MAX_SEQUENCE_NUMBER + 1 == sequenceNumber) {
                      mObserver.deRegistration(IoTSysObserveOrigin.IoTSysObserveOrigin{{swagger_data.name|class_name_syntax}});
                    } else if(mObserver != null) {
                      mObserver.onUpdate(IoTSysObserveOrigin.IoTSysObserveOrigin{{swagger_data.name|class_name_syntax}},ocRepresentation);
                    }
                });

            }

            @Override
            public void onObserveFailed(Throwable ex) {
                TaskExecutors.getExecutor().executeIoTSysNotification(() -> {
                    if(mObserver != null) {
                        mObserver.onFailed(IoTSysObserveOrigin.IoTSysObserveOrigin{{swagger_data.name|class_name_syntax}},ex.toString());
                    }
                });
            }
        });
        mObserverList.add({{swagger_data.name|java_instance_variable_syntax}});
{% endif %}
{% endif %}
{% endfor %}

    }

    @Override
    public void stopObserving() {
       try {
            IoTBaseResourceClient client = mObserverList.pop();
            if(client != null) {
              if(!client.stopObserving())
                cancelNextObserver();
            }
/*{% for swagger_data in swagger_objects %}
{% if "iotsys" in swagger_data.type %}
{% if swagger_data.prop != 0 %}
         {{swagger_data.name|java_instance_variable_syntax}}.stopObserving();
{% endif %}
{% endif %}
{% endfor %}*/
       } catch(OcException e) {
         e.printStackTrace();
       }
    }


{% set isForIoTSys = [True] %}
{% for swagger_data in swagger_objects %}
{% if "iotsys" in swagger_data.type %}
{% include "controller_method.java.jinja2" %}
{% endif %}
{% endfor %}
}
