package com.github.openwebnet.service.impl;

import com.github.niqdev.openwebnet.OpenSession;
import com.github.niqdev.openwebnet.message.Lighting;
import com.github.openwebnet.component.Injector;
import com.github.openwebnet.model.LightModel;
import com.github.openwebnet.repository.LightRepository;
import com.github.openwebnet.service.CommonService;
import com.github.openwebnet.service.LightService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.schedulers.Schedulers;

import static com.github.openwebnet.model.LightModel.Status.OFF;
import static com.github.openwebnet.model.LightModel.Status.ON;

public class LightServiceImpl implements LightService {

    private static final Logger log = LoggerFactory.getLogger(LightService.class);

    @Inject
    LightRepository lightRepository;

    @Inject
    CommonService commonService;

    public LightServiceImpl() {
        Injector.getApplicationComponent().inject(this);
    }

    @Override
    public Observable<String> add(LightModel light) {
        return lightRepository.add(light);
    }

    @Override
    public Observable<Void> update(LightModel light) {
        return lightRepository.update(light);
    }

    @Override
    public Observable<Void> delete(String uuid) {
        return lightRepository.delete(uuid);
    }

    @Override
    public Observable<LightModel> findById(String uuid) {
        return lightRepository.findById(uuid);
    }

    @Override
    public Observable<List<LightModel>> findByEnvironment(Integer id) {
        return lightRepository.findByEnvironment(id);
    }

    @Override
    public Observable<List<LightModel>> findFavourites() {
        return lightRepository.findFavourites();
    }

    private Func2<OpenSession, LightModel, LightModel> handleStatus = (openSession, light) -> {
        Lighting.handleStatus(() -> light.setStatus(ON), () -> light.setStatus(OFF)).call(openSession);
        return light;
    };

    @Override
    public Observable<List<LightModel>> requestByEnvironment(Integer id) {
        return findByEnvironment(id)
            .flatMapIterable(lightModels -> lightModels)
            .flatMap(requestLight(Lighting::requestStatus, handleStatus))
            .collect(ArrayList::new, List::add);
    }

    @Override
    public Observable<List<LightModel>> requestFavourites() {
        return findFavourites()
            .flatMapIterable(lightModels -> lightModels)
            .flatMap(requestLight(Lighting::requestStatus, handleStatus))
            .collect(ArrayList::new, List::add);
    }

    private Func2<OpenSession, LightModel, LightModel> handleResponse(LightModel.Status status) {
        return (openSession, light) -> {
            Lighting.handleResponse(() ->
                light.setStatus(status), () -> light.setStatus(null))
            .call(openSession);
            return light;
        };
    }

    @Override
    public Observable<LightModel> turnOn(LightModel light) {
        return Observable.just(light).flatMap(requestLight(Lighting::requestTurnOn, handleResponse(ON)));
    }

    @Override
    public Observable<LightModel> turnOff(LightModel light) {
        return Observable.just(light).flatMap(requestLight(Lighting::requestTurnOff, handleResponse(OFF)));
    }

    private Func1<LightModel, Observable<LightModel>> requestLight(
        Func1<String, Lighting> request, Func2<OpenSession, LightModel, LightModel> handler) {
        // TODO improvement: group by gateway and for each gateway send all requests together
        return light -> commonService.findClient(light.getGatewayUuid())
            .send(request.call(light.getWhere()))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .map(openSession -> handler.call(openSession, light))
            .onErrorReturn(throwable -> {
                log.warn("light={} | failing request={}", light.getUuid(), request.call(light.getWhere()).getValue());
                // unreadable status
                return light;
            });
    }

}

@@ -2354,32 +2354,38 @@
 Map(requestLight
+Status
 (Lighting::reque
@@ -2655,32 +2655,38 @@
 Map(requestLight
+Status
 (Lighting::reque
@@ -2758,32 +2758,829 @@
 t::add);%0A    %7D%0A%0A
+    // TODO improvement: group by gateway and for each gateway send all requests together%0A    private Func1%3CLightModel, Observable%3CLightModel%3E%3E requestLightStatus(%0A        Func1%3CString, Lighting%3E request, Func2%3COpenSession, LightModel, LightModel%3E handler) %7B%0A%0A        return light -%3E commonService.findClient(light.getGatewayUuid())%0A            .send(request.call(light.getWhere()))%0A            .subscribeOn(Schedulers.io())%0A            .observeOn(AndroidSchedulers.mainThread())%0A            .map(openSession -%3E handler.call(openSession, light))%0A            .onErrorReturn(throwable -%3E %7B%0A                log.warn(%22light=%7B%7D %7C failing request=%7B%7D%22, light.getUuid(), request.call(light.getWhere()).getValue());%0A                // unreadable status%0A                return light;%0A            %7D);%0A    %7D%0A%0A
     private Func
@@ -4345,33 +4345,33 @@
         Func
-1
+2
 %3CString,
  Lighting%3E r
@@ -4350,32 +4350,47 @@
    Func2%3CString,
+ Lighting.Type,
  Lighting%3E reque
@@ -4451,101 +4451,8 @@
 ) %7B%0A
-        // TODO improvement: group by gateway and for each gateway send all requests together
 %0A   
@@ -4560,32 +4560,57 @@
 light.getWhere()
+, light.getLightingType()
 ))%0A            .
@@ -4867,32 +4867,52 @@
 light.getUuid(),
+%0A                   
  request.call(li
@@ -4917,32 +4917,57 @@
 light.getWhere()
+, light.getLightingType()
 ).getValue());%0A 

update service to handle types