package cc.blynk.server.application.handlers.sharing.logic;
import cc.blynk.server.application.handlers.main.logic.AppSyncLogic;
import cc.blynk.server.application.handlers.sharing.auth.AppShareStateHolder;
import cc.blynk.server.core.dao.SessionDao;
import cc.blynk.server.core.model.DashBoard;
import cc.blynk.server.core.model.auth.Session;
import cc.blynk.server.core.model.enums.PinType;
import cc.blynk.server.core.model.widgets.FrequencyWidget;
import cc.blynk.server.core.model.widgets.Target;
import cc.blynk.server.core.model.widgets.Widget;
import cc.blynk.server.core.model.widgets.ui.DeviceSelector;
import cc.blynk.server.core.protocol.enums.Response;
import cc.blynk.server.core.protocol.exceptions.IllegalCommandBodyException;
import cc.blynk.server.core.protocol.model.messages.StringMessage;
import cc.blynk.utils.ParseUtil;
import cc.blynk.utils.StringUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import static cc.blynk.server.core.protocol.enums.Command.APP_SYNC;
import static cc.blynk.server.core.protocol.enums.Command.HARDWARE;
import static cc.blynk.server.core.protocol.enums.Response.NOT_ALLOWED;
import static cc.blynk.server.core.protocol.enums.Response.NO_ACTIVE_DASHBOARD;
import static cc.blynk.utils.BlynkByteBufUtil.makeResponse;
import static cc.blynk.utils.BlynkByteBufUtil.makeUTF8StringMessage;
import static cc.blynk.utils.StringUtils.*;
/**
* The Blynk Project.
* Created by Dmitriy Dumanskiy.
* Created on 2/1/2015.
*
*/
public class HardwareAppShareLogic {
private static final Logger log = LogManager.getLogger(HardwareAppShareLogic.class);
private final SessionDao sessionDao;
public HardwareAppShareLogic(SessionDao sessionDao) {
this.sessionDao = sessionDao;
}
public void messageReceived(ChannelHandlerContext ctx, AppShareStateHolder state, StringMessage message) {
Session session = sessionDao.userSession.get(state.userKey);
String[] split = split2(message.body);
String[] dashIdAndTargetIdString = split2Device(split[0]);;
int dashId = ParseUtil.parseInt(dashIdAndTargetIdString[0]);
//deviceId or tagId or device selector widget id
int targetId = 0;
//new logic for multi devices
if (dashIdAndTargetIdString.length == 2) {
targetId = ParseUtil.parseInt(dashIdAndTargetIdString[1]);
}
DashBoard dash = state.user.profile.getDashByIdOrThrow(dashId);
if (!dash.isActive) {
log.debug("No active dashboard.");
ctx.writeAndFlush(makeResponse(message.id, NO_ACTIVE_DASHBOARD), ctx.voidPromise());
return;
}
if (!dash.isShared) {
log.debug("Dashboard is not shared. User : {}, {}", state.user.email, ctx.channel().remoteAddress());
ctx.writeAndFlush(makeResponse(message.id, NOT_ALLOWED), ctx.voidPromise());
return;
}
//sending message only if widget assigned to device or tag has assigned devices
Target target = dash.getTarget(targetId);
if (target == null) {
log.debug("No assigned target id for received command.");
return;
}
final int[] deviceIds = target.getDeviceIds();
if (deviceIds.length == 0) {
log.debug("No devices assigned to target.");
return;
}
final char operation = split[1].charAt(1);
switch (operation) {
case 'u' :
String[] splitBody = split3(split[1]);
final int widgetId = ParseUtil.parseInt(splitBody[1]);
Widget deviceSelector = dash.getWidgetById(widgetId);
if (deviceSelector instanceof DeviceSelector) {
final int selectedDeviceId = ParseUtil.parseInt(splitBody[2]);
((DeviceSelector) deviceSelector).value = selectedDeviceId;
AppSyncLogic.sendSyncAndOk(ctx, dash, selectedDeviceId, message.id);
}
break;
case 'w':
splitBody = split3(split[1]);
if (splitBody.length < 3) {
log.debug("Not valid write command.");
ctx.writeAndFlush(makeResponse(message.id, Response.ILLEGAL_COMMAND_BODY), ctx.voidPromise());
return;
}
final PinType pinType = PinType.getPinType(splitBody[0].charAt(0));
final byte pin = ParseUtil.parseByte(splitBody[1]);
final String value = splitBody[2];
final long now = System.currentTimeMillis();
for (int deviceId : deviceIds) {
dash.update(deviceId, pin, pinType, value, now);
}
//additional state for tag widget itself
if (target.isTag()) {
dash.update(targetId, pin, pinType, value, now);
}
final String sharedToken = state.token;
if (sharedToken != null) {
for (Channel appChannel : session.appChannels) {
if (appChannel != ctx.channel() && appChannel.isWritable() && Session.needSync(appChannel, sharedToken)) {
appChannel.writeAndFlush(makeUTF8StringMessage(APP_SYNC, message.id, message.body), appChannel.voidPromise());
}
}
}
if (session.sendMessageToHardware(dashId, HARDWARE, message.id, split[1], deviceIds)) {
log.debug("No device in session.");
ctx.writeAndFlush(makeResponse(message.id, Response.DEVICE_NOT_IN_NETWORK), ctx.voidPromise());
}
break;
//todo fully remove this section???
case 'r':
Widget widget = dash.findWidgetByPin(targetId, split[1].split(StringUtils.BODY_SEPARATOR_STRING));
if (widget == null) {
throw new IllegalCommandBodyException("No frequency widget for read command.");
}
if (!(widget instanceof FrequencyWidget)) {
//corner case for 3-d parties. sometimes users need to read pin state even from non-frequency widgets
if (session.sendMessageToHardware(dashId, HARDWARE, message.id, split[1], targetId)) {
log.debug("No device in session.");
ctx.writeAndFlush(makeResponse(message.id, Response.DEVICE_NOT_IN_NETWORK), ctx.voidPromise());
}
}
break;
}
}
}