/* tslint:disable */
<% if (isIo === 'enabled') { %>
import { AsyncSubject } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { RealTime } from '../../services';
import { createIO } from '../io';
<% } %>
import { Observable } from 'rxjs';
import { map, combineLatest } from 'rxjs/operators';
import { Store } from '@ngrx/store';

import { OrmBase } from '../base';
import { applyFilter, toArray, filterById } from '../filter';

import * as models from '../../models';
import { <%- modelName %>, <%- modelName %>Interface, LoopBackFilter } from '../../models';
import { <%- modelName %>Actions } from '../../actions';

export class Orm<%- modelName %> extends OrmBase<<%- modelName %> | <%- modelName %>Interface> {
  constructor(protected store: Store<<%- modelName %>><% if (isIo === 'enabled') { %>, protected realTime?: RealTime<% } %>) {
    super(store, <%- modelName %>, <%- modelName %>Actions<% if (isIo === 'enabled') { %>, realTime<% } %>);
  }
<%
function deCapitalize(string) {
    return string.charAt(0).toLowerCase() + string.slice(1);
}

model.methods.forEach(function(action) {
  var methodName = action.name.split('.').join('$').replace('prototype$', '').replace(/::/g, '__');

  // all of these methods are extended from base service
  if (methodName.match(/(^create$|^createMany$|^find$|^replaceOrCreate$|^replaceById$|^upsert$|^upsertWithWhere$|^exists$|^findOne$|^findById$|^deleteById$|^updateAttributes$|^patchOrCreate$|^patchAttributes$|^updateAll$|^count$|^createChangeStream$)/)) { return; }

  // Unsupported and out of scope methods
  if (methodName.match(/(^exists$|^__exists|^myRemote$|^stats$|^count$|^__count)/)) { return; }

  if (!model.sharedClass.ctor.settings.sdk.blacklist[methodName]) {
-%>

	<% if (methodName.match(/^__get__\w+$/) && action.accepts.filter((a) => a.arg === 'filter').length) { -%>
public <%- normalizeMethodName(methodName) %>(<%- buildMethodParams(model, methodName, action.accepts) %>, meta?: any): Observable<any[]> {
    <% if (isIo === 'enabled') { %>
    if (meta && meta.io) {
      const destroyStream$: AsyncSubject<any> = new AsyncSubject();

      createIO(filter, this.store, destroyStream$, models[this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model], this.realTime, meta);

      return applyFilter(
      <% if (model.sharedClass.ctor.relations[action.name.split('__').pop()].modelThrough) { -%>
  this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.modelThrough + 's')
          .pipe(
            map(toArray),
            combineLatest(
              this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model + 's'),
              (throughInstances, state) => throughInstances
                .map((inst) => Object.assign({}, state.entities[inst[this.model.getModelDefinition().relations.organizations.keyThrough]], {
                  '<%- methodName.match(/^__get__(\w+)$/)[1] %>': inst
                }))
            ),
      <% } else { %>
        this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model + 's')
          .pipe(
            map(toArray),
            map((state: any[]) => filterById(state, id, '<%- methodName.match(/^__get__(\w+)$/)[1] %>', <%- modelName %>)),
      <% } -%>
      finalize(() => {
              destroyStream$.next(1);
              destroyStream$.complete();
            })
          )
        , filter, this.store, models[this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model]);
    } else {
      if (!meta || !meta.justCache) {
        this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
      }

      return applyFilter(
      <% if (model.sharedClass.ctor.relations[action.name.split('__').pop()].modelThrough) { -%>
  this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.modelThrough + 's')
          .pipe(
            map(toArray),
            combineLatest(
              this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model + 's'),
              (throughInstances, state) => throughInstances
                .map((inst) => Object.assign({}, state.entities[inst[this.model.getModelDefinition().relations.organizations.keyThrough]], {
                  '<%- methodName.match(/^__get__(\w+)$/)[1] %>': inst
                }))
            ),
      <% } else { %>
        this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model + 's')
          .pipe(
            map(toArray),
            map((state: any[]) => filterById(state, id, '<%- methodName.match(/^__get__(\w+)$/)[1] %>', <%- modelName %>))
      <% } -%>
    )
        , filter, this.store, models[this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model]);
    }
    <% } else { %>
    if (!meta || !meta.justCache) {
      this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
    }

    return applyFilter(
      this.store.select<any>(this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model + 's')
        .pipe(
          map(toArray),
          map((state: any[]) => filterById(state, id, '<%- methodName.match(/^__get__(\w+)$/)[1] %>', <%- modelName %>))
        )
      , filter, this.store, models[this.model.getModelDefinition().relations.<%- methodName.match(/^__get__(\w+)$/)[1] %>.model]);
    <% } %>
  }
	<% } else if (methodName.match(/(^__findById)/)) { -%>
public <%- normalizeMethodName(methodName) %>(<%- buildMethodParams(model, methodName, action.accepts) %>, meta?: any): Observable<any> {
    <% if (isIo === 'enabled') { %>
    if (meta && meta.io) {
      const destroyStream$: AsyncSubject<any> = new AsyncSubject();

      createIO({}, this.store, destroyStream$, models[this.model.getModelDefinition().relations.rooms.model], this.realTime, meta);

      return this.store.select<any>(this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(findById)(\w+)s?$/)[2]) %>.model + 's')
        .pipe(
          map((state: any) => state.entities[fk]),
          finalize(() => {
            destroyStream$.next(1);
            destroyStream$.complete();
          })
        );
    } else {
      if (!meta || !meta.justCache) {
        this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
      }

      return this.store.select<any>(this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(findById)(\w+)s?$/)[2]) %>.model + 's')
        .pipe(map((state: any) => state.entities[fk]));
    }
    <% } else { %>
    if (!meta || !meta.justCache) {
      this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
    }

    return this.store.select<any>(this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(findById)(\w+)s?$/)[2]) %>.model + 's')
      .pipe(map((state: any) => state.entities[fk]));
    <% } %>
  }
  <% } else if (methodName.match(/(^__get)/)) { -%>
public <%- normalizeMethodName(methodName) %>(<%- buildMethodParams(model, methodName, action.accepts) %>, meta?: any): Observable<any> {
    <% if (isIo === 'enabled') { %>
    if (meta && meta.io) {
      const destroyStream$: AsyncSubject<any> = new AsyncSubject();

      createIO({}, this.store, destroyStream$, models[this.model.getModelDefinition().relations.rooms.model], this.realTime, meta);

      return this.store.select<any>(this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(^get)(\w+)s?$/)[2]) %>.model + 's')
        .pipe(
          map(toArray),
          map((state: any[]) => state.filter(entity => entity[this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(^get)(\w+)s?$/)[2]) %>.keyTo] === id)[0]),
          finalize(() => {
            destroyStream$.next(1);
            destroyStream$.complete();
          })
        );
    } else {
      if (!meta || !meta.justCache) {
        this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
      }

      return this.store.select<any>(this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(^get)(\w+)s?$/)[2]) %>.model + 's')
        .pipe(
          map(toArray),
          map((state: any[]) => state.filter(entity => entity[this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(^get)(\w+)s?$/)[2]) %>.keyTo] === id)[0]),
        );
    }
    <% } else { %>
    if (!meta || !meta.justCache) {
      this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
    }

    return this.store.select<any>(this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(^get)(\w+)s?$/)[2]) %>.model + 's')
      .pipe(
        map(toArray),
        map((state: any[]) => state.filter(entity => entity[this.model.getModelDefinition().relations.<%- deCapitalize(normalizeMethodName(methodName).match(/^(^get)(\w+)s?$/)[2]) %>.keyTo] === id)[0]),
      );
    <% } %>
  }
  <% } else if (action.accepts.filter(param => !paramIsContext(param) && !paramIsFunction(param)).length < 1) { -%>
public <%- normalizeMethodName(methodName) %>(meta?: any): void {
    this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(meta));
  }
  <% } else { -%>
public <%- normalizeMethodName(methodName) %>(<%- buildMethodParams(model, methodName, action.accepts) %>, meta?: any): void {
    this.store.dispatch(new this.actions.<%- normalizeMethodName(methodName) %>(<%- buildPayloadParamsWithoutTypes(model, methodName, action.accepts) %>, meta));
  }
  <% } -%>
<% } -%>
<% }); // model.methods.foreach -%>
<% if (model.isUser) { -%>

  public signup(credentials: any, meta?: any): void {
    this.store.dispatch(new this.actions.signup(credentials, meta));
  }

<% } -%>
}
