/**
 * *****************************************************************************
 * Copyright (c) 2016
 *
 * Licensed 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 com.exalttech.trex.ui;

import com.exalttech.trex.core.ConnectionManager;
import com.exalttech.trex.ui.models.Port;
import com.exalttech.trex.ui.models.PortModel;
import com.exalttech.trex.ui.models.PortStatus;
import com.exalttech.trex.util.Util;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.log4j.Logger;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Port Manager class implementation
 *
 * @author Georgekh
 */
public class PortsManager {

    private static final Logger logger = Logger.getLogger(PortsManager.class);
    
    private static PortsManager instance = null;

    ObjectMapper mapper = new ObjectMapper();
    
    /**
     * Return instance of Port manager
     *
     * @return
     */
    public static PortsManager getInstance() {
        if (instance == null) {
            instance = new PortsManager();
        }

        return instance;
    }
    private List<Port> portList;
    PortManagerEventHandler portManagerHandler;

    private Map<Integer, PortModel> portModels = new HashMap<>();
    
    /**
     * Protected constructor
     */
    protected PortsManager() {
        // constructor
    }

    public PortModel getPortModel(int portIndex) {
        PortModel model = portModels.get(portIndex);
        if (model == null) {
            model = PortModel.createModelFrom(portList.get(portIndex));
            portModels.put(portIndex, model);
        }
        return model;
    }
    
    public void clearPorts() {
        portModels.clear();
        portList.clear();
    }
    
    /**
     * Get port list
     *
     * @return
     */
    public List<Port> getPortList() {
        if (hasPorts()) {
            return portList;
        }
        return new ArrayList<>();
    }

    /**
     * Set port list
     *
     * @param portList
     */
    public void setPortList(List<Port> portList) {
        this.portList = portList;
    }

    /**
     * Set port handler
     *
     * @param portManagerHandler
     */
    public void setPortManagerHandler(PortManagerEventHandler portManagerHandler) {
        this.portManagerHandler = portManagerHandler;
    }

    /**
     * Force request to update port state
     */
    public void updatePortForce() {
        updatedPorts(portList.stream().map(port -> port.getIndex()).collect(Collectors.toList()));
    }
    
    public void updatedPorts(List<Integer> portIndexes) {
        List<Port> list = portList.stream()
                                  .filter(port -> portIndexes.contains(port.getIndex()))
                                  .collect(Collectors.toList());

        try {
            String response = ConnectionManager.getInstance().sendPortStatusRequest(list);
            if (response == null) {
                return;
            }
            List<PortStatus> portStatusList = mapper.readValue(response, mapper.getTypeFactory().constructCollectionType(List.class, PortStatus.class));
            for (Port port : list) {
                PortStatus.PortStatusResult portStatus = portStatusList.get(list.indexOf(port)).getResult();
                port.setOwner(portStatus.getOwner());
                port.setStatus(portStatus.getState());
                port.setAttr(portStatus.getAttr());
                port.setRx_info(portStatus.getRx_info());
                port.setService(portStatus.getService());
                port.linkProperty().set(portStatus.getAttr().getLink().getUp());
            }
            portManagerHandler.onPortListUpdated(true);
        } catch (Exception ex) {
            logger.error("Error reading port status", ex);
        }
    }

    /**
     * Return port size
     *
     * @param isOwnerFiltered
     * @return
     */
    public int getPortCount(boolean isOwnerFiltered) {
        int count = 0;
        if (hasPorts()) {
            if (!isOwnerFiltered) {
                return portList.size();
            }

            for (Port port : portList) {
                if (port.getOwner().equals(ConnectionManager.getInstance().getClientName())) {
                    count++;
                }
            }
        }
        return count;
    }

    /**
     * Return number of active port
     *
     * @return
     */
    public String getActivePort() {
        int count = 0;
        if (hasPorts()) {
            for (Port port : getPortList()) {
                if (PortState.getPortStatus(port.getStatus()) == PortState.TX) {
                    count++;
                }
            }
        }
        return String.valueOf(count);
    }

    /**
     * Return whether the current user is the port owner
     *
     * @param portIndex
     * @return
     */
    public boolean isCurrentUserOwner(int portIndex) {
        return portList.get(portIndex).getOwner().equals(ConnectionManager.getInstance().getClientName());
    }

    /**
     * Return whether the port list is exists or not
     */
    private boolean hasPorts() {
        return portList != null && !portList.isEmpty();
    }

    /**
     * Return whether the port is owned or free
     *
     * @param portIndex
     * @return
     */
    public boolean isPortFree(int portIndex) {
        return Util.isNullOrEmpty(portList.get(portIndex).getOwner());
    }

    /**
     * Return indexes of the ports
     *
     * @return
     */
    public List<Integer> getPortIndexes() {
        List<Integer> portIndexes = new LinkedList<Integer>();
        portList.forEach((Port port) -> {
            portIndexes.add(port.getIndex());
        });
        return portIndexes;
    }

    /**
     * Return indexes of the owned ports
     *
     * @return
     */
    public List<Integer> getOwnedPortIndexes() {
        List<Integer> ownedPortIndexes = new LinkedList<Integer>();
        portList.forEach((Port port) -> {
            int portIndex = port.getIndex();
            if (isCurrentUserOwner(portIndex)) {
                ownedPortIndexes.add(portIndex);
            }
        });
        return ownedPortIndexes;
    }
}

@@ -1231,16 +1231,112 @@
 ager %7B%0A%0A
+    public interface PortServiceModeChangedListener %7B%0A        void serviceModeChanged();%0A    %7D%0A%0A
     priv
@@ -1504,32 +1504,139 @@
 tMapper();%0A    %0A
+    private List%3CPortServiceModeChangedListener%3E portServiceModeChangedListeners = new ArrayList%3C%3E();%0A    %0A
     /**%0A     * R
@@ -2115,24 +2115,183 @@
 ctor%0A    %7D%0A%0A
+    public void addPortServiceModeChangedListener(PortServiceModeChangedListener listener) %7B%0A        portServiceModeChangedListeners.add(listener);%0A    %7D%0A    %0A
     public P
@@ -2479,24 +2479,235 @@
 ortIndex));%0A
+            model.serviceModeProperty().addListener((observable, oldVal, newVal) -%3E %7B%0A                portServiceModeChangedListeners.forEach(PortServiceModeChangedListener::serviceModeChanged);%0A            %7D);%0A
             
@@ -3607,29 +3607,22 @@
 map(
-port -%3E port.
+Port::
 getIndex
 ()).
@@ -3617,18 +3617,16 @@
 getIndex
-()
 ).collec

Add PortServiceModeChangedListener