/**
 * @license
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed 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.
 */
import '../../../styles/shared-styles';
import '../../shared/gr-download-commands/gr-download-commands';
import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-download-dialog_html';
import {patchNumEquals} from '../../../utils/patch-set-util';
import {changeBaseURL} from '../../../utils/change-util';
import {customElement, property, computed, observe} from '@polymer/decorators';
import {ChangeInfo, ServerInfo, PatchSetNum} from '../../../types/common';
import {RevisionInfo} from '../../shared/revision-info/revision-info';
import {GrDownloadCommands} from '../../shared/gr-download-commands/gr-download-commands';
import {GrButton} from '../../shared/gr-button/gr-button';
import {hasOwnProperty} from '../../../utils/common-util';
import {GrOverlayStops} from '../../shared/gr-overlay/gr-overlay';

export interface GrDownloadDialog {
  $: {
    download: HTMLAnchorElement;
    downloadCommands: GrDownloadCommands;
    closeButton: GrButton;
  };
}

@customElement('gr-download-dialog')
export class GrDownloadDialog extends GestureEventListeners(
  LegacyElementMixin(PolymerElement)
) {
  static get template() {
    return htmlTemplate;
  }

  /**
   * Fired when the user presses the close button.
   *
   * @event close
   */

  @property({type: Object})
  change: ChangeInfo | undefined;

  @property({type: Object})
  config?: ServerInfo;

  @property({type: String})
  patchNum: PatchSetNum | undefined;

  @property({type: String})
  _selectedScheme?: string;

  @computed('change', 'patchNum')
  get _schemes() {
    // Polymer 2: check for undefined
    if (this.change === undefined || this.patchNum === undefined) {
      return [];
    }

    for (const rev of Object.values(this.change.revisions || {})) {
      if (patchNumEquals(rev._number, this.patchNum)) {
        const fetch = rev.fetch;
        if (fetch) {
          return Object.keys(fetch).sort();
        }
        break;
      }
    }
    return [];
  }

  /** @override */
  ready() {
    super.ready();
    this._ensureAttribute('role', 'dialog');
  }

  focus() {
    if (this._schemes.length) {
      this.$.downloadCommands.focusOnCopy();
    } else {
      this.$.download.focus();
    }
  }

  getFocusStops(): GrOverlayStops {
    return {
      start: this.$.downloadCommands.$.downloadTabs,
      end: this.$.closeButton,
    };
  }

  _computeDownloadCommands(
    change?: ChangeInfo,
    patchNum?: PatchSetNum,
    selectedScheme?: string
  ) {
    let commandObj;
    if (!change || !selectedScheme) return [];
    for (const rev of Object.values(change.revisions || {})) {
      if (
        patchNumEquals(rev._number, patchNum) &&
        rev &&
        rev.fetch &&
        hasOwnProperty(rev.fetch, selectedScheme)
      ) {
        commandObj = rev.fetch[selectedScheme].commands;
        break;
      }
    }
    const commands = [];
    for (const title in commandObj) {
      if (!commandObj || !hasOwnProperty(commandObj, title)) {
        continue;
      }
      commands.push({
        title,
        command: commandObj[title],
      });
    }
    return commands;
  }

  _computeZipDownloadLink(change?: ChangeInfo, patchNum?: PatchSetNum) {
    return this._computeDownloadLink(change, patchNum, true);
  }

  _computeZipDownloadFilename(change?: ChangeInfo, patchNum?: PatchSetNum) {
    return this._computeDownloadFilename(change, patchNum, true);
  }

  _computeDownloadLink(
    change?: ChangeInfo,
    patchNum?: PatchSetNum,
    zip?: boolean
  ) {
    // Polymer 2: check for undefined
    if (change === undefined || patchNum === undefined) {
      return '';
    }
    return (
      changeBaseURL(change.project, change._number, patchNum) +
      '/patch?' +
      (zip ? 'zip' : 'download')
    );
  }

  _computeDownloadFilename(
    change?: ChangeInfo,
    patchNum?: PatchSetNum,
    zip?: boolean
  ) {
    // Polymer 2: check for undefined
    if (change === undefined || patchNum === undefined) {
      return '';
    }

    let shortRev = '';
    for (const rev in change.revisions) {
      if (patchNumEquals(change.revisions[rev]._number, patchNum)) {
        shortRev = rev.substr(0, 7);
        break;
      }
    }
    return shortRev + '.diff.' + (zip ? 'zip' : 'base64');
  }

  _computeHidePatchFile(change?: ChangeInfo, patchNum?: PatchSetNum) {
    // Polymer 2: check for undefined
    if (change === undefined || patchNum === undefined) {
      return false;
    }
    for (const rev of Object.values(change.revisions || {})) {
      if (patchNumEquals(rev._number, patchNum)) {
        const parentLength =
          rev.commit && rev.commit.parents ? rev.commit.parents.length : 0;
        return parentLength === 0 || parentLength > 1;
      }
    }
    return false;
  }

  _computeArchiveDownloadLink(
    change?: ChangeInfo,
    patchNum?: PatchSetNum,
    format?: string
  ) {
    // Polymer 2: check for undefined
    if (
      change === undefined ||
      patchNum === undefined ||
      format === undefined
    ) {
      return '';
    }
    return (
      changeBaseURL(change.project, change._number, patchNum) +
      '/archive?format=' +
      format
    );
  }

  _computePatchSetQuantity(revisions?: {[revisionId: string]: RevisionInfo}) {
    if (!revisions) {
      return 0;
    }
    return Object.keys(revisions).length;
  }

  _handleCloseTap(e: Event) {
    e.preventDefault();
    e.stopPropagation();
    this.dispatchEvent(
      new CustomEvent('close', {
        composed: true,
        bubbles: false,
      })
    );
  }

  @observe('_schemes')
  _schemesChanged(schemes: string[]) {
    if (schemes.length === 0) {
      return;
    }
    if (!this._selectedScheme || !schemes.includes(this._selectedScheme)) {
      this._selectedScheme = schemes.sort()[0];
    }
  }

  _computeShowDownloadCommands(schemes: string[]) {
    return schemes.length ? '' : 'hidden';
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'gr-download-dialog': GrDownloadDialog;
  }
}
