import { ReviewDialogComponent } from './review-dialog/review-dialog.component';
import { MatDialog, MatSnackBar } from '@angular/material';
import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { MainDataService } from '../maindata.service';
import { BackendService } from './backend.service';

import { TestControllerService } from './test-controller.service';
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
import { UnitDef, Testlet, EnvironmentData, MaxTimerData } from './test-controller.classes';
import { LastStateKey, LogEntryKey, BookletData, UnitData, MaxTimerDataType, TaggedString } from './test-controller.interfaces';
import { Subscription, Observable, of, from } from 'rxjs';
import { switchMap, concatMap } from 'rxjs/operators';
import { CustomtextService, ServerError } from 'iqb-components';
import { appconfig } from '../app.config';

@Component({
  templateUrl: './test-controller.component.html',
  styleUrls: ['./test-controller.component.css']
})
export class TestControllerComponent implements OnInit, OnDestroy {
  private loginDataSubscription: Subscription = null;
  private navigationRequestSubsription: Subscription = null;
  private maxTimerSubscription: Subscription = null;
  private unitLoadQueueSubscription1: Subscription = null;
  private unitLoadQueueSubscription2: Subscription = null;

  public showProgress = true;

  private lastUnitSequenceId = 0;
  private lastTestletIndex = 0;
  private timerValue: MaxTimerData = null;
  private timerRunning = false;
  private allUnitIds: string[] = [];
  private progressValue = 0;
  private loadedUnitCount = 0;
  private unitLoadQueue: TaggedString[] = [];

  constructor (
    @Inject('APP_VERSION') public appVersion: string,
    private mds: MainDataService,
    public tcs: TestControllerService,
    private bs: BackendService,
    private reviewDialog: MatDialog,
    private snackBar: MatSnackBar,
    private router: Router,
    private cts: CustomtextService
  ) { }

  private static getChildElements(element) {
    return Array.prototype.slice.call(element.childNodes)
    .filter(function (e) { return e.nodeType === 1; });
  }

  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  // private: recursive reading testlets/units from xml
  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  private addTestletContentFromBookletXml(targetTestlet: Testlet, node: Element) {
    const childElements = TestControllerComponent.getChildElements(node);
    if (childElements.length > 0) {
      let codeToEnter = '';
      let codePrompt = '';
      let maxTime = -1;

      let restrictionElement: Element = null;
      for (let childIndex = 0; childIndex < childElements.length; childIndex++) {
        if (childElements[childIndex].nodeName === 'Restrictions') {
          restrictionElement = childElements[childIndex];
          break;
        }
      }
      if (restrictionElement !== null) {
        const restrictionElements = TestControllerComponent.getChildElements(restrictionElement);
        for (let childIndex = 0; childIndex < restrictionElements.length; childIndex++) {
          if (restrictionElements[childIndex].nodeName === 'CodeToEnter') {
            const restrictionParameter = restrictionElements[childIndex].getAttribute('parameter');
            if ((typeof restrictionParameter !== 'undefined') && (restrictionParameter !== null)) {
              codeToEnter = restrictionParameter.toUpperCase();
              codePrompt = restrictionElements[childIndex].textContent;
            }
          } else if (restrictionElements[childIndex].nodeName === 'TimeMax') {
            const restrictionParameter = restrictionElements[childIndex].getAttribute('parameter');
            if ((typeof restrictionParameter !== 'undefined') && (restrictionParameter !== null)) {
              maxTime = Number(restrictionParameter);
              if (isNaN(maxTime)) {
                maxTime = -1;
              }
            }
          }
        }
      }

      if (codeToEnter.length > 0) {
        targetTestlet.codeToEnter = codeToEnter;
        targetTestlet.codePrompt = codePrompt;
      }
      targetTestlet.maxTimeLeft = maxTime;
      if (this.tcs.LastMaxTimerState) {
        if (this.tcs.LastMaxTimerState.hasOwnProperty(targetTestlet.id)) {
          targetTestlet.maxTimeLeft = this.tcs.LastMaxTimerState[targetTestlet.id];
        }
      }

      for (let childIndex = 0; childIndex < childElements.length; childIndex++) {
        if (childElements[childIndex].nodeName === 'Unit') {
          const myUnitId = childElements[childIndex].getAttribute('id');
          let myUnitAlias = childElements[childIndex].getAttribute('alias');
          if (!myUnitAlias) {
            myUnitAlias = myUnitId;
          }
          let myUnitAliasClear = myUnitAlias;
          let unitIdSuffix = 1;
          while (this.allUnitIds.indexOf(myUnitAliasClear) > -1) {
            myUnitAliasClear = myUnitAlias + '%' + unitIdSuffix.toString();
            unitIdSuffix += 1;
          }
          this.allUnitIds.push(myUnitAliasClear);

          targetTestlet.addUnit(this.lastUnitSequenceId, myUnitId,
                childElements[childIndex].getAttribute('label'), myUnitAliasClear,
                childElements[childIndex].getAttribute('labelshort'));
          this.lastUnitSequenceId += 1;

        } else if (childElements[childIndex].nodeName === 'Testlet') {
          let testletId: string = childElements[childIndex].getAttribute('id');
          if (!testletId) {
            testletId = 'Testlet' + this.lastTestletIndex.toString();
            this.lastTestletIndex += 1;
          }
          let testletLabel: string = childElements[childIndex].getAttribute('label');
          if ((typeof testletLabel !== 'undefined') && (testletLabel !== null)) {
            testletLabel = testletId;
          }

          this.addTestletContentFromBookletXml(targetTestlet.addTestlet(testletId, testletLabel), childElements[childIndex]);
        }
      }
    }
  }

  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  // private: reading booklet from xml
  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  private getBookletFromXml(xmlString: string): Testlet {
    let rootTestlet: Testlet = null;

    try {
      const oParser = new DOMParser();
      const oDOM = oParser.parseFromString(xmlString, 'text/xml');
      if (oDOM.documentElement.nodeName === 'Booklet') {
        // ________________________
        const metadataElements = oDOM.documentElement.getElementsByTagName('Metadata');
        if (metadataElements.length > 0) {
          const metadataElement = metadataElements[0];
          const IdElement = metadataElement.getElementsByTagName('Id')[0];
          const LabelElement = metadataElement.getElementsByTagName('Label')[0];
          rootTestlet = new Testlet(0, IdElement.textContent, LabelElement.textContent);
          const unitsElements = oDOM.documentElement.getElementsByTagName('Units');
          if (unitsElements.length > 0) {
            const customTextsElements = oDOM.documentElement.getElementsByTagName('CustomTexts');
            if (customTextsElements.length > 0) {
              const customTexts = TestControllerComponent.getChildElements(customTextsElements[0]);
              const customTextsForBooklet = {};
              for (let childIndex = 0; childIndex < customTexts.length; childIndex++) {
                if (customTexts[childIndex].nodeName === 'Text') {
                  const customTextKey = customTexts[childIndex].getAttribute('key');
                  if ((typeof customTextKey !== 'undefined') && (customTextKey !== null)) {
                    customTextsForBooklet[customTextKey] = customTexts[childIndex].textContent;
                  }
                }
              }
              this.cts.addCustomTexts(customTextsForBooklet);
            }

            const bookletConfigElements = oDOM.documentElement.getElementsByTagName('BookletConfig');

            if (bookletConfigElements.length > 0) {
              const bookletConfigs = TestControllerComponent.getChildElements(bookletConfigElements[0]);
              for (let childIndex = 0; childIndex < bookletConfigs.length; childIndex++) {
                const configParameter = bookletConfigs[childIndex].getAttribute('parameter');
                // const configValue = bookletConfigs[childIndex].textContent;

                switch (bookletConfigs[childIndex].nodeName) {
                  // ----------------------
                  case 'NavPolicy':
                    if (configParameter) {
                      if (configParameter.toUpperCase() === 'NextOnlyIfPresentationComplete'.toUpperCase()) {
                        this.tcs.navPolicyNextOnlyIfPresentationComplete = true;
                      }
                    }
                    break;
                  // ----------------------
                  case 'NavButtons':
                    if (configParameter) {
                      switch (configParameter.toUpperCase()) {
                        case 'ON':
                          this.tcs.navButtons = true;
                          this.tcs.navArrows = true;
                          break;
                        case 'OFF':
                          this.tcs.navButtons = false;
                          this.tcs.navArrows = false;
                          break;
                        case 'ARROWSONLY': // default
                          this.tcs.navButtons = false;
                          this.tcs.navArrows = true;
                          break;
                        default:
                          console.log('unknown booklet configParameter NavButtons "' + configParameter + '"');
                          break;
                      }
                    }
                    break;
                  // ----------------------
                  case 'PageNavBar':
                    if (configParameter) {
                      if (configParameter.toUpperCase() === 'OFF') {
                        this.tcs.pageNav = false;
                      }
                    }
                    break;
                  // ----------------------
                  case 'Logging':
                    if (configParameter) {
                      if (configParameter.toUpperCase() === 'OFF') {
                        this.tcs.logging = false;
                      }
                    }
                    break;
                  // ----------------------
                  case 'Loading':
                    if (configParameter) {
                      if (configParameter.toUpperCase() === 'EAGER') {
                        this.tcs.lazyloading = false;
                      }
                    }
                    break;
                  // ----------------------
                  default:
                    console.log('unknown booklet config "' + bookletConfigs[childIndex].nodeName + '"');
                    break;
                }
              }
            }

            // recursive call through all testlets
            this.lastUnitSequenceId = 1;
            this.lastTestletIndex = 1;
            this.allUnitIds = [];
            this.addTestletContentFromBookletXml(rootTestlet, unitsElements[0]);
          }
        }
      }
    } catch (error) {
      console.log('error reading booklet XML:');
      console.log(error);

      rootTestlet = null;
    }
    return rootTestlet;
  }

  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  // private: get player if not already available
  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  private loadPlayerOk(playerId: string): Observable<boolean> {
    if (this.tcs.hasPlayer(playerId)) {
      return of(true);
    } else {
      // to avoid multiple calls before returning:
      this.tcs.addPlayer(playerId, '');
      return this.bs.getResource('', this.tcs.normaliseId(playerId, 'html'), true)
          .pipe(
            switchMap(myData => {
              if (myData instanceof ServerError) {
                console.log('## problem getting player "' + playerId + '"');
                return of(false);
              } else {
                const player = myData as TaggedString;
                if (player.value.length > 0) {
                  this.tcs.addPlayer(playerId, player.value);
                  return of(true);
                } else {
                  console.log('## size of player "' + playerId + '" = 0');
                  return of(false);
                }
              }
            }));
    }
  }

  private incrementProgressValueBy1() {
    this.loadedUnitCount += 1;
    this.progressValue = this.loadedUnitCount * 100 / this.lastUnitSequenceId;
  }

  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  // private: read unitdata
  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
  private loadUnitOk (myUnit: UnitDef, sequenceId: number): Observable<boolean> {
    myUnit.setCanEnter('n', 'Fehler beim Laden');
    return this.bs.getUnitData(this.mds.getBookletDbId(), myUnit.id)
      .pipe(
        switchMap(myData => {
          if (myData instanceof ServerError) {
            const e = myData as ServerError;
            console.log('error getting unit "' + myUnit.id + '": ' + e.code.toString() + ' - ' + e.labelNice);
            return of(false);
          } else {
            const myUnitData = myData as UnitData;
            if (myUnitData.restorepoint) {
              this.tcs.newUnitRestorePoint(myUnit.id, sequenceId, JSON.parse(myUnitData.restorepoint), false);
            }
            let playerId = null;
            let definitionRef = '';

            try {
              const oParser = new DOMParser();
              const oDOM = oParser.parseFromString(myUnitData.xml, 'text/xml');

              if (oDOM.documentElement.nodeName === 'Unit') {
                const defElements = oDOM.documentElement.getElementsByTagName('Definition');

                if (defElements.length > 0) {
                  const defElement = defElements[0];
                  this.tcs.addUnitDefinition(sequenceId, defElement.textContent);
                  playerId = defElement.getAttribute('player');
                } else {
                  const defRefElements = oDOM.documentElement.getElementsByTagName('DefinitionRef');

                  if (defRefElements.length > 0) {
                    const defRefElement = defRefElements[0];
                    definitionRef = defRefElement.textContent;
                    // this.tcs.addUnitDefinition(sequenceId, '');
                    playerId = defRefElement.getAttribute('player');
                  }
                }
              }
            } catch (error) {
              console.log('error parsing xml for unit "' + myUnit.id + '": ' + error.toString());
              playerId = null;
              definitionRef = '';
            }
            this.incrementProgressValueBy1();

            if (playerId) {
              myUnit.playerId = playerId;

              return this.loadPlayerOk(playerId).pipe(
                switchMap(ok => {
                  if (ok && definitionRef.length > 0) {
                    const newUnditDef: TaggedString = {
                      tag: sequenceId.toString(),
                      value: definitionRef
                    };
                    this.unitLoadQueue.push(newUnditDef);
                      myUnit.setCanEnter('y', '');
                      return of(true);
                  } else {
                    if (ok) {
                      myUnit.setCanEnter('y', '');
                    }
                    return of(ok);
                  }
                }));
            } else {
              console.log('error getting unit "' + myUnit.id + '": no player');
              return of(false);
            }
          }
        })
      );
  }

  // #####################################################################################
  // #####################################################################################
  ngOnInit() {
    this.router.navigateByUrl('/t');

    this.maxTimerSubscription = this.tcs.maxTimeTimer$.subscribe(maxTimerData => {
      if (maxTimerData.type === MaxTimerDataType.STARTED) {
        this.snackBar.open(this.cts.getCustomText('booklet_msgTimerStarted') + maxTimerData.timeLeftMinString, '', {duration: 3000});
        this.timerValue = maxTimerData;
      } else if (maxTimerData.type === MaxTimerDataType.ENDED) {
        this.snackBar.open(this.cts.getCustomText('booklet_msgTimeOver'), '', {duration: 3000});
        this.tcs.rootTestlet.setTimeLeftNull(maxTimerData.testletId);
        this.tcs.LastMaxTimerState[maxTimerData.testletId] = 0;
        this.tcs.setBookletState(LastStateKey.MAXTIMELEFT, JSON.stringify(this.tcs.LastMaxTimerState));
        this.timerRunning = false;
        this.timerValue = null;
        if (this.tcs.mode !== 'review') {
          this.tcs.setUnitNavigationRequest('#next');
        }
      } else if (maxTimerData.type === MaxTimerDataType.CANCELLED) {
        this.snackBar.open(this.cts.getCustomText('booklet_msgTimerCancelled'), '', {duration: 3000});
        this.tcs.rootTestlet.setTimeLeftNull(maxTimerData.testletId);
        this.tcs.LastMaxTimerState[maxTimerData.testletId] = 0;
        this.tcs.setBookletState(LastStateKey.MAXTIMELEFT, JSON.stringify(this.tcs.LastMaxTimerState));
        this.timerValue = null;
      } else {
        this.timerValue = maxTimerData;
        if ((maxTimerData.timeLeftSeconds % 15) === 0) {
          this.tcs.LastMaxTimerState[maxTimerData.testletId] = Math.round(maxTimerData.timeLeftSeconds / 60);
          this.tcs.setBookletState(LastStateKey.MAXTIMELEFT, JSON.stringify(this.tcs.LastMaxTimerState));
        }
        if ((maxTimerData.timeLeftSeconds / 60) === 5) {
          this.snackBar.open(this.cts.getCustomText('booklet_msgSoonTimeOver5Minutes'), '', {duration: 3000});
        } else if ((maxTimerData.timeLeftSeconds / 60) === 1) {
          this.snackBar.open(this.cts.getCustomText('booklet_msgSoonTimeOver1Minute'), '', {duration: 3000});
        }
      }
    });

    // ==========================================================
    // navigation between units and end booklet
    this.navigationRequestSubsription = this.tcs.navigationRequest$.subscribe((navString: string) => {
      if (this.tcs.rootTestlet === null) {
        this.snackBar.open('Kein Testheft verfügbar.', '', {duration: 3000});
      } else {
        if (!navString) {
          navString = '#next';
        }
        switch (navString) {
          case '#next':
            if (this.tcs.rootTestlet) {
              let startWith = this.tcs.currentUnitSequenceId;
              if (startWith < this.tcs.minUnitSequenceId) {
                startWith = this.tcs.minUnitSequenceId - 1;
              }
              const nextUnitSequenceId = this.tcs.rootTestlet.getNextUnlockedUnitSequenceId(startWith);
              if (nextUnitSequenceId > 0) {
                this.router.navigateByUrl('/t/u/' + (nextUnitSequenceId).toString());
              }
            }
            break;
          case '#previous':
            if (this.tcs.rootTestlet) {
              this.router.navigateByUrl('/t/u/' + (this.tcs.currentUnitSequenceId - 1).toString());
            }
            break;
          case '#first':
            if (this.tcs.rootTestlet) {
              this.router.navigateByUrl('/t/u/' + this.tcs.minUnitSequenceId.toString());
            }
            break;
          case '#last':
            if (this.tcs.rootTestlet) {
              this.router.navigateByUrl('/t/u/' + this.tcs.maxUnitSequenceId.toString());
            }
            break;
          case '#end':
            this.mds.endBooklet();
            break;

          default:
            if (this.tcs.rootTestlet) {
              this.router.navigateByUrl('/t/u/' + navString);
            }
            break;
        }
      }
    });


    // ==========================================================
    // loading booklet data and all unit content
    // navigation to first unit
    this.loginDataSubscription = this.mds.loginData$.subscribe(loginData => {
      this.tcs.resetDataStore();
      if ((loginData.persontoken.length > 0) && (loginData.booklet > 0)) {
        const envData = new EnvironmentData(this.appVersion);
        this.tcs.addBookletLog(LogEntryKey.BOOKLETLOADSTART, JSON.stringify(envData));

        this.tcs.mode = loginData.mode;
        this.tcs.loginname = loginData.loginname;

        this.tcs.dataLoading = true;
        this.bs.getBookletData(this.mds.getBookletDbId()).subscribe(myData => {
          if (myData instanceof ServerError) {
            const e = myData as ServerError;
            this.mds.globalErrorMsg$.next(e);
            this.mds.setCustomtextsFromDefList(appconfig.customtextsBooklet);
            this.tcs.dataLoading = false;
          } else {
            const bookletData = myData as BookletData;
            console.log('#2');

            if (bookletData.locked) {
              console.log('loading failed');
              this.mds.globalErrorMsg$.next(new ServerError(0, 'Das Testheft ist für die Bearbeitung gesperrt.', ''));
              this.tcs.resetDataStore();
            } else {
              let navTarget = 1;
              if (bookletData.laststate !== null) {
                if (bookletData.laststate.hasOwnProperty(LastStateKey.LASTUNIT)) {
                  const navTargetTemp = Number(bookletData.laststate[LastStateKey.LASTUNIT]);
                  if (!isNaN(navTargetTemp)) {
                    navTarget = navTargetTemp;
                  }
                }
                if (bookletData.laststate.hasOwnProperty(LastStateKey.MAXTIMELEFT) && (loginData.mode === 'hot')) {
                  this.tcs.LastMaxTimerState = JSON.parse(bookletData.laststate[LastStateKey.MAXTIMELEFT]);
                }
              }

              this.tcs.rootTestlet = this.getBookletFromXml(bookletData.xml);

              if (this.tcs.rootTestlet === null) {
                console.log('rootTestlet = null');
                this.mds.globalErrorMsg$.next(new ServerError(0, 'Error Parsing Booklet Xml', ''));
                this.tcs.dataLoading = false;
              } else {
                this.mds.globalErrorMsg$.next(null);
                this.tcs.maxUnitSequenceId = this.lastUnitSequenceId - 1;

                this.showProgress = true;
                this.loadedUnitCount = 0;
                const sequArray = [];
                for (let i = 1; i < this.tcs.maxUnitSequenceId + 1; i++) {
                  sequArray.push(i);
                }
                this.unitLoadQueueSubscription1 = from(sequArray).pipe(
                  concatMap(uSequ => {
                    const ud = this.tcs.rootTestlet.getUnitAt(uSequ);
                    return this.loadUnitOk(ud.unitDef, uSequ);
                  })
                ).subscribe(ok => {
                      if (!ok) {
                          console.log('unit load problem from loadUnitOk');
                      }
                    },
                    err => console.error('unit load error from loadUnitOk: ' + err),
                    () => {

                      // =====================
                      this.tcs.bookletDbId = loginData.booklet;
                      this.tcs.rootTestlet.lockUnitsIfTimeLeftNull();
                      this.tcs.updateMinMaxUnitSequenceId(navTarget);
                      this.loadedUnitCount = 0;

                      // =====================
                      this.unitLoadQueueSubscription2 = from(this.unitLoadQueue).pipe(
                        concatMap(queueEntry => {
                          const unitSequ = Number(queueEntry.tag);
                          if (!this.tcs.lazyloading) {
                            this.incrementProgressValueBy1();
                          }
                          // avoid to load unit def if not necessary
                          if (unitSequ < this.tcs.minUnitSequenceId) {
                            return of({tag: unitSequ.toString(), value: ''});
                          } else {
                            return this.bs.getResource(queueEntry.tag, queueEntry.value);
                          }
                        })
                      ).subscribe(
                        def => {
                          if (def instanceof ServerError) {
                            console.log('getting unit data failed ' + def.labelNice + '/' + def.labelSystem);
                          } else {
                            const udef = def as TaggedString;
                            this.tcs.addUnitDefinition(Number(udef.tag), udef.value);
                          }
                        },
                        err => console.error('unit load error: ' + err),
                        () => { // complete
                          this.tcs.addBookletLog(LogEntryKey.BOOKLETLOADCOMPLETE);
                          this.tcs.bookletLoadComplete = true;
                          if (!this.tcs.lazyloading) {
                              this.showProgress = false;
                              this.tcs.dataLoading = false;
                              this.tcs.setUnitNavigationRequest(navTarget.toString());
                          }
                        }
                      );

                      if (this.tcs.lazyloading) {
                        this.showProgress = false;
                        this.tcs.dataLoading = false;
                        this.tcs.setUnitNavigationRequest(navTarget.toString());
                      }

                    } // complete
                );
              }
            }
          }
        });
      } else {
        this.router.navigateByUrl('/');
      }
    });
  }


  // #####################################################################################
  showReviewDialog() {
    if (this.tcs.rootTestlet === null) {
      this.snackBar.open('Kein Testheft verfügbar.', '', {duration: 3000});
    } else {
      const dialogRef = this.reviewDialog.open(ReviewDialogComponent, {
        width: '700px',
        data: {
          loginname: this.tcs.loginname,
          bookletname: this.tcs.rootTestlet.title,
          unitTitle: this.tcs.currentUnitTitle,
          unitDbKey: this.tcs.currentUnitDbKey
        }
      });

      dialogRef.afterClosed().subscribe(result => {
        if (typeof result !== 'undefined') {
          if (result !== false) {
            const targetSelection = (<FormGroup>result).get('target').value;
            if (targetSelection === 'u') {
              this.bs.saveUnitReview(
                  this.mds.getBookletDbId(),
                  this.tcs.currentUnitDbKey,
                  (<FormGroup>result).get('priority').value,
                  dialogRef.componentInstance.getCategories(),
                  (<FormGroup>result).get('entry').value
                ).subscribe(myData => {
                  if (myData instanceof ServerError) {
                    this.snackBar.open('Konnte Kommentar nicht speichern (' +
                      myData.code.toString() + ': ' + myData.labelNice, '', {duration: 3000});
                  } else {
                    this.snackBar.open('Kommentar gespeichert', '', {duration: 1000});
                  }
                });
            } else {
              this.bs.saveBookletReview(
                this.mds.getBookletDbId(),
                (<FormGroup>result).get('priority').value,
                dialogRef.componentInstance.getCategories(),
                (<FormGroup>result).get('entry').value
              ).subscribe(myData => {
                if (myData instanceof ServerError) {
                  this.snackBar.open('Konnte Kommentar nicht speichern (' + myData.code.toString()
                    + ': ' + myData.labelNice, '', {duration: 3000});
                } else {
                  this.snackBar.open('Kommentar gespeichert', '', {duration: 1000});
                }
              });
            }
          }
        }
      });
    }
  }

  // #####################################################################################
  gotoUnit(newSequenceId: number) {
    this.tcs.setUnitNavigationRequest(newSequenceId.toString());
  }

  // % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %
  ngOnDestroy() {
    if (this.loginDataSubscription !== null) {
      this.loginDataSubscription.unsubscribe();
    }
    if (this.navigationRequestSubsription !== null) {
      this.navigationRequestSubsription.unsubscribe();
    }
    if (this.maxTimerSubscription !== null) {
      this.maxTimerSubscription.unsubscribe();
    }
    if (this.unitLoadQueueSubscription1 !== null) {
      this.unitLoadQueueSubscription1.unsubscribe();
    }
    if (this.unitLoadQueueSubscription2 !== null) {
      this.unitLoadQueueSubscription2.unsubscribe();
    }
  }
}