/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.activemq.artemis.core.protocol.mqtt;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.remoting.FailureListener;
import org.apache.activemq.artemis.spi.core.protocol.AbstractRemotingConnection;
import org.apache.activemq.artemis.spi.core.remoting.Connection;

public class MQTTConnection extends AbstractRemotingConnection {

   private boolean destroyed;

   private boolean connected;

   private int receiveMaximum = -1;

   private String protocolVersion;

   private boolean clientIdAssignedByBroker = false;

   public MQTTConnection(Connection transportConnection) throws Exception {
      super(transportConnection, null);
      this.destroyed = false;
      transportConnection.setProtocolConnection(this);
   }

   @Override
   public void fail(ActiveMQException me) {
      List<FailureListener> copy = new ArrayList<>(failureListeners);
      for (FailureListener listener : copy) {
         listener.connectionFailed(me, false);
      }
      transportConnection.close();
   }

   @Override
   public void fail(ActiveMQException me, String scaleDownTargetNodeID) {
      synchronized (failureListeners) {
         for (FailureListener listener : failureListeners) {
            //FIXME(mtaylor) How do we check if the node has failed over?
            listener.connectionFailed(me, false);
         }
      }
   }

   @Override
   public Future asyncFail(ActiveMQException me) {
      FutureTask<Void> task = new FutureTask(() -> {
         fail(me);
         return null;
      });


      // I don't expect asyncFail happening on MQTT, in case of happens this is semantically correct
      Thread t = new Thread(task);

      t.start();

      return task;
   }

   @Override
   public void destroy() {
      destroyed = true;
      disconnect(false);
   }

   @Override
   public boolean isClient() {
      return false;
   }

   @Override
   public boolean isDestroyed() {
      return destroyed;
   }

   @Override
   public void disconnect(boolean criticalError) {
      transportConnection.forceClose();
   }

   @Override
   public void disconnect(String scaleDownNodeID, boolean criticalError) {
      transportConnection.forceClose();
   }

   protected void dataReceived() {
      dataReceived = true;
   }

   @Override
   public void flush() {
      transportConnection.checkFlushBatchBuffer();
   }

   @Override
   public void bufferReceived(Object connectionID, ActiveMQBuffer buffer) {
   }

   public void setConnected(boolean connected) {
      this.connected = connected;
   }

   public boolean getConnected() {
      return connected;
   }

   @Override
   public void killMessage(SimpleString nodeID) {
      //unsupported
   }

   @Override
   public boolean isSupportsFlowControl() {
      return false;
   }

   /**
    * Returns the name of the protocol for this Remoting Connection
    *
    * @return
    */
   @Override
   public String getProtocolName() {
      return MQTTProtocolManagerFactory.MQTT_PROTOCOL_NAME + (protocolVersion != null ? protocolVersion : "");
   }

   @Override
   public String getTransportLocalAddress() {
      return getTransportConnection().getLocalAddress();
   }

   public int getReceiveMaximum() {
      return receiveMaximum;
   }

   public void setReceiveMaximum(int maxReceive) {
      this.receiveMaximum = maxReceive;
   }

   public void setProtocolVersion(String protocolVersion) {
      this.protocolVersion = protocolVersion;
   }

   public void setClientIdAssignedByBroker(boolean clientIdAssignedByBroker) {
      this.clientIdAssignedByBroker = clientIdAssignedByBroker;
   }

   public boolean isClientIdAssignedByBroker() {
      return clientIdAssignedByBroker;
   }
}
