/** Settings Service
 * ************************************/
import { Injectable } from '@angular/core';

import { AppFacade } from 'src/app/app.facade';
import { SignalKClient } from 'signalk-client-angular';
import {
  FBAppData,
  IAppConfig,
  MapPanBehavior,
  Position,
  WindIndicator
} from 'src/app/types';
import { Observable, Subject } from 'rxjs';

interface SKAppsList {
  author: string;
  description: string;
  license: string;
  location: string;
  _location: string; //npm linked app
  name: string;
  version: string;
}

interface ResourceTypeList {
  [key: string]: {
    $source: string;
    description: string;
  };
}

/** Settings Option Choices */
export class SettingsOptions {
  private symDegree = String.fromCharCode(186);

  availableUnits = {
    distance: new Map([
      ['kilometer', 'Kilometres'],
      ['naut-mile', 'Nautical Miles']
    ]),
    depth: new Map([
      ['m', 'metres'],
      ['foot', 'feet'],
      ['fathom', 'fathom']
    ]),
    length: new Map([
      ['m', 'metres'],
      ['foot', 'feet']
    ]),
    speed: new Map([
      ['kn', 'knots'],
      ['m/s', 'm/sec'],
      ['km/h', 'km/h'],
      ['mph', 'mph']
    ]),
    temperature: new Map([
      ['C', 'Celsius'],
      ['F', 'Fahrenheit']
    ])
  };

  preferredValues = {
    heading: [
      ['navigation.headingTrue', 'True'],
      ['navigation.headingMagnetic', 'Magnetic']
    ],
    wind: new Map([
      [false, 'Wind True'],
      [true, 'Wind Apparent']
    ]),
    coords: [
      ['XY', '-128.12345'],
      ['SHDd', `W-128.12345${this.symDegree}`],
      ['DMdH', `128${this.symDegree} 15.34567' W`],
      ['HDd', `W 128.12345${this.symDegree}`],
      ['HDMS', `W 128${this.symDegree}15'46"123`],
      ['DHMS', `128W15'46"123`]
    ]
  };

  buttons = {
    fab: new Map([
      ['wpt', 'Waypoint at vessel'],
      ['pob', 'Person Overboard'],
      ['autopilot', 'Autopilot Console'],
      ['radar', 'Radar Overlay']
    ])
  };

  map = {
    panBehavior: new Map<MapPanBehavior, string>([
      ['offset', 'Set Follow Offset'],
      ['exit', 'Exit Follow Mode'],
      ['none', 'Do nothing']
    ]),
    s57: {
      graphicsStyle: new Map([
        ['Simplified', 'Simplified'],
        ['Paper', 'Paper chart']
      ]),
      boundaries: new Map([
        ['Symbolized', 'Symbolized'],
        ['Plain', 'Plain']
      ]),
      colors: [2, 4],
      shallowDepth: 2,
      safetyDepth: 3,
      deepDepth: 6,
      colorTables: new Map([
        [0, 'Day Bright'],
        [1, 'Day Black Background'],
        [2, 'Day White Background']
        /*[3, 'Dusk'],
        [4, 'Night']*/
      ]),
      otherLayers: new Map([
        ['SOUNDG', 'Depth Soundings'],
        ['DEPCNT', 'Depth Contours'],
        ['OBSTRN', 'Obstructions'],
        ['UWTROC', 'Underwater Rocks'],
        ['WRECKS', 'Wrecks'],
        ['CBLSUB', 'Submarine Cable'],
        ['PIPSOL', 'Submarine Pipelines'],
        ['BERTHS', 'Berths'],
        ['MORFAC', 'Mooring Facilities'],
        ['LNDMRK', 'Landmarks'],
        ['DISMAR', 'Distance Marks'],
        ['CURENT', 'Currents'],
        ['MAGVAR', 'Magnetic Variation'],
        ['RIVERS', 'Rivers'],
        ['LAKARE', 'Lakes']
      ])
    }
  };

  trailIntervals = {
    highResolution: new Map([
      ['1s', '1 sec'],
      ['5s', '5 secs'],
      ['10s', '10 secs'],
      ['30s', '30 secs'],
      ['1m', '1 min']
    ]),
    mediumResolution: new Map([
      ['10s', '10 secs'],
      ['30s', '30 secs'],
      ['1m', '1 min'],
      ['2m', '2 mins'],
      ['5m', '5 mins'],
      ['10m', '10 mins'],
      ['15m', '15 mins'],
      ['30m', '30 mins']
    ])
  };

  alarm = {
    smoothing: new Map([
      [5000, '5 secs'],
      [10000, '10 secs'],
      [20000, '20 secs'],
      [30000, '30 secs']
    ])
  };

  darkMode = new Map([
    [0, 'Use OS setting'],
    [1, 'Use Signal K Mode'],
    [-1, 'On']
  ]);

  windIndicator = new Map<WindIndicator, string>([
    ['barb', 'Wind barb'],
    ['arrow', 'Arrow']
  ]);

  vessel = {
    selfIconScale: new Map([
      [0.75, 'S'],
      [0.9, 'M'],
      [1.15, 'L'],
      [1.4, 'XL']
    ]),
    headingLine: new Map([
      [-1, 'Default'],
      [1, '1 nmi (2km)'],
      [2, '2 nmi (4km)'],
      [5, '5 nmi (10km)'],
      [10, '10 nmi (20km)'],
      [15, '15 nmi (30km)'],
      [20, '20 nmi (40km)'],
      [50, '50 nmi (90km)'],
      [100, '100 nmi (180km)']
    ]),
    cogLine: new Map([
      [0, 'None'],
      [5, '5 min'],
      [10, '10 min'],
      [30, '30 min'],
      [60, '60 min'],
      [2 * 60, '2 hrs'],
      [24 * 60, '24 hrs']
    ]),
    aisTimeouts: new Map([
      [60000, '1 min'],
      [120000, '2 min'],
      [180000, '3 min'],
      [360000, '6 min'],
      [540000, '9 min'],
      [15 * 60000, '15 min'],
      [30 * 60000, '30 min'],
      [60 * 60000, '1 hr'],
      [24 * 60 * 60000, '24 hrs']
    ]),
    aisMaxRadius: new Map([
      [0, 'None'],
      [10000, '5 nmi (10km)'],
      [20000, '10 nmi (20km)'],
      [30000, '15 nmi (30km)'],
      [40000, '20 nmi (40km)'],
      [50000, '25 nmi (50km)'],
      [75000, '40 nmi (75km)'],
      [100000, '55 nmi (100km)']
    ]),
    aisCogLine: new Map([
      [0, 'Off'],
      [10, '10 min'],
      [30, '30 min'],
      [60, '60 min']
    ])
  };

  course = {
    nextPointTrigger: ['perpendicularPassed', 'arrivalCircleEntered'],
    nextPointDelay: this.alarm.smoothing
  };

  resources = {
    noteRadius: [5, 10, 20, 50, 100, 150, 200, 500],
    resSetRadius: [0, 5, 10, 20, 50, 100, 150, 200, 500]
  };

  zoomLevels = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17];

  rangeCircles = {
    count: [4, 5, 6, 7, 8, 9, 10, 15, 20],
    zoomLevels: [2, 3, 4, 5, 6, 7].concat(this.zoomLevels)
  };
}

@Injectable({ providedIn: 'root' })
export class SettingsFacade {
  applicationList = []; // installed webapp list
  favouritesList = []; // favourite webapp selections
  resourcePathList = []; // resource layer list
  fixedPosition: Position = [0, 0];

  settings!: IAppConfig;
  data!: FBAppData;

  // Observables
  protected changeEvent: Subject<string[]>;
  public change$: Observable<string[]>;

  constructor(
    private app: AppFacade,
    public signalk: SignalKClient
  ) {
    // ** initialise events
    this.changeEvent = new Subject<string[]>();
    this.change$ = this.changeEvent.asObservable();

    this.data = this.app.data;
    this.settings = this.app.config;

    this.fixedPosition =
      this.settings.vessels.fixedPosition.slice() as Position;
  }

  /** Notify of setting attribute change */
  emitChangeEvent(value: string) {
    const v = value ? [value] : [];
    this.changeEvent.next(v);
  }

  /** Delete auth token */
  clearToken() {
    this.app.persistToken(null);
  }

  /** Refresh dynamic data from sources */
  refresh() {
    this.settings = this.app.config;
    this.fixedPosition =
      this.settings.vessels.fixedPosition.slice() as Position;
    this.getApps();
    this.getResourcePaths();
    this.app.fetchUnitPrefsFromSKServer();
  }

  /** Populate list of available non-standard resource paths */
  private getResourcePaths() {
    this.signalk.api.get(this.app.skApiVersion, 'resources').subscribe(
      (r: ResourceTypeList) => {
        this.resourcePathList = Object.keys(r)
          .filter((i: string) => {
            return !this.app.IGNORE_RESOURCES.includes(i);
          })
          .sort();
      },
      () => (this.resourcePathList = [])
    );
  }

  /** Populate applications list */
  private getApps() {
    // apps list - default an entry to stored config value
    if (this.app.config.display.plugins.instruments) {
      this.applicationList.push(this.app.config.display.plugins.instruments);
    }

    this.signalk.apps.list().subscribe(
      (a: Array<SKAppsList>) => {
        this.applicationList = a
          .map((i) => {
            if (i.name === '@signalk/freeboard-sk') {
              return null;
            }
            if (!i._location && !i.location) {
              // npm linked app
              return {
                name: i.name,
                description: i.description,
                url: `/${i.name}`
              };
            }
            if (typeof i._location !== 'undefined') {
              // legacywebapps list
              const x = i._location.indexOf('/signalk-server/');
              return {
                name: i.name,
                description: i.description,
                url: x === -1 ? i._location : i._location.slice(15)
              };
            } else if (typeof i.location !== 'undefined') {
              return {
                name: i.name,
                description: i.description,
                url: i.location
              };
            }
          })
          .filter((e) => {
            return e;
          });

        this.applicationList.unshift({
          name: 'None',
          description: '',
          url: null
        });

        this.buildFavouritesList();
      },
      () => {
        this.app.debug('ERROR retrieving AppList!');
      }
    );
  }

  /** Favourited plugins / apps */
  buildFavouritesList() {
    this.favouritesList = this.applicationList.slice(1);
    const i = this.app.config.display.plugins.favourites.indexOf(
      this.app.config.display.plugins.instruments
    );
  }

  /** Apply / persist settings */
  applySettings() {
    this.app.debug('Saving Settings..');
    if (!this.app.config.vessels.trail) {
      this.app.config.vessels.trailFromServer = false;
    }
    if (
      typeof this.fixedPosition[0] === 'number' &&
      typeof this.fixedPosition[1] === 'number'
    ) {
      this.settings.vessels.fixedPosition =
        this.fixedPosition.slice() as Position;
      if (this.settings.vessels.fixedLocationMode) {
        this.app.data.vessels.self.position =
          this.fixedPosition.slice() as Position;
      }
    }
    this.app.saveConfig();
  }
}
