
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, ChangeDetectorRef, HostListener } from '@angular/core';

import { Subject, Subscription, of } from 'rxjs';
import { map, catchError, take } from 'rxjs/operators';

import * as d3 from 'd3';

import { ConfirmationService } from 'primeng/primeng';

import { Zone, Host, Port, Service, DiscoverHost } from '@overflow/model/discovery';
import { RPCSubscriber } from '@overflow/commons/ui/decorator/RPCSubscriber';
import { RPCError } from '@overflow/rpc-js';
import { toMetaIPType, MetaIPTypeEnum, toMetaCryptoType, MetaCryptoTypeEnum, toMetaPortType, MetaPortTypeEnum } from '@overflow/model/meta';
import { DiscoveryModeType } from '@overflow/model/discovery/discovery';

import { ProbeService, requesterID } from '../../../commons/service/probe.service';
import { DiscoveryConfigService, DiscoveryStatusType } from '../../../commons/service/discovery-config.service';
import { Node } from '../../../commons/model/node';
import { Link } from '../../../commons/model/link';


export class DiscoveryResult {
  totalHosts: number;
  totalServices: number;
  elapsedTime: string;

  hTimer: any;

  constructor() {
    this.totalHosts = 0;
    this.totalServices = 0;
    this.elapsedTime = '00:00:00';
  }

  start() {
    const __this = this;
    const startTime = new Date().getTime();
    this.hTimer = setInterval(function () {
      // Get todays date and time
      const now = new Date().getTime();

      // Find the distance between now and the count down date
      const distance = now - startTime;

      // Time calculations for days, hours, minutes and seconds
      const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((distance % (1000 * 60)) / 1000);

      __this.setElapsedTime(hours, minutes, seconds);
    }, 1000);
  }

  setElapsedTime(hour: number, minute: number, second: number) {
    this.elapsedTime = String(hour).padStart(2, '0') + ':' + String(minute).padStart(2, '0') + ':' + String(second).padStart(2, '0');
  }

  stop(hours: number, minutes: number, seconds: number) {
    clearInterval(this.hTimer);
    this.setElapsedTime(hours, minutes, seconds);
  }

  increaseHost() {
    this.totalHosts++;
  }

  increaseService() {
    this.totalServices++;
  }
}

@Component({
  selector: 'app-pages-home',
  templateUrl: './home-page.component.html',
  styleUrls: ['./home-page.component.scss'],
  providers: [
    ConfirmationService,
  ],
})
export class HomePageComponent implements OnInit, OnDestroy {

  private discoveryTargetRef: ElementRef;

  zone: Zone;
  discoverHost: DiscoverHost;

  blockedPanel = false;
  displaySidebar = false;
  stopping = false;

  blockedDocument = false;

  showIntro = true;
  selectedNode = null;

  private zoneNode: Node;
  private nodes: Node[];
  private links: Link[];
  public simulation: d3.Simulation<Node, Link> | undefined;
  private zoomBehavior: d3.ZoomBehavior<Element, {}>;
  private discoveryContainerWidth: number;
  private discoveryContainerHeight: number;
  private readonly maxScale: number;
  private readonly minScale: number;

  private discoveryStartDate: Date;
  discoveryRequestID: string | null;
  hosts: Map<string, Host>;
  ports: Map<string, Map<number, Port>>;
  discoveryResult: DiscoveryResult;
  private preventBrowserCloseSubject: Subject<void>;
  private preventBrowserCloseSubscription: Subscription;

  @HostListener('window:resize', ['$event'])
  onResize(event) {
    if (undefined !== this.discoveryTargetRef) {
      this.discoveryContainerWidth = this.discoveryTargetRef.nativeElement.clientWidth;
      this.discoveryContainerHeight = this.discoveryTargetRef.nativeElement.clientHeight;

      this.zoneNode.fx = this.discoveryContainerWidth / 2;
      this.zoneNode.fy = this.discoveryContainerHeight / 2;

      this.simulationRestart(false);
    }
  }

  @HostListener('window:beforeunload', ['$event'])
  onBeforeUnload($event: Event) {
    if (null !== this.discoveryRequestID) {
      this.preventBrowserCloseSubject.next();
      $event.returnValue = true;
    }
  }

  @ViewChild('discoveryTarget') set discoveryTarget(content: ElementRef) {
    this.discoveryTargetRef = content;
  }

  constructor(
    private changeDetector: ChangeDetectorRef,
    private confirmationService: ConfirmationService,
    private probeService: ProbeService,
    private discoveryConfigService: DiscoveryConfigService,
  ) {
    this.nodes = [];
    this.links = [];
    this.maxScale = 1;
    this.minScale = 0.7;

    this.hosts = new Map();
    this.ports = new Map();

    this.discoveryRequestID = null;
    this.preventBrowserCloseSubject = new Subject<void>();
    this.discoveryResult = null;
  }

  ngOnInit() {
    this.probeService.subscribeNotification(this);
    // this.startDiscovery("");
    this.discoveryConfigService.zone.subscribe(res => {
      this.zone = res as Zone;
    });
    this.discoveryConfigService.discoverHost.subscribe(res => {
      this.discoverHost = res as DiscoverHost;
    });
    this.discoveryConfigService.discoveryStatus.subscribe(res => {
      if (res === DiscoveryStatusType.Started) {
        this.startDiscovery();
      } else if (res === DiscoveryStatusType.Stopping) {
        if (null !== this.discoveryRequestID) {
          this.stopping = true;
          this.probeService.send('DiscoveryService.DiscoverStop', requesterID, this.discoveryRequestID);
        }
      }
    });
    this.preventBrowserCloseSubscription = this.preventBrowserCloseSubject
      .pipe(
        map(() => {
          this.confirmationService.confirm({
            message: 'Discovery is in progress.',
            header: 'Alert',
            icon: 'pi pi-info-circle',
            acceptLabel: 'Yes',
            acceptVisible: true,
            rejectVisible: false,
          });
        }),
        catchError(error => {
          console.log(error);
          alert('An error has occurred.');
          return of();
        }),
      )
      .subscribe();
  }

  ngOnDestroy(): void {
    this.probeService.unsubscribeNotification(this);
    this.preventBrowserCloseSubscription.unsubscribe();
  }

  blockDocument() {
    this.blockedDocument = true;
    setTimeout(() => {
      this.blockedDocument = false;
    }, 3000);
  }

  startDiscovery() {
    // this.zone = {
    //   network: '192.168.1.0/24',
    //   iface: 'enp3s0',
    //   metaIPType: toMetaIPType(MetaIPTypeEnum.V4),
    //   address: '192.168.1.101',
    //   mac: '44:8a:5b:f1:f1:f3',
    // };

    // this.discoverHost = {
    //   metaIPType: toMetaIPType(MetaIPTypeEnum.V4),
    //   firstScanRange: '192.168.1.1',
    //   lastScanRange: '192.168.1.254',
    //   discoveryConfig: {
    //   },
    //   discoverPort: {
    //     firstScanRange: 1,
    //     lastScanRange: 65535,
    //     includeTCP: true,
    //     includeUDP: true,
    //     discoverService: {
    //     }
    //   }
    // };

    console.log('########################################################');
    console.log(this.zone);
    console.log(this.discoverHost);
    console.log('########################################################');

    this.discoveryResult = new DiscoveryResult();
    this.showIntro = false;
    this.changeDetector.detectChanges();

    this.discoveryContainerWidth = this.discoveryTargetRef.nativeElement.clientWidth;
    this.discoveryContainerHeight = this.discoveryTargetRef.nativeElement.clientHeight;

    this.simulationInit();

    this.nodes = [];
    this.links = [];

    this.zoneNode = new Node(this.zone.network);
    this.zoneNode.group = 'zone';
    this.zoneNode.target = this.zone;
    this.zoneNode.fx = this.discoveryContainerWidth / 2;
    this.zoneNode.fy = this.discoveryContainerHeight / 2;
    this.zoneNode.r = 60;

    this.nodes.push(
      this.zoneNode,
    );

    this.links.push(
    );

    // this.setTestData();
    // this.simulationRestart(true);

    this.simulationRestart();
    this.probeService.send('DiscoveryService.DiscoverHost', requesterID, this.zone, this.discoverHost);
  }

  simulationInit() {
    if (undefined !== this.simulation) {
      return;
    }

    const svg = d3.select(this.discoveryTargetRef.nativeElement);

    this.zoomBehavior = d3.zoom()
      .scaleExtent([0.2, 4])
      .on('zoom', () => {
        const transform = d3.event.transform;
        svg.select('g').attr('transform', 'translate(' + transform.x + ',' + transform.y + ') scale(' + transform.k + ')');
      });
    svg.call(this.zoomBehavior);

    const __this = this;
    this.simulation = d3
      .forceSimulation<Node, Link>()
      .nodes(this.nodes)
      .force('charge', d3.forceManyBody().strength(-200))
      .force('link', d3.forceLink(this.links).distance(150))
      .force('center', d3.forceCenter(this.discoveryContainerWidth / 2, this.discoveryContainerHeight / 2))
      .force('collision', d3.forceCollide().radius(function (node: Node) {
        return node.r * 1.2;
      }))
      .on('tick', () => {
        __this.changeDetector.markForCheck();
      })
      ;
  }

  simulationRestart(attachEvent: boolean = false) {
    // Update and restart the simulation.
    this.simulation
      .nodes(this.nodes)
      .force('link', d3.forceLink(this.links).distance(150))
      .force('center', d3.forceCenter(this.discoveryContainerWidth / 2, this.discoveryContainerHeight / 2))
      .alpha(1)
      .restart()
      ;
    this.changeDetector.detectChanges();

    if (attachEvent) {
      const __this = this;

      const nodeElements = d3.select(this.discoveryTargetRef.nativeElement).selectAll('.node-container');
      const linkElements = d3.select(this.discoveryTargetRef.nativeElement).selectAll('.link-container');

      function getNodeFromElement(element: Element): Node | null {
        const container = d3.select(element);
        const nodeId = container.attr('nodeId');
        return __this.getNode(nodeId);
      }

      // drag
      nodeElements.call(
        d3.drag()
          .on('start', function () {
            const node = getNodeFromElement(this);
            if (null === node || 'zone' === node.group) {
              return;
            }

            d3.event.sourceEvent.stopPropagation();

            if (!d3.event.active) {
              __this.simulation.alphaTarget(0.3).restart();
            }

            d3.event.on('drag', dragged).on('end', ended);

            function dragged(d, i) {
              node.fx = d3.event.x;
              node.fy = d3.event.y;
            }

            function ended() {
              if (!d3.event.active) {
                __this.simulation.alphaTarget(0);
              }

              node.fx = null;
              node.fy = null;
            }
          })
      );

      enum ConnectedNode {
        TARGET = 1,
        CONNECTED,
        NOT_CONNECTED,
      }

      function isConnectedNode(node: Node, element: Element): ConnectedNode {
        const _node = getNodeFromElement(element);
        if (node === _node) {
          return ConnectedNode.TARGET;
        }
        for (let i = 0; i < node.links.length; i++) {
          const link = node.links[i];
          if (node === _node || link.source === _node || link.target === _node) {
            return ConnectedNode.CONNECTED;
          }
        }
        return ConnectedNode.NOT_CONNECTED;
      }

      function isConnectedLink(node: Node, element: Element) {
        const _linkElement = d3.select(element);
        const sourceId = _linkElement.attr('sourceId');
        const targetId = _linkElement.attr('targetId');

        if (sourceId === node.id || targetId === node.id) {
          return true;
        }

        return false;
      }

      nodeElements.on('click', function () {
        d3.event.stopPropagation();

        const nodeElement = this as Element;
        const node = getNodeFromElement(nodeElement);
        if (null === node || 'zone' === node.group) {
          __this.onTargetClick(node);
          return;
        }

        if (null === __this.selectedNode) {
          nodeElements.each(function () {
            const _thisElement = this as Element;
            d3.select(_thisElement).classed('semi-unfocused', false);
            d3.select(_thisElement).classed('unfocused', false);
          });

          linkElements.each(function () {
            const _thisElement = this as Element;
            d3.select(_thisElement).classed('semi-unfocused', false);
            d3.select(_thisElement).classed('unfocused', false);
          });
        } else {
          nodeElements.each(function () {
            const _thisElement = this as Element;
            d3.select(_thisElement).classed('semi-unselected', false);
            d3.select(_thisElement).classed('unselected', false);
          });

          linkElements.each(function () {
            const _thisElement = this as Element;
            d3.select(_thisElement).classed('semi-unselected', false);
            d3.select(_thisElement).classed('unselected', false);
          });
        }

        __this.selectedNode = node;

        nodeElements.each(function () {
          const _thisElement = this as Element;
          switch (isConnectedNode(node, _thisElement)) {
            case ConnectedNode.CONNECTED:
              d3.select(_thisElement).classed('semi-unselected', true);
              break;
            case ConnectedNode.NOT_CONNECTED:
              d3.select(_thisElement).classed('unselected', true);
              break;
            default:
              break;
          }
        });

        linkElements.each(function () {
          const _thisElement = this as Element;
          if (isConnectedLink(node, _thisElement)) {
            d3.select(_thisElement).classed('semi-unselected', true);
          } else {
            d3.select(_thisElement).classed('unselected', true);
          }
        });


        __this.onTargetClick(node);
      });

      // Highlight
      const discoveryContainer = d3.select(this.discoveryTargetRef.nativeElement);
      discoveryContainer.on('click', function () {
        __this.selectedNode = null;
        __this.displaySidebar = false;

        nodeElements.each(function () {
          const _thisElement = this as Element;
          d3.select(_thisElement).classed('semi-unselected', false);
          d3.select(_thisElement).classed('unselected', false);
        });

        linkElements.each(function () {
          const _thisElement = this as Element;
          d3.select(_thisElement).classed('semi-unselected', false);
          d3.select(_thisElement).classed('unselected', false);
        });
      });

      nodeElements
        .on('mouseover', function () {
          const node = getNodeFromElement(this as Element);
          if ('zone' === node.group) {
            return;
          }

          if (null === __this.selectedNode) {
            nodeElements.each(function () {
              const _thisElement = this as Element;
              switch (isConnectedNode(node, _thisElement)) {
                case ConnectedNode.CONNECTED:
                  d3.select(_thisElement).classed('semi-unfocused', true);
                  break;
                case ConnectedNode.NOT_CONNECTED:
                  d3.select(_thisElement).classed('unfocused', true);
                  break;
                default:
                  break;
              }
            });

            linkElements.each(function () {
              const _thisElement = this as Element;
              if (isConnectedLink(node, _thisElement)) {
                d3.select(_thisElement).classed('semi-unfocused', true);
              } else {
                d3.select(_thisElement).classed('unfocused', true);
              }
            });
          } else {
            nodeElements.each(function () {
              const _thisElement = this as Element;
              switch (isConnectedNode(node, _thisElement)) {
                case ConnectedNode.TARGET:
                  d3.select(_thisElement).classed('focused', true);
                  break;
                case ConnectedNode.CONNECTED:
                  d3.select(_thisElement).classed('semi-focused', true);
                  break;
                default:
                  break;
              }
            });

            linkElements.each(function () {
              const _thisElement = this as Element;
              if (isConnectedLink(node, _thisElement)) {
                d3.select(_thisElement).classed('semi-focused', true);
              }
            });
          }

        })
        .on('mouseout', function () {
          const node = getNodeFromElement(this as Element);

          if (null === __this.selectedNode) {
            nodeElements.each(function () {
              const _thisElement = this as Element;
              d3.select(_thisElement).classed('semi-unfocused', false);
              d3.select(_thisElement).classed('unfocused', false);
            });

            linkElements.each(function () {
              const _thisElement = this as Element;
              d3.select(_thisElement).classed('semi-unfocused', false);
              d3.select(_thisElement).classed('unfocused', false);
            });

          } else {
            nodeElements.each(function () {
              const _thisElement = this as Element;
              d3.select(_thisElement).classed('semi-focused', false);
              d3.select(_thisElement).classed('focused', false);
            });

            linkElements.each(function () {
              const _thisElement = this as Element;
              d3.select(_thisElement).classed('semi-focused', false);
              d3.select(_thisElement).classed('focused', false);
            });

          }
        })
        ;

    }
  }

  zoomToFit(paddingPercent, transitionDuration) {
    const root = d3.select(this.discoveryTargetRef.nativeElement);
    const bounds = root.node().getBBox();

    const fullWidth = this.discoveryContainerWidth;
    const fullHeight = this.discoveryContainerHeight;

    const width = bounds.width;
    const height = bounds.height;

    const midX = bounds.x + width / 2;
    const midY = bounds.y + height / 2;

    if (width === 0 || height === 0) { return; } // nothing to fit

    let scale = (paddingPercent || 0.75) / Math.max(width / fullWidth, height / fullHeight);
    console.log(`scale: ${scale}`);

    if (this.maxScale < scale) {
      scale = this.maxScale;
    }
    if (this.minScale > scale) {
      scale = this.minScale;
    }

    const translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY];

    root
      .transition()
      .duration(transitionDuration || 0) // milliseconds
      .call(this.zoomBehavior.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale));
  }

  onTargetClick(node: Node) {
    console.log(node);
    this.displaySidebar = true;

    switch (node.group) {
      case 'zone':
        const zone: Zone = node.target;
        zone.hostList = [];

        this.hosts.forEach(_host => {
          if (_host.zone.network === zone.network) {
            zone.hostList.push(_host);
          }
        });
        break;
      case 'host':
        const host: Host = node.target;
        host.portList = [];
        if (this.ports.has(host.address)) {
          this.ports.get(host.address).forEach(port => {
            host.portList.push(port);
          });
        }
        break;
      default:
        break;
    }
    this.selectedNode = node;
  }

  onHideDetail() {
    const __this = this;

    const nodeElements = d3.select(this.discoveryTargetRef.nativeElement).selectAll('.node-container');
    const linkElements = d3.select(this.discoveryTargetRef.nativeElement).selectAll('.link-container');

    __this.selectedNode = null;

    nodeElements.each(function () {
      const _thisElement = this as Element;
      d3.select(_thisElement).classed('semi-unselected', false);
      d3.select(_thisElement).classed('unselected', false);
    });

    linkElements.each(function () {
      const _thisElement = this as Element;
      d3.select(_thisElement).classed('semi-unselected', false);
      d3.select(_thisElement).classed('unselected', false);
    });
  }

  /**
   * DiscoverHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.Queueing' })
  public DiscoveryQueueing(requestID: string, queueingDate: Date) {
    console.log('DiscoveryQueueing', requestID, queueingDate);
    this.discoveryRequestID = requestID;
  }

  /**
   * DiscoverHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.QueueingFailed' })
  public DiscoveryQueueingFailed(queueingFailedDate: Date) {
    console.log('DiscoveryQueueingFailed', queueingFailedDate);
  }

  /**
   * DiscoverHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.QueueingTimeout' })
  public DiscoveryQueueingTimeout(queueingTimeoutDate: Date) {
    console.log('DiscoveryQueueingTimeout', queueingTimeoutDate);
    this.discoveryRequestID = null;
  }

  /**
   * DiscoverHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveryStart' })
  public DiscoveryStart(startDate: Date) {
    console.log('DiscoveryStart', startDate);
    this.discoveryStartDate = startDate;

    this.discoveryResult.start();
  }

  /**
   * DiscoverHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveryStop' })
  public DiscoveryStop(stopDate: Date) {
    console.log('DiscoveryStop', stopDate);

    this.discoveryRequestID = null;

    const _millisecond = Math.abs(stopDate.getTime() - this.discoveryStartDate.getTime());

    const _seconds = _millisecond / 1000;
    const seconds = Math.floor(_seconds % 60);
    const _minutes = _seconds / 60;
    const minutes = Math.floor(_minutes % 60);
    const _hours = _minutes / 60;
    const hours = Math.floor(_hours % 24);
    const days = Math.floor(_hours / 24);

    console.log(`${days}일 ${hours}시 ${minutes}분 ${seconds}초`);

    let hostCount = 0;
    let serviceCount = 0;

    this.nodes.forEach((node) => {
      switch (node.group) {
        case 'host':
          hostCount++;
          break;
        case 'service':
          serviceCount++;
          break;

        default:
          break;
      }
    });

    console.log(`Host: ${hostCount}개 Service: ${serviceCount}개`);

    this.discoveryConfigService.setDiscoveryStatus(DiscoveryStatusType.Stopped);

    this.simulationRestart(true);
    this.zoomToFit(0.95, 500);

    this.discoveryResult.stop(hours, minutes, seconds);
    // this.resultMsg = [];
    // this.resultMsg.push(hostCount + '');
    // this.resultMsg.push(serviceCount + '');
    // this.resultMsg.push(hours + ':' + minutes + ':' + seconds);
    this.stopping = false;
  }

  /**
 * DiscoveryMode
 */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveryMode' })
  public DiscoveryMode(mode: DiscoveryModeType) {
    this.discoveryConfigService.setDiscoveryMode(mode);
  }

  /**
   * DiscoverHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveryError' })
  public DiscoveryError(err: RPCError) {
    console.log('DiscoveryError', err);
  }

  /**
   * DiscoveredHost
   */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveredHost' })
  public DiscoveredHost(host: Host) {
    console.log('DiscoveredHost', host);

    // if (this.hosts.has(host.address)) {

    // }

    // let dup = false;
    // this.hosts.forEach((item) => {
    //   if (item.address === host.address) {
    //     dup = true;
    //     return;
    //   }
    // });
    // if (!dup) {
    //   this.hosts.push(host);
    // }
    this.hosts.set(host.address, host);

    const hostId = `${host.address}`;

    let hostNode = this.getNode(hostId);
    if (null !== hostNode) {
      hostNode.target = host;
    } else {
      hostNode = new Node(hostId);
      hostNode.target = host;
      hostNode.group = 'host';
      hostNode.r = 40;
      hostNode.x = this.zoneNode.x;
      hostNode.y = this.zoneNode.y;

      this.nodes.push(hostNode);
      this.links.push(new Link(this.zoneNode, hostNode));

      this.discoveryResult.increaseHost();
    }

    this.simulationRestart();
  }

  /**
   * DiscoveredPort
   */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveredPort' })
  public DiscoveredPort(port: Port) {
    console.log('DiscoveredPort', port);

    let _ports: Map<number, Port>;
    if (!this.ports.has(port.host.address)) {
      _ports = new Map();
      this.ports.set(port.host.address, _ports);
    } else {
      _ports = this.ports.get(port.host.address);
    }

    _ports.set(port.portNumber, port);
  }

  /**
   * DiscoveredService
   */
  @RPCSubscriber({ method: 'DiscoveryService.DiscoveredService' })
  public DiscoveredService(service: Service) {
    console.log('DiscoveredService', service);
    const hostId = service.port.host.address;
    const serviceId = `${service.port.host.address}-${service.port.portNumber}-${service.port.metaPortType.key}`;

    const hostNode = this.getNode(hostId);
    let serviceNode = this.getNode(serviceId);
    if (null !== serviceNode) {
      serviceNode.target = service;
    } else {
      serviceNode = new Node(serviceId);
      serviceNode.target = service;
      serviceNode.group = 'service';
      serviceNode.r = 30;
      serviceNode.x = hostNode.x;
      serviceNode.y = hostNode.y;

      this.nodes.push(serviceNode);
      this.links.push(new Link(hostNode, serviceNode));

      this.discoveryResult.increaseService();
    }

    this.simulationRestart();
  }

  private getNode(id: string): Node | null {
    let _n: Node = null;
    this.nodes.some((node): boolean => {
      if (node.id === id) {
        _n = node;
        return true;
      }
      return false;
    });
    return _n;
  }


  otherHostSelected(host: Host) {
    const foundNode = this.nodes.find(node => {
      return node.group === 'host' && node.id === host.address;
    });
    if (!foundNode) {
      return;
    }
    this.selectedNode = foundNode;
  }

  private setTestData() {

    const host1: Host = {
      metaIPType: toMetaIPType(MetaIPTypeEnum.V4),
      name: 'Host1',
      address: '192.168.1.1',
      mac: '44:8a:5b:f1:f1:f3',
      osType: 'UNKNOWN',
      hostType: 'HOST',
      hostVendor: 'UNKNOWN',
      hostModel: 'UNKNOWN',
      zone: this.zone,
    };

    const hostNode = new Node('192.168.1.1');
    hostNode.group = 'host';
    hostNode.target = host1;
    hostNode.r = 40;

    const host2: Host = {
      metaIPType: toMetaIPType(MetaIPTypeEnum.V4),
      name: 'Host2',
      address: '192.168.1.2',
      mac: '44:8a:5b:f1:f1:f3',
      osType: 'UNKNOWN',
      hostType: 'HOST',
      hostVendor: 'UNKNOWN',
      hostModel: 'UNKNOWN',
      zone: this.zone,
    };

    const hostNode2 = new Node('192.168.1.2');
    hostNode2.group = 'host';
    hostNode2.target = host2;
    hostNode2.r = 40;

    const host3: Host = {
      metaIPType: toMetaIPType(MetaIPTypeEnum.V4),
      name: 'Host2',
      address: '192.168.1.3',
      mac: '44:8a:5b:f1:f1:f3',
      osType: 'UNKNOWN',
      hostType: 'HOST',
      hostVendor: 'UNKNOWN',
      hostModel: 'UNKNOWN',
      zone: this.zone,
    };

    const hostNode3 = new Node('192.168.1.3');
    hostNode3.group = 'host';
    hostNode3.target = host3;
    hostNode3.r = 40;

    const serviceNode1 = new Node('192.168.1.1-10-HTTP');
    serviceNode1.target = {
      metaCryptoType: toMetaCryptoType(MetaCryptoTypeEnum.NONE),
      key: 'HTTP',
      name: 'Apache',
      serviceType: 'WEB',
      serviceVendor: 'Apache',
      serviceVersion: 'UNKNOWN',
      port: {
        host: host1,
        portNumber: 10,
        metaPortType: toMetaPortType(MetaPortTypeEnum.TCP),
      },
    };
    serviceNode1.group = 'service';
    serviceNode1.r = 30;

    const serviceNode2 = new Node('192.168.1.1-20-HTTP');
    serviceNode2.target = {
      metaCryptoType: toMetaCryptoType(MetaCryptoTypeEnum.NONE),
      key: 'HTTP',
      name: 'Apache',
      serviceType: 'WEB',
      serviceVendor: 'Apache',
      serviceVersion: 'UNKNOWN',
      port: {
        host: host1,
        portNumber: 20,
        metaPortType: toMetaPortType(MetaPortTypeEnum.TCP),
      },
    };
    serviceNode2.group = 'service';
    serviceNode2.r = 30;

    const serviceNode3 = new Node('192.168.1.1-30-HTTP');
    serviceNode3.target = {
      metaCryptoType: toMetaCryptoType(MetaCryptoTypeEnum.NONE),
      key: 'HTTP',
      name: 'Apache',
      serviceType: 'WEB',
      serviceVendor: 'Apache',
      serviceVersion: 'UNKNOWN',
      port: {
        host: host1,
        portNumber: 30,
        metaPortType: toMetaPortType(MetaPortTypeEnum.TCP),
      },

    };
    serviceNode3.group = 'service';
    serviceNode3.r = 30;


    const serviceNode31 = new Node('192.168.1.3-10-HTTP');
    serviceNode31.target = {
      metaCryptoType: toMetaCryptoType(MetaCryptoTypeEnum.NONE),
      key: 'HTTP',
      name: 'Apache',
      serviceType: 'WEB',
      serviceVendor: 'Apache',
      serviceVersion: 'UNKNOWN',
      port: {
        host: host3,
        portNumber: 10,
        metaPortType: toMetaPortType(MetaPortTypeEnum.TCP),
      },

    };
    serviceNode31.group = 'service';
    serviceNode31.r = 30;

    const serviceNode32 = new Node('192.168.1.3-20-HTTP');
    serviceNode32.target = {
      metaCryptoType: toMetaCryptoType(MetaCryptoTypeEnum.NONE),
      key: 'HTTP',
      name: 'Apache',
      serviceType: 'WEB',
      serviceVendor: 'Apache',
      serviceVersion: 'UNKNOWN',
      port: {
        host: host3,
        portNumber: 20,
        metaPortType: toMetaPortType(MetaPortTypeEnum.TCP),
      },

    };
    serviceNode32.group = 'service';
    serviceNode32.r = 30;

    this.nodes.push(
      hostNode,
      hostNode2,
      hostNode3,
      serviceNode1,
      serviceNode2,
      serviceNode3,
      serviceNode31,
      serviceNode32,
    );

    this.links.push(
      new Link(this.zoneNode, hostNode),
      new Link(this.zoneNode, hostNode2),
      new Link(this.zoneNode, hostNode3),
      new Link(hostNode, serviceNode1),
      new Link(hostNode, serviceNode2),
      new Link(hostNode, serviceNode3),
      new Link(hostNode3, serviceNode31),
      new Link(hostNode3, serviceNode32),
    );
  }

}
