import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {IItemType} from '../models/item.type';
import {IItemLinkType} from '../models/itemlink.type';
import {IItem} from '../models/item';
import {IJsonline} from '../models/json-line';
import {IItemTask} from '../models/itemTask';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {AppConfig} from '../shared/app.config';
import {ItemTypeService} from './itemtype.service';
import {IListItem} from '../models/list.item';

@Injectable({
  providedIn: 'root',
})
export class ItemService {
  constructor(public httpClient: HttpClient, public appConfig: AppConfig,private itemTypeService:ItemTypeService) {
  }

  ApiEndpoint() {
    return this.appConfig.getConfig("apiEndPoint");
  }

  getItemTypes(): Observable<{ [id: string]: IItemType }> {
    return this.httpClient.get<{ [id: string]: IItemType }>(`${this.ApiEndpoint()}/api/v1/itemtypes/`);
  }

  getFeatures(extent: number[], crs: string, searchText?: string, searchTags?:string,startDate?:Date,endDate?:Date,itemType?:string,parentCode?:string,dataFilter?:string,level?:number,indexed?:boolean): Observable<any> {
    let params = new HttpParams();
    params = params.append("crs", crs);
    if (extent) params =params.append("bbox", extent.join(","));
    if (searchText) params = params.append("q", searchText);
    if (searchTags) params = params.append("t", searchTags);
    if (startDate) params = params.append("sd", startDate.toISOString());
    if (endDate) params = params.append("ed", endDate.toISOString());
    if (itemType) {
      params = params.append("it", itemType);
      const extraAttributes = this.itemTypeService.getExtraAttributes(itemType);
      if(extraAttributes) {
        params = params.append("da", extraAttributes);
      }
    }
    if (parentCode) params = params.append("pc", parentCode);
    if (dataFilter) params = params.append("df", dataFilter);
    if (level) params = params.append("lvl", level.toString());
    params = params.append("ind", indexed ?? true);
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/features/`, {params:params});
  }

  getFeature(code:string, crs: string): Observable<any> {
    let params = new HttpParams();
    params = params.append("crs", crs);
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/feature/`, { params: params });
  }

  getItemFeature(code:string, fid:number, crs: string): Observable<any> {
    let params = new HttpParams();
    params = params.append("crs", crs);
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/feature/${fid}`, { params: params });
  }

  getItem(code: string): Observable<IItem> {
    return this.httpClient.get<IItem>(`${this.ApiEndpoint()}/api/v1/items/${code}`);
  }

  getItemLink(itemLink: IItemLinkType): Observable<any> {
    return this.httpClient.post<IItemLinkType>(`${this.ApiEndpoint()}/api/v1/itemlink`,itemLink);
  }

  getItemData(code: string,start?:number,size?:number): Observable<ArrayBuffer> {
    let headers = new HttpHeaders();
    if(start !== undefined && size !== undefined) headers=headers.set("Range",`bytes=${start}-${size-1}`);
    return this.httpClient.get(`${this.ApiEndpoint()}/api/v1/items/${code}/data`,{ headers: headers,responseType: 'arraybuffer' });
  }

  getItemByCodeAndType(code: string, itemType: string): Observable<IItem> {
    return this.httpClient.get<IItem>(`${this.ApiEndpoint()}/api/v1/items/${code}/${itemType}`);
  }

  getItemList(itemType?: string, dataFilter?: any, level?: number, atItemLocationItemCode?: string,
    indexed?: boolean, validToday?: boolean,tags?:string,crs?:string, startDate?: Date, endDate?: Date,
    skip?: number, take?: number, sourceTask?: string, exactMatchStartOrEndDate?: boolean, owner?:string): Observable<IItem[]> {
    let params = new HttpParams();
    if(itemType)  params = params.append("it", itemType);
    if(dataFilter) params = params.append("df", JSON.stringify(dataFilter));
    if(atItemLocationItemCode) params = params.append("ail",atItemLocationItemCode);
    if(indexed) params = params.append("ind",indexed?"true":"false");
    if (level) params = params.append("lvl", level.toFixed());
    if (validToday) params = params.append("vt", validToday ? "true" : "false");
    if (tags) params = params.append("t", tags);
    if (crs) params = params.append("crs", crs);
    if (startDate) params = params.append("sDate", startDate.toISOString());
    if (endDate) params = params.append("eDate", endDate.toISOString());
    if (sourceTask) params = params.append("sourceTask", sourceTask);
    if(skip) params = params.append("skip", skip);
    if(take) params = params.append("take", take);
    if(exactMatchStartOrEndDate !== undefined) params = params.append("exactMatchStartOrEndDate", exactMatchStartOrEndDate);
    if(owner) params = params.append("owner", owner);
    return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/`, { params: params });
  }

  getItemListCount(itemType: string, startDate?: Date, endDate?: Date, sourceTask?: string,
    indexed?: boolean, exactMatchStartOrEndDate?: boolean, owner?:string): Observable<number> {
    let params = new HttpParams();
    params = params.append("it", itemType);
    if (sourceTask) params = params.append("sourceTask", sourceTask);
    if (startDate) params = params.append("sDate", startDate.toISOString());
    if (endDate) params = params.append("eDate", endDate.toISOString());
    if(indexed) params = params.append("ind",indexed?"true":"false");
    if(exactMatchStartOrEndDate !== undefined) params = params.append("exactMatchStartOrEndDate", exactMatchStartOrEndDate);
    if(owner) params = params.append("owner", owner);
    return this.httpClient.get<number>(`${this.ApiEndpoint()}/api/v1/items/count`, { params: params });
  }

  getChildItemList(parentcode: string, itemType?: string, dataFilter?: any, level = 1, deep = true,
                   startDate?: Date, endDate?: Date, skip?: number, take?: number,
                   exactMatchStartOrEndDate?: boolean, owner?:string, indexed?: boolean): Observable<IItem[]> {
    let params = new HttpParams();
    if(itemType != null) {
      params = params.append("it", itemType);
    }
    if (dataFilter != null) {
      params = params.append("df", JSON.stringify(dataFilter));
    }
    params = params.append("lvl", level.toString());
    params = params.append("deep", deep.toString());
    if (startDate) params = params.append("sDate", startDate.toISOString());
    if (endDate) params = params.append("eDate", endDate.toISOString());
    if(exactMatchStartOrEndDate !== undefined) params = params.append("exactMatchStartOrEndDate", exactMatchStartOrEndDate);
    if(owner) params = params.append("owner", owner);

    if(skip) params = params.append("skip", skip);
    if(take) params = params.append("take", take);
    if(indexed) params = params.append("ind",indexed?"true":"false");
    return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children`, { params: params });
  }

  getChildItemListCount(parentcode: string, itemType?: string,dataFilter?: any,
                        startDate?: Date, endDate?: Date, exactMatchStartOrEndDate?: boolean, owner?:string,
                        indexed?: boolean): Observable<number> {
    let params = new HttpParams();
    if(itemType != null) {
      params = params.append("it", itemType);
    }
    if (dataFilter != null) {
      params = params.append("df", JSON.stringify(dataFilter));
    }
    if (startDate) params = params.append("sDate", startDate.toISOString());
    if (endDate) params = params.append("eDate", endDate.toISOString());
    if(exactMatchStartOrEndDate !== undefined) params = params.append("exactMatchStartOrEndDate", exactMatchStartOrEndDate);
    if(owner) params = params.append("owner", owner);
    if(indexed) params = params.append("ind",indexed?"true":"false");
    return this.httpClient.get<number>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children/count`, { params: params });
  }

  getChildItemListByExtent(parentcode: string, itemType: string, extent: number[], crs: string, dataFilter?: any, level = 1): Observable<IItem[]> {
    let params = new HttpParams();
    params = params.append("it", itemType);
    params = params.append("bbox", extent.join(","));
    params = params.append("crs", crs);
    if (dataFilter != null) {
      params = params.append("df", JSON.stringify(dataFilter));
    }
    params = params.append("lvl", level.toString());
    return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children`, { params: params });
  }

  getItemFeatures(code: string, extent: number[], crs: string, layerIndex?:number): Observable<any> {
    let params = new HttpParams();
    params = params.append("crs", crs);
    if(extent != null) {
      params = params.append("bbox", extent.join(","));
    }
    if(layerIndex!=null)
      return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/features/layer/${layerIndex}`, { params: params });
    else
      return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/features`, { params: params });
  }

  postItem(item:IItem): Observable<IItem> {
    return this.httpClient.post<IItem>(`${this.ApiEndpoint()}/api/v1/items`,item);
  }

  putItem(item:IItem): Observable<IItem> {
    return this.httpClient.put<IItem>(`${this.ApiEndpoint()}/api/v1/items/${item.code}`,item);
  }

  putItemFile(item: IItem, jsonObject: any): Observable<IItem> {
    const formData = new FormData();
    const file = new File([JSON.stringify(jsonObject, undefined, '\t')], 'data.dat', {type: 'application/json'});
    formData.append('file', file);
    return this.httpClient.put<any>(`${this.ApiEndpoint()}/api/v1/items/${item.code}/data`, formData);
  }

  deleteItem(code: string): Observable<any> {
    return this.httpClient.delete<any>(`${this.ApiEndpoint()}/api/v1/items/${code}`);
  }

  deleteItems(itemCodes:string[]): Observable<any> {
    return this.httpClient.post<any>(`${this.ApiEndpoint()}/api/v1/items/delete`, itemCodes);
  }

  getTemporalLast(code: string): Observable<IJsonline> {
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/temporal/last`);
  }

  getTemporal(code: string, startDate?: Date, endDate?: Date): Observable<any> {
    let params = new HttpParams();
    if (startDate) params = params.append("sd", startDate.toISOString());
    if (endDate) params = params.append("ed", endDate.toISOString());
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/temporal/`, { params: params });
  }

  postItemTask(item: IItem, task: IItemTask): Observable<IItemTask> {
    return this.httpClient.post<IItemTask>(`${this.ApiEndpoint()}/api/v1/items/${item.code}/tasks`, task);
  }

  getItemTaskList(itemcode: string, unfinishedOnly?: boolean): Observable<IItemTask[]> {
    let params = new HttpParams();
    if (unfinishedOnly) params = params.append("unfinishedOnly", unfinishedOnly.toString());
    return this.httpClient.get<IItemTask[]>(`${this.ApiEndpoint()}/api/v1/items/${itemcode}/tasks`, { params: params });
  }

  getItemListUsingRelationship(itemType: string, relationshipItemType: string, relationshipDataFilter: any): Observable<IItem[]> {
    let params = new HttpParams();
    params = params.append("it", itemType);
    params = params.append("rsit", relationshipItemType);
    params = params.append("rsdf", JSON.stringify(relationshipDataFilter));
    return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/userelationship`, { params: params });
  }

  getLayerValue(itemCode: string, layerIndex:number,x:number,y:number,crs = "EPSG:3857"): Observable<number> {
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${itemCode}/value/layer/${layerIndex}?c=${x},${y}&crs=${crs}`);
  }

  getBreadcrumbs(itemCode: string): Observable<IListItem[]> {
    return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${itemCode}/breadcrumbs`);
  }
}