import { Component, Input, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
import { DropdownPanelComponent } from '@overflow/commons/ui/component/primeng';
import { DiscoverHost, Zone } from '@overflow/model/discovery';
import { toMetaIPType, MetaIPTypeEnum, MetaPortTypeEnum } from '@overflow/model/meta';
import { DiscoveryConfigService } from '../service/discovery-config.service';

const IPCIDR = require('ip-cidr');

@Component({
  selector: 'app-scanner-setting-dropdown',
  templateUrl: './scanner-setting-dropdown.component.html',
  styleUrls: ['./scanner-setting-dropdown.component.scss'],
})

export class ScannerSettingDropdownComponent implements OnInit {

  @Input() blockTarget: any;
  @Output() ready = new EventEmitter<DiscoverHost>();
  @ViewChild('panel') panel: DropdownPanelComponent;

  valid: boolean;
  ipSummary: string;
  portSummary: string;

  ipType: MetaIPTypeEnum;
  firstIP: string;
  lastIP: string;
  includePortType: MetaPortTypeEnum[];
  firstPort: string;
  lastPort: string;
  ipErrMsg: string;
  portErrMsg: string;
  zone: Zone;
  lastCondition: Condition = null;

  validIPArray: string[];

  constructor(
    private discoveryConfigService: DiscoveryConfigService
  ) {
  }

  ngOnInit(): void {
    this.discoveryConfigService.zone.subscribe(res => {
      this.zone = res as Zone;
      this.setDefault();
    });

  }

  setDefault(): void {
    if (this.zone === null) {
      return;
    }

    const cidrUtil = new IPCIDR(this.zone.network);
    this.validIPArray = cidrUtil.toArray();
    this.ipType = MetaIPTypeEnum.V4;

    this.firstIP = cidrUtil.start();
    this.lastIP = cidrUtil.end();
    this.includePortType = [MetaPortTypeEnum.TCP, MetaPortTypeEnum.UDP];
    this.firstPort = '1';
    this.lastPort = '65535';
    this.ipErrMsg = '';
    this.portErrMsg = '';

    this.setSummary();
  }

  validateIP(value: string, idx: number) {
    this.ipErrMsg = '';
    switch (this.ipType) {
      case 'V4':
        if (!(/^(?=\d+\.\d+\.\d+\.\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.?){4}$/.test(value))) {
          this.ipErrMsg = 'Invalid IP format.';
          return;
        }
        if (this.validIPArray.indexOf(value) === -1) {
          this.ipErrMsg = 'Invalid IP range.';
          return;
        }
        const from = idx === 0 ? value : this.firstIP;
        const to = idx === 1 ? value : this.lastIP;
        if (this.ipToNum(from) > this.ipToNum(to)) {
          this.ipErrMsg = 'Invalid IP range.';
          return;
        }
        break;
      case 'V6':
        break;
      default:
        break;
    }
  }

  ipPressed(evt: KeyboardEvent) {
    if (!Number.isInteger(Number(evt.key)) && evt.key !== '.') {
      return false;
    }
  }

  portPressed(evt: KeyboardEvent) {
    if (!Number.isInteger(Number(evt.key))) {
      return false;
    }
  }

  ipToNum(ip: string): number {
    return ip.split('.').map((octet, index, array) => {
      // tslint:disable-next-line:radix
      return parseInt(octet) * Math.pow(256, (array.length - index - 1));
    }).reduce((prev, curr) => {
      return prev + curr;
    });
  }

  validatePort(value: string, idx: number) {
    this.portErrMsg = '';
    const fromStr = idx === 0 ? value : this.firstPort;
    const toStr = idx === 1 ? value : this.lastPort;
    const from = Number(fromStr);
    const to = Number(toStr);
    if (from === NaN || to === NaN) {
      this.portErrMsg = 'Invalid Port Type.';
      return;
    }
    if (from <= 0) {
      this.firstPort = '1';
      return;
    }
    if (to > 65535) {
      this.lastPort = '65535';
      return;
    }
    if (from > to) {
      this.portErrMsg = 'Invalid Port range.';
      return;
    }
  }

  validateTCP(checked: boolean) {
    if (!checked && this.includePortType.indexOf(MetaPortTypeEnum.UDP) === -1) {
      this.includePortType = [MetaPortTypeEnum.TCP];
    }
  }
  validateUDP(checked: boolean) {
    if (!checked && this.includePortType.indexOf(MetaPortTypeEnum.TCP) === -1) {
      this.includePortType = [MetaPortTypeEnum.UDP];
    }
  }

  done() {
    // TODO: re-validation
    this.saveLastCondition();
    this.setSummary();
    this.panel.hide();
  }

  cancel() {
    if (null === this.lastCondition) {
      this.setDefault();
    } else {
      this.restore();
    }
  }

  saveLastCondition() {
    const c: Condition = {
      ipType: this.ipType,
      firstIP: this.firstIP,
      lastIP: this.lastIP,
      includePortType: this.includePortType,
      firstPort: this.firstPort,
      lastPort: this.lastPort,
    };
    this.lastCondition = c;
  }

  restore() {
    this.ipType = this.lastCondition.ipType;
    this.firstIP = this.lastCondition.firstIP;
    this.lastIP = this.lastCondition.lastIP;
    this.includePortType = this.lastCondition.includePortType;
    this.firstPort = this.lastCondition.firstPort;
    this.lastPort = this.lastCondition.lastPort;
    this.ipErrMsg = '';
    this.portErrMsg = '';
  }

  setSummary(): void {
    this.ipSummary = this.ipType + ' (' + this.firstIP + ' - ' + this.lastIP + ')';
    this.portSummary = this.includePortType.join(',');
    this.portSummary += ' (' + this.firstPort + ' - ' + this.lastPort + ')';

    this.configDiscoverHost();
  }

  configDiscoverHost(): void {
    const discoverHost: DiscoverHost = {
      metaIPType: toMetaIPType(this.ipType),
      firstScanRange: this.firstIP,
      lastScanRange: this.lastIP,
      discoveryConfig: {},
      discoverPort: {
        firstScanRange: Number(this.firstPort),
        lastScanRange: Number(this.lastPort),
        includeTCP: this.includePortType.indexOf(MetaPortTypeEnum.TCP) !== -1 ? true : false,
        includeUDP: this.includePortType.indexOf(MetaPortTypeEnum.UDP) !== -1 ? true : false,
        discoverService: {
          includeServices: null,
        }
      }
    };
    this.ready.emit(discoverHost);
  }

}

class Condition {
  ipType: MetaIPTypeEnum;
  firstIP: string;
  lastIP: string;
  includePortType: MetaPortTypeEnum[];
  firstPort: string;
  lastPort: string;
}
