import {
  Component,
  Input,
  Output,
  EventEmitter,
  ChangeDetectionStrategy,
  SimpleChanges,
  ViewChild,
  ElementRef,
  inject,
  signal,
  computed,
  DestroyRef
} from '@angular/core';

import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import {
  MatCheckboxChange,
  MatCheckboxModule
} from '@angular/material/checkbox';
import { MatInputModule } from '@angular/material/input';
import { MatIconModule } from '@angular/material/icon';
import { FormsModule } from '@angular/forms';
import {
  MatSlideToggle,
  MatSlideToggleChange,
  MatSlideToggleModule
} from '@angular/material/slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatSliderModule } from '@angular/material/slider';
import { MatStepperModule } from '@angular/material/stepper';

import { NSEWButtonsComponent } from './nsew-buttons.component';

import { computeDestinationPoint } from 'geolib';

import { AnchorService } from '../anchor.service';
import { AppFacade } from 'src/app/app.facade';
import { SignalKClient } from 'signalk-client-angular';
import { HttpErrorResponse } from '@angular/common/http';
import { Convert, SI_BASE_UNIT, TARGET_UNIT } from 'src/app/lib/convert';

@Component({
  selector: 'anchor-watch',
  imports: [
    MatIconModule,
    MatButtonModule,
    MatCardModule,
    MatCheckboxModule,
    FormsModule,
    MatInputModule,
    MatSlideToggleModule,
    MatSliderModule,
    MatTooltipModule,
    MatStepperModule,
    NSEWButtonsComponent
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: './anchor-watch.component.html',
  styleUrls: ['./anchor-watch.component.css']
})
export class AnchorWatchComponent {
  @Input() radius: number;
  @Input() min = 5;
  @Input() max = 250;
  @Input() raised = true;
  @Input() showSelf = false;
  @Output() closed: EventEmitter<void> = new EventEmitter();

  @ViewChild('slideCtl', { static: true }) slideCtl: ElementRef<MatSlideToggle>;

  protected bgImage: string;
  protected sliderValue: number;
  protected rodeOut = false;
  protected convert = Convert;

  // set controls
  protected useDefaultRadius: boolean;
  protected useSetManual: boolean;
  protected defaultRodeLength = signal<number>(10);
  protected defaultAlarmRadius = signal<number>(10);

  protected disableRaiseDrop: boolean;

  private anchor = inject(AnchorService);
  protected app = inject(AppFacade);
  private signalk = inject(SignalKClient);
  private destroyRef = inject(DestroyRef);

  protected radiusValue = signal<number>(0); // incoming alarm radius
  protected formattedRadiusValue = computed(() => {
    if (!Number.isFinite(this.radiusValue()) || this.raised) {
      return '--';
    } else {
      return `${Math.round(this.radiusValue())}${Convert.getSymbol(this.app.config.units.length)}`;
    }
  });
  protected displayRadius = signal<string>('--');

  constructor() {}

  ngOnInit() {
    this.useDefaultRadius = this.app.config.anchor.setRadius;
    this.useSetManual = this.app.config.anchor.manualSet;
    this.defaultRodeLength.update(() => {
      return Math.round(this.transformValue(this.app.config.anchor.rodeLength));
    });
    this.defaultAlarmRadius.update(() => {
      return Math.round(this.transformValue(this.app.config.anchor.radius));
    });
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.radius) {
      if (changes.radius.previousValue === -1) {
        this.sliderValue = Math.round(changes.radius.currentValue);
        this.max = this.sliderValue + 100;
      } else if (
        changes.radius.firstChange &&
        changes.radius.currentValue !== -1
      ) {
        this.sliderValue = Math.round(changes.radius.currentValue);
        this.max = this.sliderValue + 100;
      }
      this.radiusValue.update(() =>
        this.transformValue(changes.radius.currentValue)
      );
      this.displayRadius.update(() =>
        this.formatTransformedValue(changes.radius.currentValue)
      );
    }

    this.bgImage = `url('${
      this.raised
        ? './assets/img/anchor-radius-raised.png'
        : './assets/img/anchor-radius.png'
    }')`;
    this.rodeOut = !this.raised && this.radius !== -1;
    this.disableRaiseDrop =
      !this.showSelf || (this.raised && this.useSetManual);
  }

  /**
   * Convert the formatted value based on config.units.length
   * @param value to convert
   * @returns Transformed value
   */
  transformValue(value: number): number {
    return Convert.transform(
      value,
      'm',
      this.app.config.units.length as TARGET_UNIT
    );
  }

  /**
   * Convert and format the value for display
   * @param value Value to display
   * @returns Formatted string of transformed for display
   */
  formatTransformedValue(value: number): string {
    return `${Math.round(this.transformValue(value))} ${Convert.getSymbol(this.app.config.units.length)}`;
  }

  /** Format slider thumb value for display */
  get formatSliderLabel() {
    return (value: number): string => {
      return this.formatTransformedValue(value);
    };
  }

  onDefaultRadiusChecked(e: MatCheckboxChange) {
    this.useDefaultRadius = e.checked;
    this.app.config.anchor.setRadius = e.checked;
    if (!e.checked) {
      this.defaultAlarmRadius.update(() => {
        return Math.round(this.transformValue(this.app.config.anchor.radius));
      });
    }
    this.app.saveConfig();
  }

  onDefaultRadiusChange(e: any) {
    this.app.config.anchor.radius = Math.round(
      Convert.transform(
        e,
        this.app.config.units.length as SI_BASE_UNIT,
        'm' as TARGET_UNIT
      )
    );
  }

  onSetManualCheck(e: MatCheckboxChange) {
    this.useSetManual = e.checked;
    this.app.config.anchor.manualSet = e.checked;
    if (!e.checked) {
      this.defaultRodeLength.update(() => {
        return Math.round(
          this.transformValue(this.app.config.anchor.rodeLength)
        );
      });
    }
    this.disableRaiseDrop = this.raised && this.useSetManual;
    this.app.saveConfig();
  }

  stepSetRode() {
    this.setRadius();
  }

  /**
   * @description Set the anchor alarm max radius.
   * @param value Alarm radius in meters
   */
  setRadius(value?: number) {
    this.rodeOut = true;
    this.displayRadius.update(() => this.formatTransformedValue(value));
    if (!this.raised) {
      this.signalk
        .post(
          '/plugins/anchoralarm/setRadius',
          typeof value === 'number' ? { radius: value } : {}
        )
        .pipe(takeUntilDestroyed(this.destroyRef))
        .subscribe({
          next: () => {
            this.app.config.anchor.radius = value;
            this.app.saveConfig();
          },
          error: (err: HttpErrorResponse) => {
            this.app.parseHttpErrorResponse(err);
          }
        });
    }
  }

  /**
   * @description Set anchor position using the rode length
   */
  setManualAnchor() {
    if (typeof this.defaultRodeLength() !== 'number') {
      this.app.showAlert('Error', 'Rode length value is not a number!');
      return;
    }
    this.app.config.anchor.rodeLength = Math.round(
      Convert.transform(
        this.defaultRodeLength(),
        this.app.config.units.length as SI_BASE_UNIT,
        'm' as TARGET_UNIT
      )
    );
    this.signalk
      .post('/plugins/anchoralarm/setManualAnchor', {
        rodeLength: this.app.config.anchor.rodeLength
      })
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: () => {
          this.app.saveConfig();
        },
        error: (err: HttpErrorResponse) => {
          this.app.parseHttpErrorResponse(err);
        }
      });
  }

  /**
   * @description Handle raise / drop slide toggle change
   * @param e Slide change event
   */
  dropRaiseAnchor(e: MatSlideToggleChange) {
    if (e.checked) {
      this.dropAnchor(
        this.useDefaultRadius ? this.app.config.anchor.radius : undefined
      );
    } else {
      this.raiseAnchor();
    }
  }

  /**
   * @description Drop the Anchor
   * @param radius Alarm radius to set
   */
  dropAnchor(radius?: number) {
    this.app.config.anchor.radius = radius;
    this.anchor.setRaisedSignal(false);
    this.signalk
      .post(
        '/plugins/anchoralarm/dropAnchor',
        typeof radius === 'number' ? { radius: radius } : {}
      )
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: () => {
          this.app.saveConfig();
        },
        error: (err: HttpErrorResponse) => {
          this.anchor.setRaisedSignal(true);
          this.app.parseHttpErrorResponse(err);
        }
      });
  }

  /**
   * @description Raise the Anchor
   */
  raiseAnchor() {
    this.signalk
      .post('/plugins/anchoralarm/raiseAnchor', {})
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: () => undefined,
        error: (err: HttpErrorResponse) => {
          this.app.parseHttpErrorResponse(err);
        }
      });
  }

  /**
   * @description Shift anchor position n,s,e,w
   * @param direction (degrees) 0 | 90 | 180 | 270
   */
  shiftAnchor(direction: number) {
    const inc = 1;
    const position = computeDestinationPoint(
      this.anchor.position(),
      inc,
      direction
    );
    this.anchor
      .setAnchorPosition([position.longitude, position.latitude])
      .catch((err: HttpErrorResponse) => {
        this.app.parseHttpErrorResponse(err);
      });
  }

  close() {
    this.closed.emit();
  }
}
