import {
  Component, EventEmitter, Input,
  Output, OnChanges, SimpleChanges
} from '@angular/core';
import { ProbeHost } from '@overflow/commons-typescript/model/probe';
import * as CIDR from 'ip-cidr';
import * as ipRangeCheck from 'ip-range-check';
import { DiscoverPort, DiscoverService, DiscoverZone } from '@overflow/commons-typescript/model/discovery';
import { Checkbox } from 'primeng/primeng';
import { MetaIPTypeEnum, toMetaIPType } from '@overflow/commons-typescript/model/meta';

@Component({
  selector: 'of-discovery-search-config',
  templateUrl: './search-config.component.html',
})
export class SearchConfigComponent implements OnChanges {

  SUPPORT_V6 = false;

  @Input() probeHost: ProbeHost;
  @Output() requestDiscovery = new EventEmitter<DiscoverZone>();

  ipType: MetaIPTypeEnum;
  startIP: string;
  endIP: string;
  startPort: string;
  endPort: string;
  excludeIPs = [];
  excludePorts = [];
  includeServices = [];

  hostChecked = true;
  portChecked = true;
  serviceChecked = true;
  tcpChecked = true;
  udpChecked = true;

  validation = false;
  ipErrMsg: string;
  portErrMsg: string;

  fixedIPrange: number;
  ipArray = [];
  portExcludeDisplay = false;
  portChips = [];

  constructor(
  ) {
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['probeHost']) {
      this.initByProbe();
    }
    this.validation = true;
  }

  initByProbe() {
    const cidr = new CIDR(this.probeHost.probe.cidr);
    if (!cidr.isValid()) {
      return;
    }
    this.ipType = MetaIPTypeEnum.V4;
    this.startIP = cidr.start();
    this.endIP = cidr.end();
    this.startPort = '1';
    this.endPort = '1024';
    // TODO: Initialize services

    this.checkEditableIPrange(cidr);
  }

  onIPExcludeFocus() {
    const cidr = new CIDR(this.probeHost.probe.cidr);
    this.ipArray = cidr.toArray().filter((value) => {
      return this.inRange(value) || this.excludeIPs.find(obj => obj.ip === value);
    }).map(value => {
      return { ip: value };
    });

  }

  inRange(value) {
    const min = this.startIP;
    const max = this.endIP;
    if (this.ipToNum(min) <= this.ipToNum(value) && this.ipToNum(max) >= this.ipToNum(value)) {
      return true;
    }
    return false;
  }

  ipToNum(ip) {
    return Number(
      ip.split('.')
        .map(d => ('000' + d).substr(-3))
        .join('')
    );
  }

  onRequestDiscovery() {
    let discoverPort: DiscoverPort = null;
    let discoverService: DiscoverService = null;

    if (this.serviceChecked) {
      const services = [];
      for (const service of this.includeServices) {
        services.push(service.key);
      }
      discoverService = {
        includeServices: services,
      };
    }
    if (this.portChecked) {
      discoverPort = {
        firstScanRange: Number(this.startPort),
        lastScanRange: Number(this.endPort),
        includeTCP: this.tcpChecked,
        includeUDP: this.udpChecked,
        excludePorts: this.excludePorts,
        discoverService: discoverService
      };
    }
    const discoverZone: DiscoverZone = {
      discoverHost: {
        metaIPType: toMetaIPType(this.ipType),
        firstScanRange: this.startIP,
        lastScanRange: this.endIP,
        discoverPort: discoverPort,
        excludeHosts: this.excludeIPs,
      },
    };

    console.log(discoverZone);
    this.requestDiscovery.emit(discoverZone);
  }

  checkEditableIPrange(cidr) {
    const startIPArray = cidr.addressStart.parsedAddress;
    const endIPArray = cidr.addressEnd.parsedAddress;
    let count = 0;
    endIPArray.forEach((element, idx) => {
      if (element === startIPArray[idx]) {
        count++;
      }
    });
    this.fixedIPrange = count;
  }

  onPortCheckChange(serviceCheckbox: Checkbox, checked: boolean) {
    if (!checked) {
      serviceCheckbox.checked = false;
      this.serviceChecked = false;
    } else {
      if (!this.tcpChecked && !this.udpChecked) {
        this.tcpChecked = true;
        this.udpChecked = true;
      }
    }
  }

  onServiceCheckChange(portCheckbox: Checkbox, checked: boolean) {
    if (checked) {
      portCheckbox.checked = true;
      this.portChecked = true;
    }
  }

  onPortTypeCheck(portCheckbox: Checkbox, serviceCheckbox: Checkbox): void {
    if (!this.tcpChecked && !this.udpChecked) {
      this.portChecked = false;
      portCheckbox.checked = false;
      this.serviceChecked = false;
      serviceCheckbox.checked = false;
    }
  }

  onInputIP(e, idx: number) {
    const value = e.value;
    if (idx === 0) {
      this.startIP = value;
    } else {
      this.endIP = value;
    }
    this.ipErrMsg = this.validateIP(value, idx);

    if (this.ipErrMsg) {
      this.validation = false;
    } else {
      this.validation = true;
    }
  }

  validateIP(value: string, idx): string {
    if (!this.isValidIPregex(value)) {
      return 'Not valid IP format.';
    }
    if (!ipRangeCheck(value, this.probeHost.probe.cidr)) {
      return 'Not valid IP range.';
    }
    const ipArray = [this.startIP, this.endIP];
    const sortedIpArray = this.sortIP([this.startIP, this.endIP]);
    if (ipArray[0] !== sortedIpArray[0]) {
      return 'Not valiad range';
    }

    return null;
  }

  isValidIPregex(ip: string): boolean {
    if (this.ipType === MetaIPTypeEnum.V4) {
      return /^(?=\d+\.\d+\.\d+\.\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.?){4}$/.test(ip);
    }
    return false;
  }

  sortIP(ipAddressArray) {
    return ipAddressArray.sort((a, b) => {
      a = a.split('.');
      b = b.split('.');
      for (let i = 0; i < a.length; i++) {
        // tslint:disable-next-line:radix
        if ((a[i] = parseInt(a[i])) < (b[i] = parseInt(b[i]))) {
          return -1;
        } else if (a[i] > b[i]) {
          return 1;
        }
      }
      return 0;
    });
  }

  onInputPort(portNum: string, idx: number) {
    if (idx === 0) {
      this.startPort = portNum;
    } else {
      this.endPort = portNum;
    }
    this.portErrMsg = this.validatePort(portNum);

    if (this.portErrMsg) {
      this.validation = false;
    } else {
      this.validation = true;
    }
  }

  validatePort(portNum): string {
    if (Number(this.startPort) > Number(this.endPort)) {
      return 'Not valid port range.';
    }
    if (Number(portNum) <= 0 || Number(portNum) > 65535) {
      return 'Not valid port range.';
    }
    return null;
  }


  onAddExcludePort(event) {
    const port = event.value.replace(/\s/g, '');
    if (port.indexOf('~') > 0) { // e.g. 1~3000
      const splited = port.split('~');
      this.portChips.pop();
      const from = Number(splited[0]);
      const to = Number(splited[1]);
      if (this.checkInvalidPort(from)
        || this.checkInvalidPort(to)
        || !Number.isInteger(from)
        || !Number.isInteger(to)) {
        return;
      }
      const chipItem = 'All ' + from + ' ~ ' + to;
      this.portChips.push(chipItem);
      for (let i = from; i <= to; i++) {
        this.excludePorts.push(String(i));
      }
    } else {
      const num = Number(event.value);
      if (!Number.isInteger(num) || this.checkInvalidPort(num) || this.checkExistExcludePort(event.value)) {
        this.portChips.pop();
        return;
      }
      this.excludePorts.push(event.value);
    }
  }

  onRemoveExcludePort(event) {
    if (event.value.indexOf('~') > 0) {
      // e.g. 'All 1 ~ 30'
      const splited = event.value.replace(/\s/g, '').replace('All', '').split('~');
      for (let i = Number(splited[0]); i <= Number(splited[1]); i++) {
        console.log(String(i));
        const index = this.excludePorts.indexOf(String(i));
        this.excludePorts.splice(index, 1);
      }
      return;
    }
    const idx = this.excludePorts.indexOf(event.value);
    this.excludePorts.splice(idx, 1);
  }

  checkExistExcludePort(portNum) {
    return this.excludePorts.find((value) => {
      return value === portNum;
    });
  }

  checkInvalidPort(port) {
    return port <= 0 || port > 65535;
  }

}
