import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { DatePipe } from '@angular/common';
import { forkJoin, Observable } from "rxjs";
import { map } from "rxjs/operators";

import { JumpResp, JumpReq, Jump } from "../models/jump";
import { JumpListResp, JumpList } from "../models/jumpList";
import { GearResp } from "../models/gear";
import { DropZoneResp } from "../models/dropzone";
import { AircraftResp } from "../models/aircraft";
import { JumpTypeResp } from "../models/jumpType";

import { DateService } from "./date.service";
import { BaseService } from "./base.service";
import { DropzoneService } from "./dropzone.service";
import { AircraftService } from "./aircraft.service";
import { JumpTypeService } from "./jump-type.service";
import { GearService } from "./gear.service";

@Injectable()
export class JumpService extends BaseService {
  private callsToAdd: Array<Observable<any>>;

  constructor(private http: HttpClient,
              private dateService: DateService,
              private datePipe: DatePipe,
              private dropzoneService: DropzoneService,
              private aircraftService: AircraftService,
              private jumpTypeService: JumpTypeService,
              private gearService: GearService) {
    super();
  }

  public getListOfJumps(): Observable<Array<Jump>> {
    return this.http.get<Array<JumpResp>>(`${this.apiUrl}/Jump`, { headers: this.headers })
                    .pipe(map((response) => {
                      return this.mapWithDataInCache(response);
                    }));
  }

  public getJumps(beginIndex: number, endIndex: number): Observable<JumpList> {
    return this.http.get<JumpListResp>(`${this.apiUrl}/Jump/${beginIndex}/${endIndex}`, { headers: this.headers })
                    .pipe(map(response => {
                      let result: JumpList = {
                        rows: this.mapWithDataInCache(response.rows),
                        count: response.count
                      };

                      return new JumpList(result);
                    }));
  }

  public addListOfJump(selectedJumpType: number,
                       selectedAircraft: number,
                       selectedDz: number,
                       selectedRig: number,
                       withCutaway: boolean,
                       beginDate: Date,
                       endDate: Date,
                       defaultExitAltitude: number,
                       defaultDeployAltitude: number,
                       countOfJumps: number,
                       notes: string,
                       isSpecial: boolean): Observable<any[]> {
    this.callsToAdd = new Array<Observable<any>>();
    const diffInDays = this.dateService.diffBetweenDates(beginDate, endDate) + 1;
    const countOfJumpsPerDay = Math.trunc(countOfJumps / diffInDays);

    for (let i = 1; beginDate.getTime() < endDate.getTime(); i++) {
      this.addJumps(selectedJumpType,
                    selectedAircraft,
                    selectedDz,
                    selectedRig,
                    withCutaway,
                    beginDate,
                    defaultExitAltitude,
                    defaultDeployAltitude,
                    countOfJumpsPerDay,
                    notes,
                    isSpecial);

      beginDate = this.dateService.addDays(beginDate, 1);
    }

    const restfJumps = countOfJumps - countOfJumpsPerDay * (diffInDays - 1);

    this.addJumps(selectedJumpType,
                  selectedAircraft,
                  selectedDz,
                  selectedRig,
                  withCutaway,
                  beginDate,
                  defaultExitAltitude,
                  defaultDeployAltitude,
                  restfJumps,
                  notes,
                  isSpecial);

    return forkJoin(this.callsToAdd);
  }

  public deleteJump(item: Jump) {
    this.http.delete(`${this.apiUrl}/Jump/${item.id}`, { headers: this.headers }).subscribe();
  }

  public updateJump(id: number,
                    isSpecial: boolean,
                    withCutaway: boolean,
                    notes: string) {
    const jumpData = {
      id: id,
      isSpecial: isSpecial,
      withCutaway: withCutaway,
      notes: notes
    };
    const bodyUpdatedJump = new JumpReq(jumpData);

    return this.http.put(`${this.apiUrl}/Jump/${id}`,
      bodyUpdatedJump,
      { headers: this.headers, });
  }

  private addJumps(selectedJumpType: number,
                   selectedAircraft: number,
                   selectedDz: number,
                   selectedRig: number,
                   withCutaway: boolean,
                   jumpDate: Date,
                   defaultExitAltitude: number,
                   defaultDeployAltitude: number,
                   countOfJumps: number,
                   notes: string,
                   isSpecial: boolean) {
    for (let i = 0; i < countOfJumps; i++) {
      const bodyNewjump: JumpReq = {
        jumpTypeId: selectedJumpType,
        aircraftId: selectedAircraft,
        dropZoneId: selectedDz,
        withCutaway: withCutaway,
        exitAltitude: defaultExitAltitude,
        deployAltitude: defaultDeployAltitude,
        gearId: selectedRig,
        notes: notes,
        id: 0,
        jumpDate: this.datePipe.transform(jumpDate, "yyyy-MM-dd"),
        isSpecial: isSpecial
      };

      let call = this.http.post(`${this.apiUrl}/Jump`, bodyNewjump, { headers: this.headers });

      this.callsToAdd.push(call);
    }
  }

  private mapWithDataInCache(apiResp: Array<JumpResp>): Array<Jump> {
    let allDropzones: Array<DropZoneResp>;
    this.dropzoneService.getFromCache().subscribe(data => { allDropzones = data; });
    let allAircrafts: Array<AircraftResp>;
    this.aircraftService.getFromCache().subscribe(data => { allAircrafts = data; });
    let allJumpType: Array<JumpTypeResp>;
    this.jumpTypeService.getFromCache().subscribe(data => { allJumpType = data; });
    let allGears: Array<GearResp>;
    this.gearService.getFromCache().subscribe(data => { allGears = data; });

    return apiResp.map((data) => {
      let tmp = new Jump(data);

      tmp.dropZone = allDropzones.find(d => d.id == data.dropZoneId);
      tmp.aircraft = allAircrafts.find(d => d.id == data.aircraftId);
      tmp.jumpType = allJumpType.find(d => d.id == data.jumpTypeId);
      tmp.gear = allGears.find(d => d.id == data.gearId);

      return tmp;
    });
  }
}
