/**
 * Copyright (c) 2016-2021 by the respective copyright holders.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package com.zsmartsystems.zigbee.console;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import com.zsmartsystems.zigbee.CommandResult;
import com.zsmartsystems.zigbee.ZigBeeEndpoint;
import com.zsmartsystems.zigbee.ZigBeeNetworkManager;
import com.zsmartsystems.zigbee.zcl.ZclAttribute;
import com.zsmartsystems.zigbee.zcl.ZclCluster;
import com.zsmartsystems.zigbee.zcl.ZclStatus;
import com.zsmartsystems.zigbee.zcl.clusters.general.ReadAttributesResponse;
import com.zsmartsystems.zigbee.zcl.field.ReadAttributeStatusRecord;

/**
 *
 * @author Chris Jackson
 *
 */
public class ZigBeeConsoleAttributeReadCommand extends ZigBeeConsoleAbstractCommand {

    @Override
    public String getCommand() {
        return "read";
    }

    @Override
    public String getDescription() {
        return "Read one or more attributes. If no attribute specified, all attributes will be read.";
    }

    @Override
    public String getSyntax() {
        return "ENDPOINT CLUSTER [ATTRIBUTE1 ATTRIBUTE2 ...] [PERIOD=x] [CYCLES=x]";
    }

    @Override
    public String getHelp() {
        return "";
    }

    @Override
    public void process(ZigBeeNetworkManager networkManager, String[] args, PrintStream out)
            throws IllegalArgumentException, InterruptedException, ExecutionException {
        if (args.length < 2) {
            throw new IllegalArgumentException("Invalid number of arguments");
        }

        final ZigBeeEndpoint endpoint = getEndpoint(networkManager, args[1]);
        ZclCluster cluster = getCluster(endpoint, args[2]);

        int repeatPeriod = 0;
        int repeatCycles = 10;
        final Map<Integer, String> attributes = new HashMap<>();
        for (int i = 3; i < args.length; i++) {
            String cmd[] = args[i].split("=");
            if (cmd != null && cmd.length == 2) {
                switch (cmd[0].toLowerCase()) {
                    case "period":
                        repeatPeriod = parseInteger(cmd[1]);
                        break;
                    case "cycles":
                        repeatCycles = parseInteger(cmd[1]);
                        break;
                    default:
                        break;
                }
                continue;
            }
            Integer attributeId = parseAttribute(args[i]);
            ZclAttribute attribute = cluster.getAttribute(attributeId);
            attributes.put(attributeId,
                    attribute != null ? attribute.getName() : String.format("Attribute %d", attributeId));
        }

        if(attributes.isEmpty()) {
            if(!cluster.discoverAttributes(false, null).get()) {
                out.println("Failed to discover attributes");
            }
            cluster.getAttributes().forEach(zclAttribute -> {
                if(zclAttribute.isImplemented()) {
                    attributes.put(zclAttribute.getId(), zclAttribute.getName());
                }
            });
        }

        StringBuilder strAttributes = new StringBuilder();
        for (String value : attributes.values()) {
            if (strAttributes.length() != 0) {
                strAttributes.append(", ");
            }
            strAttributes.append(value);
        }

        out.println("Reading endpoint " + endpoint.getEndpointAddress() + ", cluster " + printCluster(cluster)
                + ", attributes " + strAttributes.toString()
                + (repeatPeriod == 0 ? "" : (" @period = " + repeatPeriod + " sec")));

        for (int cnt = 0; cnt < repeatCycles; cnt++) {
            if (!readAttribute(out, cluster, attributes)) {
                break;
            }
            if (repeatPeriod == 0) {
                break;
            }
            Thread.sleep(repeatPeriod * 1000L);
        }
    }

    private boolean readAttribute(PrintStream out, ZclCluster cluster, Map<Integer, String> attributes)
            throws InterruptedException, ExecutionException {
        CommandResult result = cluster.readAttributes(new ArrayList<>(attributes.keySet())).get();

        if (result.isSuccess()) {
            final ReadAttributesResponse response = result.getResponse();

            out.println(String.format("Response for cluster 0x%04x", response.getClusterId()));

            if (response.getRecords().size() == 0) {
                out.println("No records returned");
                return false;
            }

            for (ReadAttributeStatusRecord statusRecord : response.getRecords()) {
                if (statusRecord.getStatus() == ZclStatus.SUCCESS) {
                    out.println("Attribute " + statusRecord.getAttributeIdentifier() + ", type "
                            + statusRecord.getAttributeDataType() + ", value: " + statusRecord.getAttributeValue());
                } else {
                    out.println("Attribute " + statusRecord.getAttributeIdentifier() + " error: "
                            + statusRecord.getStatus());
                }
            }

            return true;
        } else {
            out.println("Error executing command: " + result);
            return false;
        }
    }
}

@@ -2965,16 +2965,17 @@
       if
+ 
 (attribu
@@ -3005,16 +3005,17 @@
       if
+ 
 (!cluste
@@ -3213,16 +3213,17 @@
       if
+ 
 (zclAttr
@@ -4917,157 +4917,501 @@
     
-if (statusRecord.getStatus() == ZclStatus.SUCCESS) %7B%0A                    out.println(%22Attribute %22 + statusRecord.getAttributeIdentifier() + %22, type %22
+ZclAttribute attribute = cluster.getAttribute(statusRecord.getAttributeIdentifier());%0A                String name = %22UNKNOWN%22;%0A                if (attribute != null) %7B%0A                    name = attribute.getName();%0A                %7D%0A                if (statusRecord.getStatus() == ZclStatus.SUCCESS) %7B%0A                    out.println(%0A                            String.format(%22Attribute %254d  %25-50s  %25-30s  %25s%22, statusRecord.getAttributeIdentifier(),%0A                                    name,
 %0A   
@@ -5427,33 +5427,39 @@
                 
-+
+       
  statusRecord.ge
@@ -5482,24 +5482,9 @@
 pe()
- + %22, value: %22 +
+,
  sta
@@ -5512,16 +5512,28 @@
 eValue()
+.toString())
 );%0A     
@@ -5588,22 +5588,48 @@
 tln(
-%22Attribute %22 +
+String.format(%22Attribute %254d  Error %25s%22,
  sta
@@ -5666,21 +5666,9 @@
 er()
- + %22 error: %22
+,
 %0A   
@@ -5691,18 +5691,16 @@
         
- +
  statusR
@@ -5716,16 +5716,28 @@
 Status()
+.toString())
 );%0A     

Improve Read Attribute console command formatting (#1245)