content: // 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 com.cloud.hypervisor.vmware.mo;
  
  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.OutputStreamWriter;
  import java.util.ArrayList;
  import java.util.Properties;
  
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  public class SnapshotDescriptor {
      protected Logger logger = LogManager.getLogger(getClass());
  
      private final Properties _properties = new Properties();
  
      public SnapshotDescriptor() {
      }
  
      public void parse(byte[] vmsdFileContent) throws IOException {
          BufferedReader in = null;
          try {
              in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(vmsdFileContent),"UTF-8"));
              String line;
              while ((line = in.readLine()) != null) {
                  // TODO, remember to remove this log, temporarily added for debugging purpose
                  logger.info("Parse snapshot file content: " + line);
  
                  String[] tokens = line.split("=");
                  if (tokens.length == 2) {
                      String name = tokens[0].trim();
                      String value = tokens[1].trim();
                      if (value.charAt(0) == '\"')
                          value = value.substring(1, value.length() - 1);
  
                      _properties.put(name, value);
                  }
              }
          } finally {
              if (in != null)
                  in.close();
          }
      }
  
      public void removeDiskReferenceFromSnapshot(String diskFileName) {
          String numSnapshotsStr = _properties.getProperty("snapshot.numSnapshots");
          if (numSnapshotsStr != null) {
              int numSnaphosts = Integer.parseInt(numSnapshotsStr);
              for (int i = 0; i < numSnaphosts; i++) {
                  String numDisksStr = _properties.getProperty(String.format("snapshot%d.numDisks", i));
                  int numDisks = Integer.parseInt(numDisksStr);
  
                  boolean diskFound = false;
                  for (int j = 0; j < numDisks; j++) {
                      String keyName = String.format("snapshot%d.disk%d.fileName", i, j);
                      String fileName = _properties.getProperty(keyName);
                      if (!diskFound) {
                          if (fileName.equalsIgnoreCase(diskFileName)) {
                              diskFound = true;
                              _properties.remove(keyName);
                          }
                      } else {
                          _properties.setProperty(String.format("snapshot%d.disk%d.fileName", i, j - 1), fileName);
                      }
                  }
  
                  if (diskFound)
                      _properties.setProperty(String.format("snapshot%d.numDisks", i), String.valueOf(numDisks - 1));
              }
          }
      }
  
      public byte[] getVmsdContent() {
          ByteArrayOutputStream bos = new ByteArrayOutputStream();
  
          try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(bos, "UTF-8"));) {
  
              out.write(".encoding = \"UTF-8\"");
              out.newLine();
              out.write(String.format("snapshot.lastUID = \"%s\"", _properties.getProperty("snapshot.lastUID")));
              out.newLine();
              String numSnapshotsStr = _properties.getProperty("snapshot.numSnapshots");
              if (numSnapshotsStr == null || numSnapshotsStr.isEmpty())
                  numSnapshotsStr = "0";
              out.write(String.format("snapshot.numSnapshots = \"%s\"", numSnapshotsStr));
              out.newLine();
  
              String value = _properties.getProperty("snapshot.current");
              if (value != null) {
                  out.write(String.format("snapshot.current = \"%s\"", value));
                  out.newLine();
              }
  
              String key;
              for (int i = 0; i < Integer.parseInt(numSnapshotsStr); i++) {
                  key = String.format("snapshot%d.uid", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  key = String.format("snapshot%d.filename", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  key = String.format("snapshot%d.displayName", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  key = String.format("snapshot%d.description", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  key = String.format("snapshot%d.createTimeHigh", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  key = String.format("snapshot%d.createTimeLow", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  key = String.format("snapshot%d.numDisks", i);
                  value = _properties.getProperty(key);
                  out.write(String.format("%s = \"%s\"", key, value));
                  out.newLine();
  
                  int numDisks = Integer.parseInt(value);
                  for (int j = 0; j < numDisks; j++) {
                      key = String.format("snapshot%d.disk%d.fileName", i, j);
                      value = _properties.getProperty(key);
                      out.write(String.format("%s = \"%s\"", key, value));
                      out.newLine();
  
                      key = String.format("snapshot%d.disk%d.node", i, j);
                      value = _properties.getProperty(key);
                      out.write(String.format("%s = \"%s\"", key, value));
                      out.newLine();
                  }
              }
          } catch (IOException e) {
              assert (false);
              logger.error("Unexpected exception ", e);
          }
  
          return bos.toByteArray();
      }
  
      private int getSnapshotId(String seqStr) {
          if (seqStr != null) {
              int seq = Integer.parseInt(seqStr);
              String numSnapshotStr = _properties.getProperty("snapshot.numSnapshots");
              assert (numSnapshotStr != null);
              for (int i = 0; i < Integer.parseInt(numSnapshotStr); i++) {
                  String value = _properties.getProperty(String.format("snapshot%d.uid", i));
                  if (value != null && Integer.parseInt(value) == seq)
                      return i;
              }
          }
  
          return 0;
      }
  
      public SnapshotInfo[] getCurrentDiskChain() {
          ArrayList<SnapshotInfo> l = new ArrayList<SnapshotInfo>();
          String current = _properties.getProperty("snapshot.current");
          int id;
          while (current != null) {
              id = getSnapshotId(current);
              String numDisksStr = _properties.getProperty(String.format("snapshot%d.numDisks", id));
              int numDisks = 0;
              if (numDisksStr != null && !numDisksStr.isEmpty()) {
                  numDisks = Integer.parseInt(numDisksStr);
                  DiskInfo[] disks = new DiskInfo[numDisks];
                  for (int i = 0; i < numDisks; i++) {
                      disks[i] =
                          new DiskInfo(_properties.getProperty(String.format("snapshot%d.disk%d.fileName", id, i)), _properties.getProperty(String.format(
                              "snapshot%d.disk%d.node", id, i)));
                  }
  
                  SnapshotInfo info = new SnapshotInfo();
                  info.setId(id);
                  info.setNumOfDisks(numDisks);
                  info.setDisks(disks);
                  info.setDisplayName(_properties.getProperty(String.format("snapshot%d.displayName", id)));
                  l.add(info);
              }
  
              current = _properties.getProperty(String.format("snapshot%d.parent", id));
          }
  
          return l.toArray(new SnapshotInfo[0]);
      }
  
      public static class SnapshotInfo {
          private int _id;
          private String _displayName;
          private int _numOfDisks;
          private DiskInfo[] _disks;
  
          public SnapshotInfo() {
          }
  
          public void setId(int id) {
              _id = id;
          }
  
          public int getId() {
              return _id;
          }
  
          public void setDisplayName(String name) {
              _displayName = name;
          }
  
          public String getDisplayName() {
              return _displayName;
          }
  
          public void setNumOfDisks(int numOfDisks) {
              _numOfDisks = numOfDisks;
          }
  
          public int getNumOfDisks() {
              return _numOfDisks;
          }
  
          public void setDisks(DiskInfo[] disks) {
              _disks = disks;
          }
  
          public DiskInfo[] getDisks() {
              return _disks;
          }
  
          @Override
          public String toString() {
              StringBuffer sb = new StringBuffer();
              sb.append("SnapshotInfo : { id: ");
              sb.append(_id);
              sb.append(", displayName: ").append(_displayName);
              sb.append(", numOfDisks: ").append(_numOfDisks);
              sb.append(", disks: [");
              if (_disks != null) {
                  int i = 0;
                  for (DiskInfo diskInfo : _disks) {
                      if (i > 0)
                          sb.append(", ");
                      sb.append(diskInfo.toString());
                      i++;
                  }
              }
              sb.append("]}");
  
              return sb.toString();
          }
      }
  
      public static class DiskInfo {
          private final String _diskFileName;
          private final String _deviceName;
  
          public DiskInfo(String diskFileName, String deviceName) {
              _diskFileName = diskFileName;
              _deviceName = deviceName;
          }
  
          public String getDiskFileName() {
              return _diskFileName;
          }
  
          public String getDeviceName() {
              return _deviceName;
          }
  
          @Override
          public String toString() {
              return "DiskInfo: { device: " + _deviceName + ", file: " + _diskFileName + " }";
          }
      }
  }
  